73

Background

Google has announced a new layout called "ConstraintLayout" that's supposed to be the ultimate layout, that could replace all of the layouts while staying flat (without nested layouts) and have better performance.

The problem

Thing is, I barely see any tutorials for it that could help me on this matter, other than the video presented on Google IO.

What I am trying to do is, given that I have a vertically-centered LinearLayout within another layout - convert them both into a single ConstraintLayout.

After all, this is the purpose of this new layout...

The layout I wish to deal with looks like this:

enter image description here

Notice that the views in the center are only centered vertically, and that the 2 textViews are to the right of the ImageView, which is also centered vertically.

This all works well with RelativeLayout, which has the LinearLayout of the 2 TextViews, but I wish to know how to convert them into a single ConstraintLayout.

Here's a sample XML of what I've shown:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    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:minHeight="?attr/listPreferredItemHeightSmall">

    <ImageView
        android:id="@+id/appIconImageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_centerVertical="true"
        android:layout_marginEnd="4dp"
        android:layout_marginLeft="2dp"
        android:layout_marginRight="4dp"
        android:layout_marginStart="2dp"
        android:adjustViewBounds="true"
        android:src="@android:drawable/sym_def_app_icon"
        tools:ignore="ContentDescription"/>

    <LinearLayout
        android:id="@+id/appDetailsContainer"
        android:layout_width="0px"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_toEndOf="@+id/appIconImageView"
        android:layout_toLeftOf="@+id/overflowView"
        android:layout_toRightOf="@+id/appIconImageView"
        android:layout_toStartOf="@+id/overflowView"
        android:orientation="vertical">

        <TextView
            android:id="@+id/appLabelTextView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ellipsize="marquee"
            android:text="label"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:textDirection="locale"
            tools:ignore="HardcodedText,UnusedAttribute"/>

        <TextView
            android:id="@+id/appDescriptionTextView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ellipsize="marquee"
            android:minLines="3"
            android:text="description"
            android:textAppearance="?android:attr/textAppearanceSmall"
            android:textDirection="locale"
            tools:ignore="HardcodedText,UnusedAttribute"/>
    </LinearLayout>

    <ImageView
        android:id="@+id/overflowView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:adjustViewBounds="true"
        android:background="?attr/selectableItemBackground"
        android:clickable="true"
        android:padding="10dp"
        app:srcCompat="@drawable/ic_more_vert_black_24dp"

        tools:src="@drawable/ic_more_vert_black_24dp"
        tools:ignore="ContentDescription"/>

    <ImageView
        android:id="@+id/isSystemAppImageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignEnd="@+id/overflowView"
        android:layout_alignLeft="@+id/overflowView"
        android:layout_alignParentBottom="true"
        android:layout_alignRight="@+id/overflowView"
        android:layout_alignStart="@+id/overflowView"
        android:adjustViewBounds="true"
        android:scaleType="centerInside"
        app:srcCompat="@drawable/ic_warning_black_24dp"
        tools:ignore="ContentDescription"
        tools:src="@drawable/ic_warning_black_24dp"/>

</RelativeLayout>

What I tried

I tried to read some articles and watch some videos of Google :

That didn't help, so I tried to using it, hoping I will find out how to use it myself. But I can't find out how to do it. I tried using the feature to convert the layouts, but this makes a huge mess of the views and puts additional margins that I don't want to have.

The question

How can I convert the 2 layouts into a single ConstraintLayout ?

Rishabh Maurya
  • 1,272
  • 3
  • 19
  • 37
android developer
  • 106,412
  • 122
  • 641
  • 1,128
  • this one: http://pasteboard.co/1hmJDCgg.png? – pskink May 29 '16 at 07:48
  • @pskink Kinda, but the centered views have 2 TextViews, one below the other. Not one. Currently they are inside a LinearLayout, which is centered. Also, the LinearLayout is between the right and left views. – android developer May 29 '16 at 09:41
  • have you tried automated conversion at design pane? "Convert to ConstraintLayout"? – Devrim Jun 03 '16 at 22:08
  • @DevrimTuncer Yes. and the result didn't flatten it at all. You can see my try here: https://code.google.com/p/android/issues/detail?id=212116 – android developer Jun 04 '16 at 07:12

5 Answers5

100

Take a look at my answer here.

ContraintLayout contains a feature - Chains - that makes it possible to implement what you are asking:

Chains provide group-like behavior in a single axis (horizontally or vertically).

A set of widgets are considered a chain if they a linked together via a bi-directional connection

Once a chain is created, there are two possibilities:

  • Spread the elements in the available space
  • A chain can also be "packed", in that case the elements are grouped together

As for your case, you'll have to pack your label and description TextViews and center them in you parent vertically:

(make sure you use a version of ConstraintLayout that supports chains)

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="16dp"
        android:text="TextView"
        app:layout_constraintBottom_toTopOf="@+id/button"
        app:layout_constraintLeft_toRightOf="@+id/imageView2"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintHorizontal_chainStyle="packed"/>

    <TextView
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:text="Button\nMkay"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toRightOf="@+id/imageView2"
        app:layout_constraintTop_toBottomOf="@+id/textView"/>

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginTop="16dp"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@mipmap/ic_launcher"/>

    <ImageView
        android:id="@+id/imageView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@mipmap/ic_launcher"/>

    <ImageView
        android:id="@+id/imageView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        android:layout_marginEnd="16dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:srcCompat="@mipmap/ic_launcher"/>
</android.support.constraint.ConstraintLayout>

Update 25-06-2019 (@Saeid Z):

Now in constraint layout 1.1.3 we must use app:layout_constraintHorizontal_chainStyle="packed" instead of app:layout_constraintVertical_chainPacked="true"

Yury Fedorov
  • 12,277
  • 6
  • 44
  • 63
  • 2
    How does it group together views? – android developer Oct 18 '16 at 10:56
  • It uses a new feature of ConstraintLayout - chains. Check out my other answer with more info and links on it – Yury Fedorov Oct 18 '16 at 11:02
  • I mean, how does it know which views to chain together. In LinearLayout, it's all views that are inside of it. – android developer Oct 18 '16 at 11:31
  • 1
    The definition of a chain is "A set of widgets are considered a chain if they a linked together via a bi-directional connection" - two views in the example are dependent one on another (the textView is above the button, and the button is below the textView), so they are called a chain and grouped together. Please follow the links and find more info on it. – Yury Fedorov Oct 18 '16 at 11:34
  • But there are multiple views that depend one on another. How does it know which should be in which group of the chain ? – android developer Oct 18 '16 at 18:32
  • 1
    I don't know the implementation details. What I do know is that views that reference each other in their constraints make a chain, and its behavior is defined by its first member. – Yury Fedorov Oct 18 '16 at 18:54
  • This only works if you center the label and droid imageview in the parent. You cannot have them aligned vertically one relative to the other. See @Marcin answer instead. – mbonnin Apr 17 '17 at 16:19
  • @mbonnin The question was exactly how to center the views in parent, and not align relatively one to another. I think that down-voting an answer that does not answer your question, but answers the OP's question, is inappropriate. – Yury Fedorov Apr 18 '17 at 06:52
  • @Yury Agreed, I edited slightly the answer to make it more explicit. As soon as I can, I'll remove the downvote – mbonnin Apr 18 '17 at 07:33
  • @YuryFedorov Thank you for all the explanations. – android developer Jan 22 '19 at 12:38
  • 2
    @Yury Fedorov Thanks. Now in constraint layout 1.1.3 we must use app:layout_constraintHorizontal_chainStyle="packed" instead of app:layout_constraintVertical_chainPacked="true" – Saeid Z Jun 24 '19 at 13:54
7

Set app:layout_constraintVertical_bias="0.5" to the views that need to be centered vertically, bias attribute only works if you specify the constraints for the boundaries (e.g. top and bottom for vertical bias, left and right for horizontal bias)

An example:

<android.support.constraint.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/constraintLayout">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="@id/constraintLayout"
        app:layout_constraintBottom_toBottomOf="@id/constraintLayout"
        app:layout_constraintVertical_bias="0.5" />

</android.support.constraint.ConstraintLayout>

Got it working in my layout here: https://github.com/hidroh/tldroid/blob/master/app/src/main/res/layout/activity_main.xml, pretty much similar layout, though I position things at 1/3rd of screen height.

enter image description here

hidro
  • 11,509
  • 5
  • 49
  • 53
  • 1
    I don't understand. I have 2 TextViews. I might have more. I want them to be centered vertically together. What should I do? – android developer May 30 '16 at 13:01
  • My layout has 2 TextViews. I only give an example of how to center 1 view vertically inside ConstraintLayout. If you want a bunch of them, set bias attribute for each of them. In my case, I only set to 1 TextView, and use it as the anchor for the other one. – hidro May 30 '16 at 13:05
  • Can you please show how to do it for the case I've presented? one TextView above the other, while both, together, are centered ? – android developer May 30 '16 at 20:21
  • Did you look at the Github file in my answer? It's almost identical, just change the bias value in my layout to 0.5 instead of 0.3. – hidro May 31 '16 at 00:31
6

To center something vertically or horizontally, set an opposing constraint on the layout.

Centered Vertically

    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"

Centered Horizontally

    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
Kevin
  • 149
  • 1
  • 3
  • 1
    Please see this first [how-to-answer](https://stackoverflow.com/help/how-to-answer) This question is answered before, obviously, you can add your answer here. But You Need to understand some points before answering. First, don't add an answer which is previously added with the same code or suggestion. Second, don't add an overly complicated answer if the user has asked very specifically about the problem and what he needs to solve this. Third, You can add a comment if you want to suggest anything regarding the answer or question. – ankit suthar Jul 14 '17 at 04:55
  • 18
    this only center single view – Rasel Mar 14 '19 at 07:08
5

EDIT: This answer had been written before chains became available. Please use chains now, see above answer: https://stackoverflow.com/a/40102296/1402641


For now I believe your only choice would be to wrap the two text views in another layout - probably linear layout best fits this situation.

But the team behind constraint layout said they want to introduce "virtual containers", which serve exactly this use case: you group two or more views together and set a constraint (like centring vertically) on the container. The idea is that it is not a full nested layout inside of constraint layout, but just something that constraint layout uses to position it's children - hence it should provide better performance than nesting.

They mention it in their I/O talk (linked to the exact time). So I guess stay tuned.

Community
  • 1
  • 1
Marcin Koziński
  • 9,758
  • 3
  • 43
  • 58
  • But the RelativeLayout already has a LinearLayout inside. Isn't the whole point to flatten it all to have a single layout? Is my scenario that special? – android developer Jun 03 '16 at 10:12
  • 1
    Yes, the point is to flatten. Your scenario is not special, just not yet handled. It's just on version `alpha2`. Virtual groups are coming to handle your scenario. – Marcin Koziński Jun 03 '16 at 11:08
  • How do you use "virtual groups" ? Is there any example of this ? I don't think I saw it. – android developer Jun 03 '16 at 19:38
  • 1
    Virtual groups don't exist yet. It's a concept that the team behind `ConstraintLayout` want to introduce. They decided to release early, without all the features and without handling all the common cases. – Marcin Koziński Jun 03 '16 at 19:41
  • How do you know all of this? I don't remember hearing about it in the lecture of Google IO , and also not in any article. – android developer Jun 03 '16 at 19:59
  • 1
    Actually I managed to find in the I/O talk :) But they called it "virtual containers", so I'll edit my answer and post the link. – Marcin Koziński Jun 03 '16 at 20:33
  • Odd. I watched it too. Probably missed this part, or thought it's already available. Thanks. Since this isn't yet available, as it seems, I will only +1 your post and comments. Once you can get a working solution for this (using the new "virtual groups"), I will accept this answer – android developer Jun 04 '16 at 07:14
2

Take a look at mine example (Center components in ConstraintLayout)

For now you could center "image" (constrain left-top-bottom), constrain "label" top to "image" top, constrain "description" top to "label" bottom. In example below i have matched button height with two textviews to the right (which is your case). Other textviews are constrained as on the link above.

enter image description here

UPDATE: Answering to your comment below:

I have installed your app and this is only i can think of currently. Since it is a ViewHolder layout, layout_height can be set to wrap_content, or you can fix it if you'd like to. I can send you xml if you want, i dont want to flood the answer.

Top TextView left constraint is constrained to right constraint of ImageView. Also, top TextView -> top constraint is constrained to top of container, so is right. Bottom TextView is constrained to bottom of container. TextViews in the middle are constrained to match width of top TextView and got no connection with ImageView.

enter image description here

Community
  • 1
  • 1
bajicdusko
  • 1,570
  • 1
  • 15
  • 29
  • I asked about centering 2 views (together). Not one. – android developer Jun 09 '16 at 10:49
  • @androiddeveloper yes i am aware of that, i have offered a workaround. It visually looks the same. Problem still remains ofcourse since both of us want to center multiple components mutually constrained. – bajicdusko Jun 09 '16 at 10:51
  • Actually maybe it could be a nice workaround in my case. The layout I use is for items in RecyclerView (in this app: https://play.google.com/store/apps/details?id=com.lb.app_manager ) . Maybe I could set the top TextView to take the top (yet with gravity bottom), and the bottom TextView to take the bottom (below the top TextView). Do you think it can look the same? – android developer Jun 09 '16 at 11:12
  • @androiddeveloper i have updated my answer with layout specific to the one in your app. Hope it could help. – bajicdusko Jun 09 '16 at 13:28
  • I will try it when I have the time. As I've noticed, only recently they've fixed a bug that prevents the new layout from being used within RecyclerView. – android developer Jun 09 '16 at 13:42