49

I am trying to set the width of the layout to "fill_parent" while having the height of the view just the same length, to make the layout a square.

Any suggestions will be appreciated.

starball
  • 20,030
  • 7
  • 43
  • 238
Bolton
  • 2,226
  • 3
  • 25
  • 29
  • use wrap_content . It takes the same width and length the view has. – Chirag Sep 07 '12 at 03:34
  • possible duplicate of [Fill remaining space with fixed aspect ratio surfaceview](http://stackoverflow.com/questions/10510371/fill-remaining-space-with-fixed-aspect-ratio-surfaceview) – Tim Sep 07 '12 at 03:39
  • I struggled with this myself for a while, and eventually I decided I had to override the onMeasure of the view I wanted to constrain and set the dimensions programmatically. See the linked duplicate. – Tim Sep 07 '12 at 03:43
  • 1
    Yes, I've voted up you answer there, and It's easier to do it as newbyca provided below :D – Bolton Sep 07 '12 at 05:48
  • Aren't the answers here closer to what you're looking for?: http://stackoverflow.com/questions/7058507/fixed-aspect-ratio-view – ThomasW Oct 11 '13 at 09:17

8 Answers8

112

With introduction of ConstraintLayout you don't have to write either a single line of code or use third-parties or rely on PercentFrameLayout which were deprecated in 26.0.0.

Here's the example of how to keep 1:1 aspect ratio for your layout using ConstraintLayout:

<android.support.constraint.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginEnd="0dp"
        android:layout_marginStart="0dp"
        android:layout_marginTop="0dp"
        android:background="@android:color/black"
        app:layout_constraintDimensionRatio="H,1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

    </FrameLayout>

</android.support.constraint.ConstraintLayout>

Or instead of editing your XML file, you can edit your layout directly in Layout Editor:

Layout Editor

Eugene Brusov
  • 17,146
  • 6
  • 52
  • 68
22

In your build.gradle add:

compile 'com.android.support:percent:23.1.1'

and in your layout.xml wrap whatever view or viewgroup you want to to follow a ratio inside a PercentFrameLayout where:

android:layout_width="match_parent"
android:layout_height="wrap_content"

and then for the view or viewgroup you want to to follow a ratio replace android:layout_width and android:layout_height:

    app:layout_aspectRatio="178%"
    app:layout_widthPercent="100%"

and you have a view or viewgroup where the aspect ratio is 16:9 (1.78:1) with the width matching the parent and the height adjusted accordingly.

Note: It's important you remove the normal width and height properties. Lint will complain, but it'll work.

YetAnotherUser
  • 527
  • 5
  • 12
Espen Riskedal
  • 1,425
  • 15
  • 28
  • 1
    I agree with everything said here except to remove the normal width and height properties that you mentioned is important. I've tried that and it didn't work for me. Instead I had to set them to the following android:layout_width="0dp" android:layout_height="0dp" as described here: http://stackoverflow.com/questions/34931023/percentframelayout-you-must-supply-a-layout-width-attribute – Nick.D Mar 28 '16 at 00:56
  • Warning: this will only work if container width >= container height * 16/9. If for instance your container width is 1000dp, and its height 50dp, your child will fit the width and have a height of 1000*9/16=562.5. – Tim Autin Oct 28 '16 at 10:23
  • 2
    `PercentRelativeLayout` and `PercentFrameLayout` are now deprecated and suggest using `ConstraintLayout` instead. – advice Aug 18 '17 at 11:44
18

You might try initially setting the layout_height to wrap_content. But from there I think you have to go into code. Just to experiment, in your activity try something like:

@Override
public void onResume(){
    super.onResume();
    findViewById(R.id.squareView).getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        @Override public void onGlobalLayout() {
            View squareView = findViewById(R.id.squareView);
            LayoutParams layout = squareView.getLayoutParams();
            layout.height = squareView.getWidth();
            squareView.setLayoutParams(layout);
            squareView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
        }
    });
}

Where R.id.squareView is the id of the view in question. And note, this code is wrapped in the onGlobalLayout call to ensure that squareView.getWidth() has a meaningful value.

Pramesh Bajracharya
  • 2,153
  • 3
  • 28
  • 54
newbyca
  • 1,523
  • 2
  • 13
  • 25
6

I created a layout library for similiar use case. Feel free to use it.

RatioLayouts

Installation

Add this to the top of the file

repositories {
    maven {
        url  "http://dl.bintray.com/riteshakya037/maven" 
    }
}


dependencies {
    compile 'com.ritesh:ratiolayout:1.0.0'
}

Usage

Define 'app' namespace on root view in your layout

xmlns:app="http://schemas.android.com/apk/res-auto"

Include this library in your layout

<com.ritesh.ratiolayout.RatioRelativeLayout
        android:id="@+id/activity_main_ratio_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:fixed_attribute="WIDTH" // Fix one side of the layout 
        app:horizontal_ratio="2" // ratio of 2:3
        app:vertical_ratio="3">
Ritesh Shakya
  • 575
  • 5
  • 17
1

If you want to preserve aspect ratio and parent layout must be "wrap_content" (useful for text views with variable text), you can add a fake view, which keeps aspect ratio and other elements must be constrained to this view:

    <androidx.constraintlayout.widget.ConstraintLayout     
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

    <View
        android:id="@+id/ratio_constraint"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="122:246"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="@id/ratio_constraint"
        app:layout_constraintStart_toStartOf="@id/ratio_constraint"
        app:layout_constraintTop_toTopOf="@id/ratio_constraint"
        tools:text="The TextView which can extend the layout" />

  </androidx.constraintlayout.widget.ConstraintLayout>

enter image description here

Fox
  • 417
  • 4
  • 13
0
LinearLayout ll = (LinearLayout)findViewById(R.id.LL);


int width = ll.getWidth();
LinearLayout.LayoutParams ll_params = new LinearLayout.LayoutParams(width, width);
ll.setLayoutParams(ll_params);
Aditya Nikhade
  • 1,373
  • 10
  • 15
  • 1
    This is more or less OK, however it all depends on *when* it is called. For example, if you were to put this code in onCreate, ll.getWidth() would return 0. – newbyca Sep 07 '12 at 03:57
0
 final RelativeLayout rlMyList = findViewById(R.id.rlMyList);
 rlMyList.postDelayed(new Runnable() {
     @Override
     public void run() {
          int width = rlMyList.getWidth();
          LayoutParams lp = (LayoutParams) rlMyList.getLayoutParams();
          lp.width = width;
          lp.height = (int) (width * 0.67);// in my case ratio 3:2
          rlMyList.setLayoutParams(lp);
     }
 },500);
-1

set height as fill_parent, the width as wrap_content

and you fix the aspect ratio with android:adjustViewBounds="true"

Aditya Nikhade
  • 1,373
  • 10
  • 15