How to give vertical margin to constraint layout group? - android

So I have a few view that I group it using constraint layout group. I want to give vertical margin to the group as I do if I group it using linear/relative layout. add android:layout_marginVertical="100dp" doesn't seem to work.

Groups don't work like that. Take a look at ConstraintLayout's Layer widget available since version 2.0. You can search for tutorials on the use of Layer. In a nutshell, Layer will allow you to group widgets similar to a ViewGroup but will maintain a flat layout structure. If the views contained with the layer can be constrained to the layer itself, the whole layer will accept a top margin.
For example, take the following layout:
The blue is the background for the layer. The top/bottom, right/left views are contained within the layer and are constrained to it. The top margin of 100dp is set on the Layer. "Outside view" is a TextView that is constrained to the "Bottom right" TextVIew which is contained within the Layer something that can't be done if Layer is replaced by a ViewGroup.
The views may still be contained within a Group for group manipulations.
Here is the XML for this layout:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.constraintlayout.helper.widget.Layer
android:id="#+id/layer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="100dp"
android:background="#android:color/holo_blue_light"
app:constraint_referenced_ids="topLeft,topRight,bottomLeft,bottomRight"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/topLeft"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="Top left"
android:textAppearance="#style/TextAppearance.AppCompat.Display1"
app:layout_constraintStart_toStartOf="#id/layer"
app:layout_constraintTop_toTopOf="#id/layer" />
<TextView
android:id="#+id/topRight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="Top right"
android:textAppearance="#style/TextAppearance.AppCompat.Display1"
app:layout_constraintStart_toStartOf="#+id/bottomRight"
app:layout_constraintTop_toTopOf="#id/layer" />
<TextView
android:id="#+id/bottomLeft"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="Bottom left"
android:textAppearance="#style/TextAppearance.AppCompat.Display1"
app:layout_constraintStart_toStartOf="#id/layer"
app:layout_constraintTop_toBottomOf="#+id/topLeft" />
<TextView
android:id="#+id/bottomRight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="Bottom right"
android:textAppearance="#style/TextAppearance.AppCompat.Display1"
app:layout_constraintStart_toEndOf="#+id/bottomLeft"
app:layout_constraintTop_toBottomOf="#+id/topRight" />
<TextView
android:id="#+id/outsideView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Outside view"
android:textAppearance="#style/TextAppearance.AppCompat.Display1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/bottomRight" />
</androidx.constraintlayout.widget.ConstraintLayout>

what you are trying to do is not correct.
ConstraintLayout Group can be used to "group" some references of different views or widgets and set their visibility (you can set the visibility of the group and all views will get it). It's not a fisical group like a LinearLayout can make.
Please, take a look at the documentation
https://developer.android.com/reference/androidx/constraintlayout/widget/Group
This class controls the visibility of a set of referenced widgets.
The visibility of the group will be applied to the referenced widgets. It's a convenient way to easily hide/show a set of widgets without having to maintain this set programmatically.
For what you want to do, you should use a Layout (LinearLayout, ConstraintLayout...) and group that views inside the layout and finally give the desired margin to this layout.

Related

Cannot align rows with chain restraints

I have a widget that, no matter what constraints I place upon it and other widgets, including the addition of barriers, always positions itself at the top of the layout.
This is a fairly simple arrangement of two rows of two elements each, not aligned column wise. The first element in each row is a TextView label, the second an input (Spinner).
There is also a lone TextView title above the first row stretching all the way across. By my understanding and previous experience with constraint layout, this shouldn't require a barrier between the rows, and that was my initial version.
This is the design view, where the selected element ("Credentials") is supposed to be in the second row but instead appears above the first row, over top of the title TextView ("PKIX"):
Actual result in the emulator looks much the same. The selected "Credentials" element is the fourth of five elements in the XML layout below. All of the other elements are in the right place.
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/addsrv_pkix_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="#drawable/bottomborder"
android:text="PKIX"
android:textAlignment="center"
android:layout_marginHorizontal="10sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#+id/addsrv_trust_lbl"
/>
<TextView
android:id="#+id/addsrv_trust_lbl"
android:text="Trust"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="10sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="#+id/addsrv_trust_spin"
app:layout_constraintTop_toBottomOf="#+id/addsrv_pkix_title"
app:layout_constraintBaseline_toBaselineOf="#+id/addsrv_trust_spin"
app:layout_constraintBottom_toTopOf="#+id/addsrv_cred_lbl"
/>
<Spinner
android:id="#+id/addsrv_trust_spin"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="#+id/addsrv_trust_lbl"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#+id/addsrv_pkix_title"
app:layout_constraintBottom_toTopOf="#+id/addsrv_cred_spin"
/>
<TextView
android:id="#+id/addsrv_cred_lbl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:paddingHorizontal="10sp"
android:text="Credentials"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="#+id/addsrv_cred_spin"
app:layout_constraintTop_toBottomOf="#+id/addsrv_trust_lbl"
app:layout_constraintBaseline_toBaselineOf="#+id/addsrv_cred_spin"
/>
<Spinner
android:id="#+id/addsrv_cred_spin"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="#+id/addsrv_cred_lbl"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#+id/addsrv_trust_spin"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
I'm perplexed. The addsrv_cred_lbl TextView ("Credentials") is:
Start aligned with parent.
End aligned with the addsrv_cred_spin spinner, which appears correctly positioned;
this alignment is reciprocated to create a horizontal chain. They are also baseline
aligned.
Top aligned with the bottom of the TextView above it, addsrv_trust_lbl.
This alignment is also reciprocated.
There's no bottom alignment yet (there's another row to go); bottom aligning it with the parent makes no difference unless I bottom align the spinner from the same row, in which case the result goes from bad to worse.
Since this did not work, I tried to use a barrier between the rows. If I use it as a "top", with the second row widgets as the constraint referents, the barrier appears at the top, above the title, regardless of what constraints are used to position it below the first row. Used as a "bottom", with the first row widgets referenced and the second row chained below it (which is more logical), things are a little bit better in that the barrier appears in the right place -- but the "Credentials" widget is still up top.
The design view of this looks exactly the same as the previous one except the barrier is visible below the first row. In the XML, I aslo added optimizationLevel="none" after having read this can help with misbehaving barriers (but it made no difference). There's also a few stylistic elements added back here (such as font size) I removed for brevity before.
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_optimizationLevel="none"
>
<TextView
android:id="#+id/addsrv_pkix_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="#drawable/bottomborder"
android:backgroundTint="#color/tbar"
android:text="PKIX"
android:textAlignment="center"
android:textSize="#dimen/addsrv_bigfont"
android:textColor="#color/titleText"
android:layout_marginHorizontal="10sp"
app:layout_constraintBottom_toTopOf="#+id/addsrv_trust_lbl"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
<TextView
android:id="#+id/addsrv_trust_lbl"
android:text="Trust"
android:textSize="#dimen/addsrv_fontsz"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="10sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="#+id/addsrv_trust_spin"
app:layout_constraintTop_toBottomOf="#+id/addsrv_pkix_title"
app:layout_constraintBaseline_toBaselineOf="#+id/addsrv_trust_spin"
app:layout_constraintBottom_toTopOf="#+id/addsrv_bar1"
app:layout_constraintHorizontal_chainStyle="packed"
/>
<Spinner
android:id="#+id/addsrv_trust_spin"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="#+id/addsrv_trust_lbl"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#+id/addsrv_pkix_title"
app:layout_constraintBottom_toTopOf="#+id/addsrv_bar1"
/>
<androidx.constraintlayout.widget.Barrier
android:id="#+id/addsrv_bar1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="addsrv_trust_lbl,addsrv_trust_spin"
app:layout_constraintBottom_toTopOf="#+id/addsrv_cred_lbl"
/>
<TextView
android:id="#+id/addsrv_cred_lbl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="10sp"
android:text="Credentials"
android:textSize="#dimen/addsrv_fontsz"
app:layout_constraintBaseline_toBaselineOf="#+id/addsrv_cred_spin"
app:layout_constraintEnd_toStartOf="#+id/addsrv_cred_spin"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/addsrv_bar1" />
<Spinner
android:id="#+id/addsrv_cred_spin"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="#+id/addsrv_cred_lbl"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#+id/addsrv_bar1"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
Am I correct in observing that some of the constraints on addsrv_cred_lbl are being completely ignored? Doesn't topToBottom mean that the top of the widget is aligned with the bottom of the other? Instead, it seems simply to mean that they will be connected with a squiggly, potentially curved and convoluted line in the design view, and the spacial relation of the two widgets is arbitrary, such that the semantic logic might as well be inverted, "top = bottom, bottom = top", etc.
Please note that I do not want to use absolute values to position anything. If the only way to get this to work is to do that, constraint layout seems a complete waste of time even in this simple case, and I'd rather just stack some liner layouts.
The problem is that Spinner doesn't have a baseline. You were trying to tie it to the baseline of the item inside the Spinner is my guess - ConstraintLayout can't reach children of its children.
You can check it via the design tab with a right click. TextViews will show an option "Show baseline" but the Spinner doesn't.
Also snippet from the doc
Align the text baseline of a view to the text baseline of another
view.
That's what baselines are for, if you want two TextViews connect together so they don't have the height where the text starts messed up.
I think there is a tiny flaw in your approach to the chain. You were setting the top constraint of both elements in each row to the bottom of the previous row(or title in the first row). Even if the Spinner would have a baseline, this would make the label off-centered in relation to the Spinner(slightly higher because the baseline of a text is higher than the actual bottom of the view).
I think the best approach in these types of layouts is to have one guiding element(which is also the element of the chain) that represents the row and let other elements be positioned in relation to it.
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/addsrv_pkix_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="PKIX"
android:textAlignment="center"
android:layout_marginHorizontal="10sp"
app:layout_constraintVertical_chainStyle="spread"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#+id/addsrv_trust_spin"
/>
<TextView
android:id="#+id/addsrv_trust_lbl"
android:text="Trust"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="10sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="#+id/addsrv_trust_spin"
app:layout_constraintTop_toTopOf="#+id/addsrv_trust_spin"
app:layout_constraintBottom_toBottomOf="#+id/addsrv_trust_spin"
/>
<Spinner
android:id="#+id/addsrv_trust_spin"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="#+id/addsrv_trust_lbl"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#+id/addsrv_pkix_title"
app:layout_constraintBottom_toTopOf="#+id/addsrv_cred_spin"
/>
<TextView
android:id="#+id/addsrv_cred_lbl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="10sp"
android:text="Credentials"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="#+id/addsrv_cred_spin"
app:layout_constraintTop_toTopOf="#+id/addsrv_cred_spin"
app:layout_constraintBottom_toBottomOf="#+id/addsrv_cred_spin"
/>
<Spinner
android:id="#+id/addsrv_cred_spin"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="#+id/addsrv_cred_lbl"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#+id/addsrv_trust_spin"
app:layout_constraintBottom_toBottomOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
NOTE: I added the last bottom constraint to the parent and "spread" to the chain for demonstration but also know that if you don't have the bottom of the last view constrained to something - it's not a chain.
Working with ConstraintLayout is easy if you obey few simple rules:
Create one chain. That means Views hooking to each other reciprocally, all the way.
Now that you have a working chain, you can manipulate it with layout_constraintVertical_bias and layout_constraintVertical_chainStyle. If changing those does nothing, it means your chain is broken.
hook remaining Views to ones laid out by the chain
every control must have 4 constraints: top, bottom, start and end
do not create competing chains and try to force them into working together.
Spinner cannot have baseline, only TextView and its descendants have it. Spinner is AdapterView, so it can contain whatever you can imagine.
BTW: don't use #+id to refer to existing ids. Plus means creating new id, so if you make a typo it will create new id that refers to nothing instead of error "there's no such id".
Here's an example: top label and Spinners form the main chain and side labels are positioned each to their Spinner:
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:id="#+id/addsrv_pkix_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10sp"
android:background=""
android:text="PKIX"
android:textAlignment="center"
app:layout_constraintBottom_toTopOf="#id/addsrv_trust_spin"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
app:layout_constraintVertical_chainStyle="packed"
/>
<TextView
android:id="#+id/addsrv_trust_lbl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="10sp"
android:text="Trust"
app:layout_constraintBottom_toBottomOf="#id/addsrv_trust_spin"
app:layout_constraintEnd_toStartOf="#id/addsrv_trust_spin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#id/addsrv_trust_spin"
app:layout_constraintVertical_bias="1.0"
/>
<Spinner
android:id="#+id/addsrv_trust_spin"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="#id/addsrv_cred_spin"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#id/addsrv_trust_lbl"
app:layout_constraintTop_toBottomOf="#id/addsrv_pkix_title"
app:layout_constraintVertical_bias="0.0"
/>
<TextView
android:id="#+id/addsrv_cred_lbl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="10sp"
android:text="Credentials"
app:layout_constraintBottom_toBottomOf="#id/addsrv_cred_spin"
app:layout_constraintEnd_toStartOf="#id/addsrv_cred_spin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#id/addsrv_cred_spin"
app:layout_constraintVertical_bias="1.0"
/>
<Spinner
android:id="#+id/addsrv_cred_spin"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#id/addsrv_cred_lbl"
app:layout_constraintTop_toBottomOf="#id/addsrv_trust_spin"
/>
</androidx.constraintlayout.widget.ConstraintLayout>

How to Partially Allow LTR, RTL support in a ConstraintLayout

So in my recent interview I was asked what if within a ConstraintLayout which has multiple child Views supports part of the Views LTR and RTL change and part of the Views to ignore it. Like the images below:
As you can see, blue views change their direction based on Views' layout direction but the Red Green and Yellow ones keep there position relative to each other, but align their (collective) start and end based on the layout direction to different View.
I understand you can set up the blue Views simply by using start and end attributes instead of left and right. But how do you keep the order of the multicolored Views same but at the same time reposition them alongside other Views? While making sure there is no deeper hierarchy than the parent ConstraintLayout.
I realize I may not be making much sense here, I just can't think of another way to convey the question.
Interesting question. I don't know of any way to specify that part of a layout should remain LTR when all the other parts go RTL. I presume that you are looking for an XML-only solution.
Something to keep in mind is that the left-to-left, right-to-left constraints are still valid. Normally, these would just be replaced by start-to-start and end-to-start constraints. So, with this in mind and making some assumptions about the layout, the following works:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<View
android:id="#+id/view"
android:layout_width="100dp"
android:layout_height="300dp"
android:layout_marginStart="24dp"
android:layout_marginTop="24dp"
android:background="#android:color/darker_gray"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="#+id/view2"
android:layout_width="239dp"
android:layout_height="75dp"
android:layout_marginStart="24dp"
android:layout_marginTop="24dp"
android:background="#android:color/holo_blue_bright"
app:layout_constraintStart_toEndOf="#+id/view"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="#+id/box1"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="24dp"
app:layout_constraintHorizontal_chainStyle="spread_inside"
android:background="#android:color/holo_orange_light"
app:layout_constraintRight_toLeftOf="#+id/box2"
app:layout_constraintLeft_toLeftOf="#+id/view2"
app:layout_constraintTop_toBottomOf="#+id/view2" />
<View
android:id="#+id/box2"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginLeft="24dp"
android:background="#android:color/holo_red_light"
app:layout_constraintRight_toLeftOf="#+id/box3"
app:layout_constraintLeft_toRightOf="#+id/box1"
app:layout_constraintTop_toTopOf="#+id/box1" />
<View
android:id="#+id/box3"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginLeft="24dp"
app:layout_constraintHorizontal_chainStyle="spread_inside"
android:background="#android:color/holo_green_light"
app:layout_constraintRight_toRightOf="#id/view2"
app:layout_constraintLeft_toRightOf="#+id/box2"
app:layout_constraintTop_toTopOf="#+id/box2" />
</androidx.constraintlayout.widget.ConstraintLayout>
There is one trick: layout_constraintHorizontal_chainStyle has to be set on the first and last view in the horizontal chain. There is also a tendency for the designer to change the left/right attributes back to start/end.
IRL, I would just wrap the blocks in a view group and be done with it. I think that it would be easier to maintain and not be prone to break.
Wrap the multicolored views in single view and make that view as parent for your other view's constraints.

How to center multiple Views together using ConstraintLayout?

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:
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 :
https://codelabs.developers.google.com/codelabs/constraint-layout/index.html#0
https://www.youtube.com/watch?v=sO9aX87hq9c
https://youtu.be/csaXml4xtN8?t=1693
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 ?
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"
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.
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.
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"
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.
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.

How do I automatically `layout_below` sequential controls in an Android layout?

I often seem to make Android layouts that have a series of controls that are meant to sit one below the other. For example
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="#+id/a"/>
<TextView
android:id="#+id/b"
android:layout_below="#+id/a"/>
<TextView
android:id="#+id/c"
android:layout_below="#+id/b"/>
<TextView
android:id="#+id/d"
android:layout_below="#+id/c"/>
<TextView
android:id="#+id/e"
android:layout_below="#+id/d"/>
</RelativeLayout>
The android:layout_below attributes are necessary: without them the TextViews all bunch up together in the same place.
They are also, usually, redundant and a general source of bugs and tedium. As control IDs change, as controls are added and removed, all of these strings must be edited to match up properly. To illustrate the general redundancy of this scheme, note how it promotes this sort of spaghetti:
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="#+id/e"
android:layout_below="#+id/d"/>
<TextView
android:id="#+id/b"
android:layout_below="#+id/a"/>
<TextView
android:id="#+id/d"
android:layout_below="#+id/c"/>
<TextView
android:id="#+id/a"/>
<TextView
android:id="#+id/c"
android:layout_below="#+id/b"/>
</RelativeLayout>
I can see how explicit layout_below directives (and friends such as layout_above) could be useful in some circumstances. But is there no way of configuring the layout (e.g. the RelativeLayout) to simply assume that the each control in the series that it contains should automatically layout_below the preceding control?
LinearLayout might be more suitable for this kind of UI structure. It does exactly what you need, and it does it automatically for you. All that you really have to specify is its android:orientation which can be either vertical or horizontal.
More information on LinearLayout can be found here.
All children of a LinearLayout
are stacked one after the other, so a vertical list will only have one
child per row, no matter how wide they are, and a horizontal list will
only be one row high (the height of the tallest child, plus padding).
Here's a quick example:
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/text_view_a"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hey, I'm TextView A!"/>
<TextView
android:id="#+id/text_view_b"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hey, I'm TextView B!"/>
<TextView
android:id="#+id/text_view_c"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hey, I'm TextView C!"/>
<!-- ..and so on. -->
</LinearLayout>
What you are looking for is LinearLayout with a vertical orientation.

Horizontal LinearLayout with Multiple Children, Move Children Below on New Line When No More Horizontal Space

I would like to have a horizontal LinearLayout that is as wide as the screen and as high as its children, but the trick is that its children will have dynamic width each and I don't want them going off screen (cut out). I want them to flow/break in a new line so that they are all visible.
Although totally irrelevant to Android, it should work similar to how inline <div>'s work in HTML.
Here's what I have right now:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="If you would enter some digits in this field " />
<EditText
android:id="#+id/tvDistance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="enter some digits here"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" that would be great" />
</LinearLayout>
But as soon as the children of the LinearLayout get wider than the screen the extra part gets off the screen/invisible.
I would like to have a horizontal LinearLayout that is as wide as the
screen and as high as its children, but the trick is that its children
will have dynamic width each and I don't want them going off screen
(cut out).
A LinearLayout can't do that(any default layout from the SDK can't do that) because the LinearLayout is set to place all the children in one horizontal or vertical line. Also, any type of conditional layout rules based on dimensions not yet available(like in your case the available width for the LinearLayout) are not possible in the xml anyway.
What you need is a custom layout which measures the children to use the available space moving any non fitting children on a new line below(a so called FlowLayout).
Edit:
Google now provides the flexbox library which implements the web's flexbox layout on Android. Using that library, the children of a FlexboxLayout will be placed on multiple lines based on their widths by using the flexDirection(with a value of row) and flexWrap(with a value of wrap) attributes.
In addition to Flexbox you could use ChipGroup which is part of the Material Design Library
The ChipGroup can be a container for other components as well, it doesn't have to be a Chip, it could be a button etc.
A ChipGroup is used to hold multiple Chips. By default, the chips are
reflowed across multiple lines. Set the app:singleLine attribute to
constrain the chips to a single horizontal line. If you do so, you'll
usually want to wrap this ChipGroup in a HorizontalScrollView.
<com.google.android.material.chip.ChipGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp">
<com.google.android.material.chip.Chip
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:chipText="This" />
<com.google.android.material.chip.Chip
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:chipText="is" />
<com.google.android.material.chip.Chip
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:chipText="a" />
<com.google.android.material.chip.Chip
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:chipText="because" />
<com.google.android.material.chip.Chip
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:chipText="chip" />
<com.google.android.material.chip.Chip
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:chipText="group" />
</com.google.android.material.chip.ChipGroup>
Updated:
Starting with Constraint Layout 2.0 we can now use Flow
<androidx.constraintlayout.helper.widget.Flow
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:flow_wrapMode="chain"
app:constraint_referenced_ids="card1, card2, card3"
/>
Notice app:constraint_referenced_ids and app:flow_wrapMode
First one is about passing the views and the second one about handling the way we wrap them.
Make sure to read about it on the official documentation

Categories

Resources