ConstraintLayout chain with fixed aspect ratio
Asked Answered
C

2

8

I'm trying to build an activity with 3 imageViews according to the following layout:

   <------W------->         <------W-------->
^  +---------------+--------+---------------+
|  |               |        |               |
|  |               |        |               |
H  |       A       |   B    |      C        |
|  |               | (1:3)  |               |
|  |               |        |               |
v  +---------------+--------+---------------+
  • H should be 1/3 of the parent height
  • B has a fixed aspect ratio of 1:3
  • A and C should have the same width

I tried multiple solutions and could never find one that works. The last one uses :

  • a guideline set to 33% of the parent height
  • an horizontal chain between A, B and C
  • a fixed aspect ratio for B
  • A, B and C use match_constraint dimensions

While I would expect that the solver would make the desired layout instead it seems the chain defines a similar width to all views as seen in the screenshot.

I also tried using a LinearLayout but it doesn't seem possible to fix the aspect ratio of items inside one.

enter image description here

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@android:color/holo_blue_bright"
        app:layout_constraintBottom_toTopOf="@+id/guideline"
        app:layout_constraintDimensionRatio=""
        app:layout_constraintEnd_toStartOf="@+id/imageView2"
        app:layout_constraintHorizontal_chainStyle="spread"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.0"
        app:srcCompat="@mipmap/ic_launcher" />

    <ImageView
        android:id="@+id/imageView2"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@android:color/holo_blue_bright"
        app:layout_constraintBottom_toTopOf="@+id/guideline"
        app:layout_constraintDimensionRatio="1:3"
        app:layout_constraintEnd_toStartOf="@+id/imageView3"
        app:layout_constraintStart_toEndOf="@+id/imageView1"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.0"
        app:srcCompat="@mipmap/ic_launcher" />

    <ImageView
        android:id="@+id/imageView3"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@android:color/holo_blue_bright"
        app:layout_constraintBottom_toTopOf="@+id/guideline"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/imageView2"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.0"
        app:srcCompat="@mipmap/ic_launcher" />

    <android.support.constraint.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.3333" />

    <android.support.constraint.Guideline
        android:id="@+id/guideline3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.6666666666" />

</android.support.constraint.ConstraintLayout>

Thanks a lot!

Corker answered 31/5, 2018 at 14:18 Comment(0)
N
5

There are probably several approaches to doing what you ask. Here is one approach:

enter image description here

The key to this layout is to set up the center image first as defined in the XML. Once the center image is established, it becomes easier to define the left and right images.

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

    <ImageView
        android:id="@+id/imageViewLeft"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="@+id/imageCenter"
        app:layout_constraintEnd_toStartOf="@+id/imageCenter"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/imageCenter"
        app:srcCompat="@color/colorPrimary" />

    <ImageView
        android:id="@+id/imageCenter"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="W,1:3"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHeight_percent="0.33"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.0"
        app:srcCompat="@color/colorAccent" />

    <ImageView
        android:id="@+id/imageViewRight"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="@+id/imageCenter"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/imageCenter"
        app:layout_constraintTop_toTopOf="@+id/imageCenter"
        app:srcCompat="@color/colorPrimary" />
</android.support.constraint.ConstraintLayout>
Nonbelligerent answered 31/5, 2018 at 23:17 Comment(1)
I didn't know about the layout_constraintHeight_percent, thanks a lot !!Corker
E
0

I think the ConstraintLayout won't be able to handle all the restrictions put upon the middle view in terms of height or width, aspect ration, and boundaries, so this solution has part implemented in the xml and a final adjustment programatically.

It separates the three images with guidelines, and when the layout is rendered, it recalculates the position of the guidelines.

Layout

<android.support.constraint.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:id="@+id/mainLayout"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.constraint.Guideline
        android:id="@+id/guidelineV33"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.3333" />

    <android.support.constraint.Guideline
        android:id="@+id/guidelineH33"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.3333" />

    <android.support.constraint.Guideline
        android:id="@+id/guidelineH66"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.6666" />

    <ImageView
        android:id="@+id/ivA"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:src="@drawable/ic_launcher_foreground"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@id/guidelineH33"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@id/guidelineV33"/>

    <ImageView
        android:id="@+id/ivB"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:src="@drawable/ic_launcher_foreground"
        app:layout_constraintLeft_toRightOf="@id/guidelineH33"
        app:layout_constraintRight_toLeftOf="@id/guidelineH66"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@id/guidelineV33"
         />
    <ImageView
        android:id="@+id/ivC"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:src="@drawable/ic_launcher_foreground"
        app:layout_constraintLeft_toRightOf="@id/guidelineH66"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@id/guidelineV33"/>


</android.support.constraint.ConstraintLayout>

Activity

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.constraint.ConstraintLayout;
import android.support.constraint.ConstraintSet;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.ViewTreeObserver;

public class SO50626509Activity extends AppCompatActivity {

    private static final String TAG = SO50626509Activity.class.getName();

    ConstraintLayout cl;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.so50626509_layout);
        cl = findViewById(R.id.mainLayout);
        ViewTreeObserver observer = cl.getViewTreeObserver();
        observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                adjustLayout();
                cl.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
        });
    }


    private void adjustLayout(){
        int height = (int) (cl.getMeasuredHeight() * 0.3333);
        int width = cl.getMeasuredWidth();
        int middleWidth = height / 3;
        int g33 = (width - middleWidth) / 2;
        int g66 = g33 + middleWidth;


        ConstraintSet set = new ConstraintSet();
        set.clone(cl);
        set.setGuidelinePercent(R.id.guidelineH33,((float) g33 / (float) width));
        set.setGuidelinePercent(R.id.guidelineH66,((float) g66 / (float) width));
        set.applyTo(cl);
    }

}
Ebert answered 31/5, 2018 at 15:35 Comment(1)
This is a very nice idea and a great answer. I'm accepting the answer from @Nonbelligerent because it's XML only, but thanks a lot for this variantCorker

© 2022 - 2024 — McMap. All rights reserved.