On some screens the text and image overlap. In that case I want to move the text upwards.
I place a TextView at a percentage position using a guideline. This is the preferred position of the text. Under it is an image with layout_width="match_parent" and ratio preserved. Depending on the screen aspect ratio, the image will become high enough to overlap with the text. In that case, I want the text to move upwards. So I want a "minimum constraint" of like 8dp between the text and image.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guidelineGoldenRatio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.381966" />
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Keep me from overlapping"
app:layout_constraintTop_toTopOf="#+id/guidelineGoldenRatio"
app:layout_constraintBottom_toTopOf="#+id/guidelineGoldenRatio"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ImageView
android:id="#+id/imageView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:adjustViewBounds="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="#drawable/ic_supercat_popular" />
</androidx.constraintlayout.widget.ConstraintLayout>
You can use a Barrier widget. Set the barrier's diection to "Top" and set the reference ids to your image and the guideline. (If you can't reference the guideline, constrain a Space widget to the guideline and set the Space widget's id in the referenced ids of the barrier.)
Now constrain the text to the barrier with an 8dp margin. The text will now float with the barrier which will always be 8dp above the higher of the guideline and image.
Based on Cheticamp's answer:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guidelineGoldenRatio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.381966" />
<androidx.constraintlayout.widget.Barrier
android:id="#+id/barrier"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:barrierDirection="top"
app:constraint_referenced_ids="imageView, guidelineGoldenRatio" />
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Keep me from overlapping"
android:textSize="30dp"
app:layout_constraintBottom_toTopOf="#+id/barrier"
app:layout_constraintTop_toTopOf="#+id/barrier"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ImageView
android:id="#+id/imageView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:adjustViewBounds="true"
android:paddingTop="18dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="#drawable/ic_supercat_popular" />
</androidx.constraintlayout.widget.ConstraintLayout>
Note: the minimum distance between text and image goes into image padding. This padding should also compensate for half text height (when centering text vertically around guideline).
How about binding TextView to the Image, and setting padding simply? The image would be always screen-wide, and having a height that depends on the screen type - but the text would be always 8dp over the image.
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Keep me from overlapping"
android:paddingBottom="8dp"
app:layout_constraintBottom_toTopOf="#+id/guidelineGoldenRatio"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ImageView
android:id="#+id/imageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="#drawable/ic_supercat_popular" />
Related
I am trying to set up a layout for a counting app, which has a large circular button slightly above the centre of the screen and a counter display below it. I'm having trouble keeping the circle circular, entirely on the screen, and not overlying the other view in all situations.
The setup I'm currently using is a ConstraintLayout with two TextViews. The button TextView uses an xml oval as its background, and is set to be circular with the "layout_constraintDimensionRatio" attribute set to one. In portrait mode the width is set to 80% of the available space, and all is well when the screen is fairly standard in normal use.
The problem occurs if the available screen dimensions for the app are more square so that the vertical dimension is smaller than the horizontal dimension. In this case the width is still set first so my circle covers the other view or partially slips off screen.
Currently in the landscape layout I have the timer off to one side. Ideally in the landscape layout I would keep the button centred and the counter to the right, but to stop increasing the size of the button if there is not enough space for the timer.
I think that ideally I would set the layout up to check whether the horizontal or the vertical space is shorter initially, then set the smaller dimension first. Is this possible? Or perhaps is there a better approach to achieving my desired layout?
Any help would be really appreciated,
Thank you,
Katie
This is the default xml layout:
<?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"
android:background="#android:color/white"
tools:context=".ui.CounterFragment">
<ImageView
android:id="#+id/imageView"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="#string/content_description_background_image"
android:scaleType="fitStart"
android:src="#drawable/dog_image_jpg_light"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1"
tools:visibility="visible" />
<TextView
android:id="#+id/timer_display"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/timer_display"
android:textSize="40sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/guidelineHorizontal" />
<TextView
android:id="#+id/counter_button"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#drawable/circle_selector"
android:contentDescription="#string/counter_button_content_description"
android:text="#string/counter_text"
app:layout_constraintBottom_toTopOf="#+id/timer_display"
app:layout_constraintDimensionRatio="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.375"
app:layout_constraintWidth_percent=".8" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guidelineVertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guidelineHorizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.75" />
</androidx.constraintlayout.widget.ConstraintLayout>
This is the landscape layout:
<?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"
android:background="#android:color/white"
tools:context=".ui.CounterFragment">
<ImageView
android:id="#+id/imageView"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="#string/content_description_background_image"
android:scaleType="fitStart"
android:src="#drawable/dog_image_jpg_light"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1"
tools:visibility="visible" />
<TextView
android:id="#+id/timer_display"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/timer_display"
android:textSize="40sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/counter_button"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/counter_button"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#drawable/circle_selector"
android:contentDescription="#string/counter_button_content_description"
android:text="#string/counter_text"
app:layout_constraintBottom_toBottomOf="#id/guidelineHorizontal"
app:layout_constraintDimensionRatio="1"
app:layout_constraintEnd_toStartOf="#id/guidelineVertical87"
app:layout_constraintHeight_percent="0.8"
app:layout_constraintStart_toEndOf="#id/guidelineVertical12"
app:layout_constraintTop_toTopOf="#id/guidelineHorizontal" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guidelineVertical75"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.75" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guidelineHorizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guidelineVertical12"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.125" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guidelineVertical87"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.875" />
</androidx.constraintlayout.widget.ConstraintLayout>
As you suggest, the primary issue with your layout is the percentage width/height set at 80% and driving the rest of the layout. You are losing control of the circle height and nothing keeps it from growing to cover other views.
Take a look at ConstraintLayout chains. They are a powerful tool and will let you tie your views together so they don't trounce one another. They will help you solve the overlay problem.
You will still want to set the width of the circle to 80% of the width or height depending on the orientation of the device. I assume that you want the side margins to be such that the circle can be 80% of the view but will allow the circle to be smaller if it needs to fit the screen and not overlap the other views. One way to do this is to define two Space widgets that are 10% of the layout width and 10% high. Place one in the upper left corner of the layout and the other in the lower right. Constrain the circle to these Space widgets on the left, top and right. Place the circle in a vertical chain with the TextView below.
Here is a mock up of what it would look like. You will have to work with your layout to get it right, but these are some concepts that should help. (Is seems to me that this can also be done without the Space widgets, but that solution is not occurring to me right now. I'll post back if it dawns on me.)
The XML follows the GIF.
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white">
<TextView
android:id="#+id/timer_display"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/timer_display"
android:textSize="40sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/counter_button" />
<TextView
android:id="#+id/counter_button"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#drawable/circle"
android:text="#string/counter_text"
app:layout_constraintBottom_toTopOf="#+id/timer_display"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toStartOf="#+id/spaceBottomRight"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="#+id/spaceStart"
app:layout_constraintTop_toBottomOf="#+id/spaceStart"
app:layout_constraintVertical_bias="0.375"
app:layout_constraintVertical_chainStyle="packed" />
<Space
android:id="#+id/spaceBottomRight"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.10"
app:layout_constraintWidth_percent="0.10" />
<Space
android:id="#+id/spaceStart"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintHeight_percent="0.10"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.10" />
</androidx.constraintlayout.widget.ConstraintLayout>
I am having ConstraintLayout unit inflated to LinearLayout with horizontal orientation so that 4 units fits in one line. I got layout working but its cutoff and the left edge since i use margin on right to get this margin as on image. One item is designed to as following img:
And the layout i designed for this item:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_weight="1">
<android.support.constraint.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="#+id/imageview_listunit_button"
app:layout_constraintRight_toRightOf="#+id/imageview_listunit_button"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.0"
android:layout_marginTop="8dp"
android:layout_marginRight="20dp"
android:background="#drawable/back_one">
<ImageView
android:id="#+id/imageview_listunit_image"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:src="#drawable/placeholder"
android:padding="6dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/>
<TextView
android:id="#+id/textview_listunit_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textColor="#color/color_text"
android:layout_gravity="start"
android:paddingStart="6dp"
android:paddingEnd="6dp"
android:paddingBottom="12dp"
app:layout_constraintTop_toBottomOf="#id/imageview_listunit_image"
app:layout_constraintLeft_toLeftOf="#id/imageview_listunit_image"
app:layout_constraintRight_toRightOf="#id/imageview_listunit_image"/>
</android.support.constraint.ConstraintLayout>
<ImageView
android:id="#+id/imageview_listunit_button"
android:layout_width="40dp"
android:layout_height="40dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:src="#drawable/button"/>
</android.support.constraint.ConstraintLayout>
I believe the problem is with margin and i margin the black layout from red a bit, and it cuts off on left edge than. I tried with padding but no success any tips how should i solve this?
Since the red ImageView in the top right corner has fixed dimensions, it allows for some margin manipulation. We set the width of the black ImageView to match constraints of the parent but set a margin on the right and on the top of 20dp (half of the red ImageView). We then position the center of the red ImageView in the top right corner of the black one and there is enough space because of the margins:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_weight="1">
<android.support.constraint.ConstraintLayout
android:id="#+id/background"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="20dp"
android:layout_marginTop="20dp"
android:background="#drawable/back_one"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
<ImageView
android:id="#+id/imageview_listunit_image"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:src="#drawable/placeholder"
android:padding="6dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<TextView
android:id="#+id/textview_listunit_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textColor="#color/color_text"
android:layout_gravity="start"
android:paddingStart="6dp"
android:paddingEnd="6dp"
android:paddingBottom="12dp"
app:layout_constraintTop_toBottomOf="#id/imageview_listunit_image"
app:layout_constraintLeft_toLeftOf="#id/imageview_listunit_image"
app:layout_constraintRight_toRightOf="#id/imageview_listunit_image" />
</android.support.constraint.ConstraintLayout>
<ImageView
android:id="#+id/imageview_listunit_button"
android:layout_width="40dp"
android:layout_height="40dp"
app:layout_constraintBottom_toTopOf="#id/background"
app:layout_constraintLeft_toRightOf="#id/background"
app:layout_constraintRight_toRightOf="#id/background"
app:layout_constraintTop_toTopOf="#id/background"
android:src="#drawable/button"/>
</android.support.constraint.ConstraintLayout>
Okay i solved this the other way around, i just put the small red circle on top and than margined the main layout for the half width of red button.
Consider the following layout:
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:padding="8dp"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Text" />
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toEndOf="#id/text_view"
app:layout_constraintTop_toTopOf="parent"
tools:background="#android:color/black" />
</android.support.constraint.ConstraintLayout>
The image view (or button, or any other widget, important is that it has a fixed size) is aligned right/end of a TextView with a size of wrap_content, thus, the text itself only takes the space it needs.
What I now want is, when the text expands (aka if it's longer), that the button gets aligned to the right of the parent, and the text view only gets the width that is left and gets ellipsized.
At the moment, the image view is pushed outside and the text gets ellipsized once it fills the whole parent.
I tried to fix this by adding a endToEnd="parent" constraint the the image and setting the horizontal bias to 0 (see code), but that doesn't work.
Any suggestions on what I could do instead?
In order to prevent the ImageView from getting pushed outside the parent you need to constrain the end of the TextView to the start of the ImageView to create a chain. Now to ensure that the TextView only takes the space it needs but also has its constraints enforced, you need to add app:layout_constrainedWidth="true" attribute to your TextView.
If you want the ImageView to stay immediately to the right of the TextView can use packed chain style with a horizontal bias of 0.
If you want the ImageView to be aligned to the right of the parent you can use the spread_inside chain style without bias.
The resulting XML with above changes might look like this:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:padding="8dp"
android:textSize="18sp"
app:layout_constrainedWidth="true"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#id/image_view"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Text" />
<ImageView
android:id="#+id/image_view"
android:layout_width="48dp"
android:layout_height="48dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#id/text_view"
app:layout_constraintTop_toTopOf="parent"
tools:background="#android:color/black" />
</android.support.constraint.ConstraintLayout>
EDIT: As requested in the comment, here's how it would work with a Barrier sparating the 3 TextViews and the ImageView. In this case you don't specify the constraints for the ends of the TextViews thus you no longer have them chained horizontally. Instead, you create a Barrier with the direction being the end of all TextViews that are referenced in the Barrier. This will ensure that the Barrier is always at the end of the widest TextView. The last thing to do is to constrain the start side of the ImageView to the Barrier.
Example XML:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/text_view1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:padding="8dp"
android:textSize="18sp"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintBottom_toTopOf="#id/text_view2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Text1" />
<TextView
android:id="#+id/text_view2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:padding="8dp"
android:textSize="18sp"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintBottom_toTopOf="#id/text_view3"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/text_view1"
tools:text="Text2" />
<TextView
android:id="#+id/text_view3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:padding="8dp"
android:textSize="18sp"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/text_view2"
tools:text="Text3" />
<android.support.constraint.Barrier
android:id="#+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="end"
app:constraint_referenced_ids="text_view1,text_view2,text_view3" />
<ImageView
android:id="#+id/image_view"
android:layout_width="48dp"
android:layout_height="48dp"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#id/barrier"
app:layout_constraintTop_toTopOf="parent"
tools:background="#android:color/black" />
</android.support.constraint.ConstraintLayout>
I'm having a bit of a problem with ConstraintLayout in Android.
Observe the following illustration:
I would like to draw your attention to the row of three views across the middle: the TextView, and the two Buttons.
The dotted vertical and horizontal lines are fixed guidelines (expressed in terms of percentage of screen height/width), and are used to constrain these views to.
The TextView is constrained in all 4 directions as you can see.
The Button on the far right is constrained in 3 directions: top, bottom, and right... and has an aspect ratio constraint on it of 1:1.
Now, the Button in the middle is only constrained on top and bottom, and also has an aspect ratio applied of 1:1.
Everything is great so far... but what if we want to keep everything exactly the same except center the middle button such that it has equal space to its right and to its left?
One might expect that simply applying horizontal constraints to its nearest neighbors would do the trick... but no.
This is the result:
So... the size of the button is increasing for some reason. Why?
It seems to me that Android Studio is applying the 1:1 aspect ratio rule (in this case) by saying "height is a function of width..." that is, width is calculated first (based on the newly applied constraints) and therefore takes precedence.
If it had simply done it the other way around and said "width = height" with height taking precedence... (the same way it was doing prior to those final constraints being applied), then everything would be fine.
So... in other words, how can you center a "middle" view between two other "right" and "left" views and still maintain:
1) All three views have matching heights since they are bound by the same horizontal constraints.
2) The left view is in a fixed position.
3) The right view has a 1:1 aspect ratio and is not allowed to separate from its right-side guideline (fixed position).
?
EDIT: I have made the following XML snippet for anyone that wants to play with it:
(Remember the three rules above. Have fun!)
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.boober.deletethis.MainActivity">
<android.support.constraint.Guideline
android:id="#+id/guidelineH1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.45" />
<android.support.constraint.Guideline
android:id="#+id/guidelineH2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.55" />
<android.support.constraint.Guideline
android:id="#+id/guidelineV1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.50" />
<TextView
android:id="#+id/textView"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#color/colorPrimary"
android:text="1"
android:textAlignment="center"
android:gravity="center_vertical"
app:layout_constraintBottom_toTopOf="#+id/guidelineH2"
app:layout_constraintEnd_toStartOf="#+id/guidelineV1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/guidelineH1" />
<Button
android:id="#+id/button"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="3"
app:layout_constraintBottom_toTopOf="#+id/guidelineH2"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="#+id/guidelineH1" />
<Button
android:id="#+id/button2"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="2"
app:layout_constraintBottom_toTopOf="#+id/guidelineH2"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toTopOf="#+id/guidelineH1"
tools:layout_editor_absoluteX="219dp" />
</android.support.constraint.ConstraintLayout>
Updated:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.constraint.Guideline
android:id="#+id/guidelineH1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.45" />
<android.support.constraint.Guideline
android:id="#+id/guidelineH2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.55" />
<android.support.constraint.Guideline
android:id="#+id/guidelineV1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.50" />
<TextView
android:id="#+id/textView"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#color/colorPrimary"
android:gravity="center_vertical"
android:text="1"
android:textAlignment="center"
app:layout_constraintBottom_toTopOf="#+id/guidelineH2"
app:layout_constraintEnd_toStartOf="#+id/guidelineV1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/guidelineH1" />
<Button
android:id="#+id/button"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="3"
app:layout_constraintBottom_toTopOf="#+id/guidelineH2"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="#+id/guidelineH1" />
<android.support.constraint.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/guidelineH2"
app:layout_constraintEnd_toStartOf="#+id/button"
app:layout_constraintStart_toEndOf="#+id/textView"
app:layout_constraintTop_toTopOf="#+id/guidelineH1">
<Button
android:id="#+id/button2"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
I want to show an icon in the top right corner of an image. Also, it should be slightly offset. And finally, the combination of both should be scalable, so that doubling the total width results in the image as well as the icon having thwice the original width. Of course, the aspect ration should be kept so that the height doubles as well.
As the description of that actually simple problem is quite complex, here's an artful drawing illustrating the subject :)
So, let's say I have a RecyclerView with a GridLayout where each image tile has a width of 200dp. Then, the gray area should have a width and height of 200dp, the orange icon has a width and height of 50dp, the offset of the icon to the blue image's border is 25dp and so on.
And if I decide to double the number of columns in my GridLayout, the gray area has a width and height of 100dp etc.
Here's what I tried so far:
I started out with a FrameLayout which works fine for the simplest cases but becomas problematic as soon you want to specify dimensions with percentages.
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="50dp"
android:adjustViewBounds="true"
app:srcCompat="#drawable/blueShip200x200" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|end"
android:layout_marginRight="25dp"
android:layout_marginTop="25dp"
app:srcCompat="#drawable/new50x50" />
</FrameLayout>
Then, I learned about the PercentFrameLayout that has been introduced with the support library, recently - and has already been deprecated: https://developer.android.com/reference/android/support/percent/PercentFrameLayout.html
So now the advice is to use ConstraintLayout instead (see link above) where you can use helper guidelines that allow you to define their position with percentages.
Fortunately, the docs give an example how to replace the PercentFrameLayout which works great if the gray box has a fixed width and height:
<android.support.constraint.ConstraintLayout
android:layout_width="200dp"
android:layout_height="200dp">
<android.support.constraint.Guideline
android:id="#+id/left_image_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent=".25" />
<android.support.constraint.Guideline
android:id="#+id/right_image_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent=".75" />
<android.support.constraint.Guideline
android:id="#+id/top_image_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent=".25" />
<android.support.constraint.Guideline
android:id="#+id/bottom_image_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent=".75" />
<ImageView
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="#+id/bottom_image_guideline"
app:layout_constraintLeft_toLeftOf="#+id/left_image_guideline"
app:layout_constraintRight_toRightOf="#+id/right_image_guideline"
app:layout_constraintTop_toTopOf="#+id/top_image_guideline"
app:srcCompat="#drawable/blueShip200x200" />
<android.support.constraint.Guideline
android:id="#+id/left_icon_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent=".625" />
<android.support.constraint.Guideline
android:id="#+id/right_icon_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent=".875" />
<android.support.constraint.Guideline
android:id="#+id/top_icon_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent=".125" />
<android.support.constraint.Guideline
android:id="#+id/bottom_icon_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent=".375" />
<ImageView
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="#+id/bottom_icon_guideline"
app:layout_constraintLeft_toLeftOf="#+id/left_icon_guideline"
app:layout_constraintRight_toRightOf="#+id/right_icon_guideline"
app:layout_constraintTop_toTopOf="#+id/top_icon_guideline"
app:srcCompat="#drawable/new50x50" />
</android.support.constraint.ConstraintLayout>
But if you do not specifiy fixed dimensions... it doesn't.
Adapting the following lines from the FrameLayout example (layout_width, layout_height, adjustViewBounds) results in the ConstraintLayout getting a height of 0.
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="0dp"
android:layout_height="0dp"
android:adjustViewBounds="true"
app:layout_constraintBottom_toBottomOf="#+id/bottom_image_guideline"
app:layout_constraintLeft_toLeftOf="#+id/left_image_guideline"
app:layout_constraintRight_toRightOf="#+id/right_image_guideline"
app:layout_constraintTop_toTopOf="#+id/top_image_guideline"
app:srcCompat="#drawable/blueShip200x200" />
I hope it's possible to solve this problem non-programmatically with some changes to the layout file. Basically, it's just adjustViewBounds that is not working with the ImageView's width and height set to match_constraint (0dp).
Also, if possible, I'd like to stick with ConstraintLayout. Maybe one can work around the problem using LinearLayouts with Spaces and layout_weight - I haven't tried that yet.
Thanks!
you can create a layout using a constraint layout.
first you split the layout into 5x5 using some views as a guide.
the key is to use the horizontal and vertical weights.
<!--GUIDE LINE-->
<TextView
android:id="#+id/guide_1"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#color/primary_light"
android:text="25%"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_weight="25"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="#+id/guide_2"
app:layout_constraintTop_toBottomOf="#+id/guide_2"
app:layout_constraintVertical_weight="25" />
<TextView
android:id="#+id/guide_2"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#color/primary_light"
android:text="37.5%"
app:layout_constraintBottom_toTopOf="#id/guide_1"
app:layout_constraintHorizontal_weight="37.5"
app:layout_constraintLeft_toRightOf="#id/guide_1"
app:layout_constraintRight_toLeftOf="#id/guide_3"
app:layout_constraintTop_toBottomOf="#id/guide_3"
app:layout_constraintVertical_weight="37.5" />
<TextView
android:id="#+id/guide_3"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#color/primary_light"
android:text="12.5%"
app:layout_constraintBottom_toTopOf="#id/guide_2"
app:layout_constraintHorizontal_weight="12.5"
app:layout_constraintLeft_toRightOf="#id/guide_2"
app:layout_constraintRight_toLeftOf="#id/guide_4"
app:layout_constraintTop_toBottomOf="#id/guide_4"
app:layout_constraintVertical_weight="12.5" />
<TextView
android:id="#+id/guide_4"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#color/primary_light"
android:text="12.5%"
app:layout_constraintBottom_toTopOf="#id/guide_3"
app:layout_constraintHorizontal_weight="12.5"
app:layout_constraintLeft_toRightOf="#id/guide_3"
app:layout_constraintRight_toLeftOf="#id/guide_5"
app:layout_constraintTop_toBottomOf="#id/guide_5"
app:layout_constraintVertical_weight="12.5" />
<TextView
android:id="#+id/guide_5"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#color/primary_light"
android:text="12.5%"
app:layout_constraintBottom_toTopOf="#id/guide_4"
app:layout_constraintHorizontal_weight="12.5"
app:layout_constraintLeft_toRightOf="#id/guide_4"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_weight="12.5" />
after you create the guide, you can put the images you want with the guide line...
the two views would be something like the following.
<TextView
android:text="MAIN ICON"
android:textColor="#color/icons"
android:gravity="center"
android:textSize="60sp"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintLeft_toLeftOf="#id/guide_2"
app:layout_constraintBottom_toBottomOf="#id/guide_2"
app:layout_constraintTop_toTopOf="#id/guide_3"
app:layout_constraintRight_toRightOf="#id/guide_3"
android:background="#color/primary"/>
<TextView
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="#id/guide_4"
app:layout_constraintRight_toRightOf="#id/guide_4"
app:layout_constraintBottom_toBottomOf="#id/guide_3"
app:layout_constraintLeft_toLeftOf="#id/guide_3"
android:background="#color/accent"
android:text="NEW"
android:gravity="center"
android:textSize="40sp"
android:textColor="#color/icons"/>
FIxing dimension of small image will help you attain that kind of UI. Please look into code below
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="25dp"
android:paddingRight="25dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:id="#+id/image"
app:layout_constraintBottom_toBottomOf="parent"
app:srcCompat="#drawable/blue200" />
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
app:layout_constraintTop_toTopOf="#+id/image"
app:layout_constraintRight_toRightOf="#+id/image"
app:srcCompat="#drawable/new50x50" />
</android.support.constraint.ConstraintLayout>
What you want to accomplish can be done with some recent additions to ConstraintLayout. I did the following with version 1.1.0-beta3. See the documentation here.
With later releases of ConstrainLayout, we can now specify the width and height of a view as a percentage of the containing view's width/height. The blue box is sized at 50% of the width/height of the gray area and centered.
The orange box is sized at 25% of the container's width and height. It is centered on the top right corner of the blue box by setting its start and end sides to the end of the blue box and its top and bottom to the top.
The layout will accommodate all sizes but, as you mention, the aspect ratios must be respected.
<android.support.constraint.ConstraintLayout
android:layout_width="200dp"
android:layout_height="200dp">
<ImageView
android:id="#+id/boat"
android:layout_width="0dp"
android:layout_height="0dp"
android:adjustViewBounds="true"
android:src="#drawable/boat"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_default="percent"
app:layout_constraintHeight_percent="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent="0.5" />
<ImageView
android:layout_width="0dp"
android:layout_height="0dp"
android:src="#drawable/new_square"
app:layout_constraintBottom_toTopOf="#id/boat"
app:layout_constraintEnd_toEndOf="#id/boat"
app:layout_constraintHeight_default="percent"
app:layout_constraintHeight_percent="0.25"
app:layout_constraintStart_toEndOf="#id/boat"
app:layout_constraintTop_toTopOf="#id/boat"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent="0..25" />
</android.support.constraint.ConstraintLayout>
Here are the images I used: