Boycott A.I.
Boycott A.I.

Reputation: 18951

Constraint Layout Vertical Align Center - Two Child Views

I have two TextViews, one above the other. I would like the vertical middle of the two TextViews to be at the same position as the vertical middle of the ImageView. (This is so, regardless of the amount of text that may go into either TextView, everything will always look neat, vertically.)

I created what I need perfectly using two LinearLayouts (as the space above the title is the same as the space beneath the description):

enter image description here

But Android Studio was unable to covert it to ConstraintLayout successfully, as it just dumped the TextViews at the bottom of the layout. I've played with a lot of attributes, but could not quite arrive at the desired layout.

My question is similar to this one, except that I am trying to center_vertical align a pair of views rather than a single one - which means I have no view edge to align to the centre of the ImageView/container.

Is it possible to achieve what I'm after with ConstraintLayout? (I expect I may be able to do it with a single RelativeLayout, but I would like to use the layout_constraintDimensionRatio attribute on my ImageView which presumably leave me needing to use ConstraintLayout.)

In case it helps, here's the code for my aforementioned LinearLayout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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="wrap_content"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="@dimen/resources_list_image_size"
        android:layout_height="@dimen/resources_list_image_size"
        android:layout_marginEnd="@dimen/activity_horizontal_margin"
        android:layout_marginRight="@dimen/activity_horizontal_margin"
        android:layout_gravity="center_vertical"
        android:contentDescription="@string/resource_image"
        android:scaleType="centerCrop"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:orientation="vertical">

        <TextView
            android:id="@+id/textViewTitle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textAppearance="@style/MyTextAppearanceMedium"
            tools:text="Title" />

        <TextView
            android:id="@+id/textViewDescription"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textAppearance="@style/MyTextAppearanceSmall"
            tools:text="Description" />

    </LinearLayout>

</LinearLayout>

Update: Solution

Thanks to Ben P's answer, this is my final code:

<?xml version="1.0" encoding="utf-8"?>
<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="match_parent"
    android:layout_height="wrap_content">

    <!-- Add guideline to align imageView to. -->
    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.3" />

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:contentDescription="@string/resource_image"
        android:scaleType="centerCrop"
        app:layout_constraintDimensionRatio="H,1:1"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@id/guideline"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>

    <TextView
        android:id="@+id/textViewTitle"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="@dimen/activity_horizontal_margin"
        android:layout_marginLeft="@dimen/activity_horizontal_margin"
        app:layout_constraintBottom_toTopOf="@id/textViewDescription"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/imageView"
        app:layout_constraintTop_toTopOf="parent"
        android:textAppearance="@style/MyTextAppearanceMedium"
        app:fontFamily="@font/roboto_slab_regular"
        app:layout_constraintVertical_chainStyle="packed"
        tools:text="@string/enter_title_colon" />

    <TextView
        android:id="@+id/textViewDescription"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="@dimen/activity_horizontal_margin"
        android:layout_marginLeft="@dimen/activity_horizontal_margin"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/imageView"
        app:layout_constraintTop_toBottomOf="@id/textViewTitle"
        app:fontFamily="@font/roboto_slab_light"
        android:textAppearance="@style/MyTextAppearanceSmall"
        tools:text="Description" />

</androidx.constraintlayout.widget.ConstraintLayout>

Upvotes: 7

Views: 3681

Answers (3)

Ben P.
Ben P.

Reputation: 54264

It sounds like you could solve this problem by using a packed chain anchored to the top and bottom of the ImageView. You'll also need to use horizontal bias and a constrained width in order to get wrapping to work correctly.

<?xml version="1.0" encoding="utf-8"?>
<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="match_parent"
    android:layout_height="match_parent">

    <View
        android:id="@+id/anchor"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_margin="64dp"
        android:background="#caf"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <TextView
        android:id="@+id/one"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintVertical_chainStyle="packed"
        app:layout_constraintHorizontal_bias="0"
        app:layout_constrainedWidth="true"
        app:layout_constraintTop_toTopOf="@id/anchor"
        app:layout_constraintStart_toEndOf="@id/anchor"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toTopOf="@id/two"/>

    <TextView
        android:id="@+id/two"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintHorizontal_bias="0"
        app:layout_constrainedWidth="true"
        app:layout_constraintTop_toBottomOf="@id/one"
        app:layout_constraintStart_toEndOf="@id/anchor"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="@id/anchor"/>

</androidx.constraintlayout.widget.ConstraintLayout>

enter image description here

enter image description here

The important attributes here are:

  • app:layout_constraintVertical_chainStyle="packed" on the first view, which causes the two textviews to stack right on top of each other
  • app:layout_constraintHorizontal_bias="0" on both views, which means that when the text is not long enough to reach the edge of the screen it will stick to the edge of the anchor view
  • app:layout_constrainedWidth="true" on both views, which prevents the textview from ever being wider than its constraints, and so the text wraps to a new line

Upvotes: 10

If you want to use ConstraintLayout you could use something like this:

<?xml version="1.0" encoding="utf-8"?>
<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="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:contentDescription="description"
        android:scaleType="centerCrop"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <LinearLayout
        android:id="@+id/linearLayout"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintBottom_toBottomOf="@+id/imageView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/imageView"
        app:layout_constraintTop_toTopOf="@+id/imageView">

        <TextView
            android:id="@+id/textViewTitle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            tools:text="Title" />

        <TextView
            android:id="@+id/textViewDescription"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            tools:text="Description" />

    </LinearLayout>


</androidx.constraintlayout.widget.ConstraintLayout>

Upvotes: 2

Tamir Abutbul
Tamir Abutbul

Reputation: 7661

You can use this layout:

<?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">

<TextView
    android:id="@+id/textView2"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:background="@color/colorAccent"
    android:text="I am 5% of the screen height"
    app:layout_constraintBottom_toTopOf="@+id/textView3"
    app:layout_constraintEnd_toEndOf="@+id/textView3"
    app:layout_constraintHeight_percent="0.05"
    app:layout_constraintHorizontal_bias="1.0"
    app:layout_constraintStart_toStartOf="@+id/textView3"
    app:layout_constraintTop_toTopOf="@+id/imageView2" />

<TextView
    android:id="@+id/textView3"
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:layout_constraintHeight_percent="0.15"
    android:layout_marginStart="8dp"
    android:layout_marginEnd="8dp"
    android:background="@color/colorPrimary"
    android:text="I am 15% of the screen height (And the image is 20% screen size in height) "
    app:layout_constraintBottom_toBottomOf="@+id/imageView2"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toEndOf="@+id/imageView2"
    app:layout_constraintTop_toBottomOf="@+id/textView2" />

<ImageView
    android:id="@+id/imageView2"
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:layout_constraintHeight_percent="0.20"
    app:layout_constraintDimensionRatio="1:1"
    android:scaleType="fitXY"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:srcCompat="@drawable/rose" />

</android.support.constraint.ConstraintLayout>

It will look like this:

enter image description here

One important thing about this layout:

You can control your aspect ratio (for the image) with app:layout_constraintDimensionRatio="x:y" and by passing "1:1" make it square

And by the way - I am using support library for no reason on this example, you can use androidx

Upvotes: 0

Related Questions