Multi-line text-views alignment to their baseline (ConstraintLayout Android Studio) - android

I have three TextViews "hi", "x" and "Hello World" which I would like to align in the bottom of the Hello World text (ie hi_x_World). Hello World is just one line but both layout_width and layout_height are set to wrap_content.
They have different font sizes so even though I can easily align the bottom of the boxes of the textviews, the text itself does not become aligned.
I found a different XML parameter app:layout_constraintBaseline_toBaselineOf="#+id/text that works when I only have one line in the TextView. However, when I have 2 or more lines (like in the Hello World TextView) the baseline that's considered is in the 'Hello' instead of 'World'.
Is there any way to change the setting to consider the baseline below the word "World" instead of "Hello" ?

Second update: This is another way of looking at the first solution taken from this Stack Overflow answer that will also work with ConstraintLayout. This solution uses a custom TextView. The custom TextView returns the baseline of the last line of text in the TextView from the getBaseline() function instead of the baseline of the first line which is the default action. This is a nice, clean solution (IMO) that takes into account multi-line TextViews but also gravity, etc.
Kotlin version of BaselineLastLineTextView
class BaselineLastLineTextView #JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : AppCompatTextView(context, attrs) {
override fun getBaseline(): Int {
val layout = layout ?: return super.getBaseline()
val baselineOffset = super.getBaseline() - layout.getLineBaseline(0)
return baselineOffset + layout.getLineBaseline(layout.lineCount - 1)
}
}
First update: This is an update to my answer below which is still a valid solution (IMO). This is an alternate approach that does not involve any Java/Kotlin code and can be accomplished just using XML.
Create an invisible wrap_content TextView that has the same font size as the "Hello World!" TextView. (You may also need to consider padding and margins depending upon the actual layout.) Constrain this new view to the bottom of "Hello World!", make it invisible and set the contents to something short that is guaranteed to occupy just one line. This will give you a target view that has the same base line as the last line of the "Hello World!" view.
Constrain the base lines of "hi" and "x" to the new invisible view. All views will now share the same base line and without coding.
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="#+id/hiddenView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="A"
android:textSize="50sp"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="#id/helloView"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="#+id/hiView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="hi"
android:textSize="46sp"
app:layout_constraintBaseline_toBaselineOf="#id/hiddenView"
app:layout_constraintEnd_toStartOf="#+id/xView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#id/helloView" />
<TextView
android:id="#+id/xView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="x"
android:textSize="36sp"
app:layout_constraintBaseline_toBaselineOf="#id/hiddenView"
app:layout_constraintEnd_toStartOf="#+id/helloView"
app:layout_constraintStart_toEndOf="#+id/hiView"
app:layout_constraintTop_toTopOf="#id/helloView" />
<TextView
android:id="#+id/helloView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello\nWorld!"
android:textSize="50sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/xView"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:onClick="onClick"
android:text="Adjust Base Lines"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/helloView" />
</androidx.constraintlayout.widget.ConstraintLayout>
First answer: As noted in another answer, there is no way to do this just using ConstraintLayout constraints. You will need to resort to a programmatic solution.
Within each TextView is a StaticLayout that can reveal a bit about the typography of the text. By referring to the static layout, padding can be added to the appropriate views to make the base lines align.
In this demo, the three TextViews simply have their tops aligned. Initially, the views look like this:
When the button is clicked, the base line locations are calculated and padding is added to the tops of the "hi" and "x" TextViews.
Details will vary with the implementation, but this is the general technique.
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun onClick(view: View) {
button.isEnabled = false
// Get the StaticLayout from the TextView
val layout = helloView.layout
// Get the base line location for last line of Hello World! TextView, "hi" and "x"
val helloBaseLIne = layout.getLineBaseline(layout.lineCount - 1)
val hiBaseLine = hiView.layout.getLineBaseline(0)
val xBaseLine = xView.layout.getLineBaseline(0)
// Shift "hi" and "x" down so base lines match that of hello world!
hiView.updatePadding(top = helloBaseLIne - hiBaseLine)
xView.updatePadding(top = helloBaseLIne - xBaseLine)
}
}
activity_main.xml
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:id="#+id/layout"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="#+id/hiView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="hi"
android:textSize="46sp"
app:layout_constraintTop_toTopOf="#id/helloView"
app:layout_constraintEnd_toStartOf="#+id/xView"
app:layout_constraintStart_toStartOf="parent"/>
<TextView
android:id="#+id/xView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="x"
android:textSize="36sp"
app:layout_constraintTop_toTopOf="#id/helloView"
app:layout_constraintEnd_toStartOf="#+id/helloView"
app:layout_constraintStart_toEndOf="#+id/hiView" />
<TextView
android:id="#+id/helloView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello\nWorld!"
android:textSize="50sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/xView"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="Adjust Base Lines"
android:onClick="onClick"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/helloView" />
</androidx.constraintlayout.widget.ConstraintLayout>

AFAIK, there is no way to accomplish this task using ConstraintLayout as of today.
If you know the content of the "helloWorldTextView" in advance, you might wanna split the lines into several textViews and then use app:layout_constraintBaselineToBaselineOf.
I know this is a tricky workaround, but it's the only way that comes to mind.

You can use:
app:layout_constraintBottom_toBottomOf="#+id/helloworldtextid"
Also, don't forget to set the height to wrap_content so that the bottom of the TextViews line up to the bottom of the "Hello World" even when it wraps to two lines or more.
If you want it centered to "Hello World" when it is multi-line, consider adding
app:layout_constraintTop_toTopOf="#+id/helloworldtextid"
along with the bottom one. It will center the text to the multi-lined "Hello World" vertically.

Use Guideline
Utility class representing a Guideline helper object for ConstraintLayout. Helper objects are not displayed on device (they are marked as View.GONE) and are only used for layout purposes. They only work within a ConstraintLayout.
A Guideline can be either horizontal or vertical:
a) Vertical Guidelines have a width of zero and the height of their ConstraintLayout parent
b) Horizontal Guidelines have a height of zero and the width of their ConstraintLayout parent
Widgets can then be constrained to a Guideline, allowing multiple
widgets to be positioned easily from one Guideline, or allowing
reactive layout behavior by using percent positioning.
Example
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="match_parent">
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent=".4"
/>
<TextView
android:id="#+id/textViewHi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginLeft="50dp"
android:text="hi"
android:textSize="30sp"
app:layout_constraintBottom_toTopOf="#+id/guideline" />
<TextView
android:id="#+id/textViewX"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="#+id/textViewHi"
android:layout_marginLeft="50dp"
android:text="x"
android:textSize="30sp"
app:layout_constraintBottom_toTopOf="#+id/guideline" />
<TextView
android:id="#+id/textViewHelloWorld"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="#+id/textViewX"
android:layout_marginLeft="50dp"
android:text="Hello World"
android:textSize="45sp"
app:layout_constraintBottom_toTopOf="#+id/guideline" />
</androidx.constraintlayout.widget.ConstraintLayout>

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 set minimum percentage width of View in a ConstraintLayout

I have two Buttons organized in a row in a ContraintLayout. By default, I need them to be evenly distributed each taking 50% of the ConstraintLayout width (easy enough with a vertical Guideline set to 50%):
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
...
<Button
android:id="#+id/secondaryButton"
style="#style/Button.Secondary"
android:text="Secondary Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#id/primaryButtonBarrier"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/detail" />
<Button
android:id="#+id/primaryButton"
style="#style/Button.Primary"
android:layout_width="0dp"
android:text="Primary Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#id/middleGuideline"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintTop_toBottomOf="#id/detail" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/middleGuideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<androidx.constraintlayout.widget.Barrier
android:id="#+id/primaryButtonBarrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="start"
app:constraint_referenced_ids="primaryButton, middleGuideline"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="#id/detail" />
</androidx.constraintlayout.widget.ConstraintLayout>
Desired behavior (currently achieved programmatically with code snippet below):
I also need to support "overflow" text in the primary Button on the right.
So, if I have a larger amount of text, the Button width will push past the 50% guideline. I've tried using app:layout_constraintWidth_min, but it doesn't take a percentage (wrap or dimen).
The best solution I can come up with is setting the Button width to wrap_content and then the min width programmatically after the ConstraintLayout has been measured:
doOnLayout { constraintLayout ->
val layoutParams = primaryButton.layoutParams as ConstraintLayout.LayoutParams
layoutParams.matchConstraintMinWidth = constraintLayout.measuredWidth / 2
primaryButton.layoutParams = layoutParams
}
I feel like there has to be a combo of settings here that I'm missing to achieve this. Thanks in advance!
There is no straightforward way to do what you want that I know of with any standard XML code and the standard ConstraintLayout attributes. You might be able to do something with a nested layout, but, in general, that should be avoided with ConstraintLayout. I think that the way you devised is probably going to be the way to go.
However, if you want to do this all in XML, you could create a shadow button that is a replica of the primary button in all aspects (position, style, text, etc.) except that it is invisible. A barrier can then ride to the left of the shadow button and the center guideline. The visible primary button can now be constrained to the barrier since the barrier is not dependent upon the primary button. The primary button will at least be 1/2 the width of the ConstraintLayout but can expand beyond.
A second approach would be to create a custom view for the primary button. This custom view can implement an attribute that specifies a minimum width for the button based on the width of the ConstraintLayout.
ButtonMinWidthOfParent.kt
class ButtonMinWidthOfParent #JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : MaterialButton(context, attrs) {
private var minWidthOfParent = 0f
init {
context.theme.obtainStyledAttributes(
attrs,
R.styleable.ButtonMinWidthOfParent,
0, 0
).apply {
try {
minWidthOfParent = getFloat(R.styleable.ButtonMinWidthOfParent_minWidthOfParent, 0f)
} finally {
recycle()
}
}
}
#Override
override fun getSuggestedMinimumWidth(): Int {
return maxOf(
super.getSuggestedMinimumWidth(),
((parent as ViewGroup).measuredWidth * minWidthOfParent).toInt()
)
}
}
attrs.xml
<resources>
<declare-styleable name="ButtonMinWidthOfParent">
<attr name="minWidthOfParent" format="float"/>
</declare-styleable>
</resources>
activity_main
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="#+id/detail"
android:layout_width="0dp"
android:layout_height="100dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/secondaryButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Secondary Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#id/primaryButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/detail" />
<com.example.secondarybutton.ButtonMinWidthOfParent
android:id="#+id/primaryButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Primary Button that is a little bit longer"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#id/detail"
app:minWidthOfParent="0.5" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/middleGuideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<View
android:id="#+id/view"
android:layout_width="2dp"
android:layout_height="0dp"
android:background="#android:color/holo_red_light"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
There are undoubtedly other ways, but the XML way has the advantage of being a pure XML implementation with a little trickery while the custom view is a more standard way to extend view capabilities.

constraint layout - Two views with largest width

I want to create a layout (using constraint layout) like the following:
In different languages Button1 may be larger than button2. How can I do this?
I've only been able to achieve this using a LinearLayout inside the constraint that contains the two buttons, but I'm trying to use only a layout.
Thanks
Update
I want to mention that Remq's answer works and gets around the self-reference problem I noted below where a view's width is determined by the location of a barrier that uses the same view as a referenced id. The key seems to be the specification of app:layout_constraintWidth_min="wrap" for both views. It is unclear to me why this works, or that it will continue to work, but I wanted to make note of it
Update #2
I took another look at why Remq's answer works. My experience is that without specifying app:layout_constraintWidth_min="wrap" for the views, both views collapse to zero width. Once the views measure out as zero width, app:layout_constraintWidth_min="wrap" grows them again so the contents are wrapped. This is just what I surmise and have no proof that this is what is going on, but it makes sense.
I have seen questions akin to this one on Stack Overflow a number of times. These questions never have a satisfactory answer IMO (including ones that I have answered.) The difficulty is that there is a dependency problem since one view depends on the width of another but that other view depends on the width of the first. We fall into a referential quandary. (Forcing widths programmatically is always an option but seems undesirable.)
Another and, probably, better approach is to use a custom ConstraintHelper that will inspect the sizes of the referenced views and adjust the width of all views to the width of the largest.
The custom ConstraintHelper is placed in the XML for the layout and references the effected views as in the following sample layout:
activity_main
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:gravity="center"
android:text="Button1"
app:layout_constraintBottom_toTopOf="#+id/button2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<Button
android:id="#+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Button2 may be larger"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/button1" />
<com.example.constraintlayoutlayer.GreatestWidthHelper
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="button1,button2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
The custom ConstraintHelper looks like this:
GreatestWidthHelper
class GreatestWidthHelper #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintHelper(context, attrs, defStyleAttr) {
override fun updatePostMeasure(container: ConstraintLayout) {
var maxWidth = 0
// Find the greatest width of the referenced widgets.
for (i in 0 until this.mCount) {
val id = this.mIds[i]
val child = container.getViewById(id)
val widget = container.getViewWidget(child)
if (widget.width > maxWidth) {
maxWidth = widget.width
}
}
// Set the width of all referenced view to the width of the view with the greatest width.
for (i in 0 until this.mCount) {
val id = this.mIds[i]
val child = container.getViewById(id)
val widget = container.getViewWidget(child)
if (widget.width != maxWidth) {
widget.width = maxWidth
// Fix the gravity.
if (child is TextView && child.gravity != Gravity.NO_GRAVITY) {
// Just toggle the gravity to make it right.
child.gravity = child.gravity.let { gravity ->
child.gravity = Gravity.NO_GRAVITY
gravity
}
}
}
}
}
}
The layout displays as shown below.
I stumbled upon this question and was able to solve it with androidx.constraintlayout.widget.Barrier which were introduced in 1.1.0-beta1 of constraint layout. So I thought it'd be nice to share with people who run into the same issue:
<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">
<Button
android:id="#+id/button1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:gravity="center"
android:text="Button1"
app:layout_constraintBottom_toTopOf="#+id/button2"
app:layout_constraintEnd_toEndOf="#id/barrierEnd"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintWidth_min="wrap" />
<Button
android:id="#+id/button2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Button2 may be larger"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="#id/barrierEnd"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/button1"
app:layout_constraintWidth_min="wrap" />
<androidx.constraintlayout.widget.Barrier
android:id="#+id/barrierEnd"
android:layout_width="0dp"
android:layout_height="0dp"
app:barrierDirection="end"
app:constraint_referenced_ids="button1,button2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Haven't been able to center them horizontally, so if someone figures that out I'll update this example.
As per what I understand from your question, below code will help you.
<?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/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello world"
app:layout_constraintBottom_toTopOf="#+id/text2"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello world"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/text1" />
</androidx.constraintlayout.widget.ConstraintLayout>

How to put 2 customViews inside a dynamic RelativeLayout?

I got the following customViews:
val paintTextTypology = Paint()
val paintTextDate = Paint()
This 2 texts are the following customViews:
And the result I want to achieve is this:
So I want to align the text to right, but since the texts are separated, I think I have to create a dynamic RelativeLayout to align them to the right of the parent.
(I can't afford to have margin-right because the text can change.)
How can I do it?
Based on your comment (not 100% clear to me yet because I don't know what your custom views are), I would use a ConstraintLayout that would look like this:
The red rectangle is your picture.
And then I added two textViews which are aligning to the image. You can play with it and see what else you need. (this is a simplification).
<?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/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is textView One"
app:layout_constraintEnd_toStartOf="#id/image"
app:layout_constraintTop_toTopOf="#+id/image"
/>
<TextView
android:id="#+id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is textView Two -> longer"
app:layout_constraintEnd_toStartOf="#id/image"
app:layout_constraintTop_toBottomOf="#id/text1"
/>
<ImageView
android:id="#+id/image"
android:layout_width="150dp"
android:layout_height="300dp"
android:contentDescription="TODO"
android:src="#color/emphasis_red"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
</android.support.constraint.ConstraintLayout>
This would look like this:

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.

Categories

Resources