I am creating a dialog with two buttons aligned right in relation to the parent ConstraintLayout.
Everything is fine, until the text of the buttons becomes very long. When the text of either or both buttons is lengthy, the buttons extend beyond the bounds of the parent, causing clipping of the text, as shown in the image below. I would like to handle cases where there is longer text.
i.e. The desired behavior would be
buttons should wrap text when text is long
buttons should stay within bounds of parent and obey parent padding
buttons should stay aligned right of parent
When button text is short, the layout works as intended:
When button text is long:
Cancel text is truncated when cancel button text is long. This is happening because the button itself is being pushed past the parent's boundaries.
Cancel text pushed beyond parent boundaries when ok button's text is long, again, because the button is pushed beyond the parent's boundaries.
Layout code
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="#dimen/dialog_padding"
android:paddingLeft="#dimen/dialog_padding"
android:paddingRight="#dimen/dialog_padding"
android:paddingTop="#dimen/dialog_padding">
<TextView
android:id="#+id/dialog_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/dialog_text_margin"
tools:text="Dialog title" />
<TextView
android:id="#+id/dialog_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/dialog_text_margin"
app:layout_constraintTop_toBottomOf="#id/dialog_title"
tools:text="Dialog text content" />
<Button
android:id="#+id/cancel_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toStartOf="#id/ok_btn"
app:layout_constraintTop_toBottomOf="#id/dialog_content"
tools:text="Dismiss" />
<Button
android:id="#+id/ok_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#id/dialog_content"
tools:text="Accept" />
</android.support.constraint.ConstraintLayout>
Things that I've tried to no avail:
adding app:layout_constraintStart_toStartOf="parent" to the cancel button causes the buttons to no longer be aligned right, and so that solution is incorrect
constraining the end of the dismiss button to the start of the accept button causes buttons to no longer be aligned right
using layout_width="0dp" for the buttons and using app:layout_constrainedWidth="true" has no effect
Here are two screen shots of what I think you are trying to accomplish.
First, with some short text:
Now with some long text:
I took a few liberties with the layout and introduced a guideline at 33% of the width that the button are constrained to. You didn't specify how much the button could expand horizontally, so I made this assumption.
The XML for this layout follows. I set the width of the buttons to 0dp or match_constraints so their size would adjust. The buttons have also been placed into a packed chain that groups the two buttons together. The horizontal bias is set to 0.5 now, but increasing it will move the group to the right if it starts to creep left on you.
The ConstraintLayout documentation has some good descriptions of these features and how to use them.
<android.support.constraint.Guideline
android:id="#+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.33" />
<TextView
android:id="#+id/dialog_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/dialog_text_margin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Dialog title" />
<Button
android:id="#+id/cancel_btn"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toStartOf="#+id/ok_btn"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="#+id/guideline"
app:layout_constraintTop_toBottomOf="#+id/dialog_title"
tools:text="Dismiss" />
<Button
android:id="#+id/ok_btn"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="#+id/cancel_btn"
app:layout_constraintTop_toTopOf="#+id/cancel_btn"
tools:text="Accept" />
Related
I have a constraint layout with 3 buttons as shown below.
<androidx.appcompat.widget.AppCompatImageButton
android:id="#+id/img_subscribe"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/dp20"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone"
/>
<androidx.appcompat.widget.AppCompatImageButton
android:id="#+id/img_start_over"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/dp45"
app:layout_constraintStart_toEndOf="#id/img_subscribe"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone"
/>
<androidx.appcompat.widget.AppCompatImageButton
android:id="#+id/img_trailer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/dp45"
app:layout_goneMarginLeft="#dimen/dp20"
app:layout_constraintStart_toEndOf="#id/img_start_over"
app:layout_constraintTop_toTopOf="parent"
android:visibility="visible"
/>
I want to give gone margin for third button only when first two are not visible. But in this current code if the it is taking gone margin when immediate previous button is not visible. How can I make the 3rd button to take the gone margin if and only if the first two are not visible.
Any suggestions appreciated.
This is a great use case for Chains in constraint layout.
Chains in constraintlayout
Instead of using a linear layout chain your buttons together. The 3rd button will take the gone margin as you want it to
I want to create a user name centered TextView with an edit button to the right of the screen. If I specify the TextView to match the start/end of the parent, it will center. However, the text will overflow and paint over the button. I can chain one side of the text to the button, but then the text will not be centered. Here is the example code I have where tvName is not centered due to the button in the horizontal chain:
<TextView
android:id="#+id/tvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="#dimen/pt_name_font_size"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="#id/tvUserMsisdn"
app:layout_constraintEnd_toStartOf="#id/ibChangeName"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/aivProfile"
tools:text="B"/>
<TextView
android:id="#+id/tvName2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="#dimen/pt_name_font_size"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="#id/tvUserMsisdn"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/tvName"
tools:text="A"/>
<ImageButton
android:id="#+id/ibChangeName"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginEnd="#dimen/default_margin"
app:layout_constraintBottom_toBottomOf="#id/tvName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="#id/tvName"
android:background="#android:color/black"/>
This is what it looks like:
I can't use a horizontal bias, since the button has to remain with a static size, and the width of the screen may vary, as well as the size of the font. Ideally, I would be able to specify two app:layout_constraintEnd_toStartOf attributes, one for the whole width of the screen, so the text remains centered, and another one for the button, so it won't overflow.
Ideally, I would be able to specify two app:layout_constraintEnd_toStartOf attributes
I'm sure you already know this, but that is impossible.
The only thing you can do that is like that is to use a Barrier, but that's not helpful here because (in this case) that would be functionally identical to simply constraining the end to the start of the ImageButton.
The best thing you can do here, in my opinion, is to constrain the TextView's start and end to the parent, and then use padding to make sure that long text doesn't overlap the ImageButton. The button has a fixed width (28dp), and a fixed margin (#dimen/default_margin), so you can give the TextView left/right padding equal to 28dp + 2x default_margin. This will make sure that text wraps at least default_margin away from the left edge of the ImageButton.
Put both the TextView and the ImageButton inside a RelativeLayout:
<RelativeLayout
android:id="#+id/container"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="#+id/tvName2"
android:layout_alignParentStart="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toStartOf="#+id/ibChangeName"
android:textSize="#dimen/pt_name_font_size"
android:textStyle="bold"
tools:text="A"/>
<ImageButton
android:id="#+id/ibChangeName"
android:layout_alignParentEnd="true"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginEnd="#dimen/default_margin"
android:background="#android:color/black"/>
</RelativeLayout>
This attribute for the TextView
android:layout_toStartOf="#+id/ibChangeName"
ensures that the TextView won't overlap with the ImageButton
Now you have to center the relative layout
If the ImageButton always has a fixed size (28dp in this case) you can use a Space widget with the same dimensions as the button and place it on the left edge of the parent. You can then chain the three Views together and set the chain's style to spread_inside. The TextView's width can be set to match_constraint and its gravity to center or use wrap_content and app:layout_constrainedWidth="true" so it doesn't overlap the other Views if it gets too big.
Example XML to get the general idea:
<?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">
<Space
android:id="#+id/space"
android:layout_width="28dp"
android:layout_height="28dp"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintEnd_toStartOf="#id/textview"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="#+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Textview"
app:layout_constrainedWidth="true"
app:layout_constraintEnd_toStartOf="#id/button"
app:layout_constraintStart_toEndOf="#id/space" />
<ImageButton
android:id="#+id/button"
android:layout_width="28dp"
android:layout_height="28dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#id/textview" />
</android.support.constraint.ConstraintLayout>
Can't understand how to implement simple thing.
I need layout of 2 views with next behaviour:
button should be to right of text when text is short
when text is long it ellipsizes and button always visible and full-width
now I get that button is going out of screen
You can achieve this using ConstraintLayout. Here's a template:
<?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/text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Hello world"
android:maxLines="1"
android:ellipsize="end"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintWidth_default="wrap"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="#+id/button"
app:layout_constraintBottom_toBottomOf="parent"/>
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="HELLO WORLD"
app:layout_constraintLeft_toRightOf="#+id/text"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBaseline_toBaselineOf="#+id/text"/>
</android.support.constraint.ConstraintLayout>
The initial setup is:
Create a horizontal chain including the text and the button
Set the chain style to "packed" so that there's no space between the views
Set the horizontal bias to 0 so that the packed views hug to the left
The magic comes with the TextView's width and the app:layout_constraintWidth_default attribute. By setting the width to 0dp and the "default width" to wrap, we're telling Android to give the view as much space as it needs to hold its contents as long as it fits the constraints. When the text is really long, the constraints will keep it from pushing the button off the right side of the screen.
I created two rows of ImageButtons through chains and used the packed style to close the gap between the two rows. However there is a lot of extra space surrounding the overall ImageButtons rows on the top and bottom.
When I try to use layout_constraintVertical_bias="0.1" on the top row buttons and layout_constraintVertical_bias="0.9" on the bottom row buttons to remove the gap it only shifts the rows up to the top of the layout. I know chain constraint details should be in the parent view but is there anyway to get the children of the chain to adapt a bias as well? I know this can be done by changing the width to 0dp but when I do that the images on the bottom become wider than the images at the top due to an uneven number of buttons.
<?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="wrap_content">
<ImageButton
android:id="#+id/button1"
android:layout_width="80dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/button4"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="#id/button2"
app:layout_constraintVertical_chainStyle="packed"/>
<ImageButton
android:id="#+id/button2"
android:layout_width="80dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/button4"
app:layout_constraintLeft_toRightOf="#+id/button1"
app:layout_constraintRight_toLeftOf="#id/button3"
app:layout_constraintVertical_bias="1"/>
<ImageButton
android:id="#+id/button3"
android:layout_width="80dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/button5"
app:layout_constraintLeft_toRightOf="#id/button2"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintVertical_chainStyle="packed"/>
<ImageButton
android:id="#+id/button4"
android:layout_width="80dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#id/button1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="#id/button5"
app:layout_constraintBottom_toBottomOf="parent"/>
<ImageButton
android:id="#+id/button5"
android:layout_width="80dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#id/button3"
app:layout_constraintLeft_toRightOf="#id/button4"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</android.support.constraint.ConstraintLayout>
Edit:
This is what I'm trying to accomplish below. I basically want the arrows to push up to the red line while the ImageButtons stay in the place they are at right now so there is a gap between the buttons and the top and bottom edge of the layout. I attempted to shrink the gap in the second image using vertical bias but as you can see it only worked for the top row of buttons and not the buttons on the bottom row.
So, the question is really "why is my ConstraintLayout so tall?" If you can use a later release of ConstraintLayout (1.1.0-beta3 is the latest), this problem will go away. If you can't use a beta release, there is a work-around but upgrading is the best solution. Ping back if you can't use a beta release.
Let's start with the fun part, here's the graphic of the trouble. Horizontally, everything is beautiful.
The middle button, I'd like to to be aligned with the other three. Here are the basics:
overall, it's a relativelayout
inside this relativelayout, it's a horizontal linear layout, containing the three buttons
the "sinking" of the middle button is 100% correlated with it being a dual line of text, if I change it to a single line, it aligns properly
the specified height of the buttons has nothing to do with the sinking, even at more than double their current size (from current 70 to 170) the exact same behavior (and size of behavior) is displayed
The "custom_button" background has no effect, if I change them all to no background, stock looking buttons, the same positioning occurs
And here's the XML (of just the linearlayout within the relativelayout):
<LinearLayout
android:id="#+id/wideButtons"
android:layout_below="#+id/buttonClockFinish"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginLeft="30dp"
android:layout_marginRight="30dp"
android:orientation="horizontal">
<Button
android:id="#+id/buttonLog"
android:layout_weight="1"
android:layout_height="70dp"
android:padding="0dp"
android:layout_marginRight="3dp"
android:layout_width="fill_parent"
android:background="#drawable/custom_button"
android:text="View Log" />
<Button
android:id="#+id/buttonLocation"
android:layout_weight="1"
android:layout_height="70dp"
android:padding="0dp"
android:layout_marginLeft="3dp"
android:layout_marginRight="3dp"
android:layout_width="fill_parent"
android:background="#drawable/custom_button"
android:text="Location\nD1-RS" />
<Button
android:id="#+id/buttonHelp"
android:layout_weight="1"
android:layout_height="70dp"
android:padding="0dp"
android:layout_marginLeft="3dp"
android:layout_width="fill_parent"
android:background="#drawable/custom_button"
android:text="Help" />
</LinearLayout>
So why on earth is it not aligning?
I was just about to post this question, and did one final experiment. I added a THIRD line of text to the button. This pushed it down even further. But what I realized it had in common was that the text of the top line of the middle button remained perfectly aligned with the text of the two buttons to either side of it.
So it wasn't that it was having trouble with an interior margin, unable to squish the text against the top border. The alignment was of the TEXT, not the button graphic. All along I had thought that there was some mystery :padding that I was not nulling out, but with three lines of button text it was quite happy to have just about 1dp or so of padding.
The solution was to add
android:layout_gravity="center_vertical"
to that button. I added it to the rest of them too, just for consistency.
Lesson: When you think things aren't aligning, perhaps they actually are, but maybe you're looking at the wrong thing.