After adding a ConstraintLayout inside a ScrollView, I noticed that the content remains stuck in the middle of the screen (both horizontally and vertically). I already tried add android:layout_gravity="top" to the main ConstraintLayout but that didn't work. Is there something else I can use to position the content at the top?
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
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"
android:fillViewport="true">
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/cl_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingStart="#dimen/activity_horizontal_margin"
android:paddingEnd="#dimen/activity_horizontal_margin">
<androidx.cardview.widget.CardView
android:id="#+id/cv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="30dp"
app:cardUseCompatPadding="true"
app:contentPadding="10dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="#+id/tv_disclaimer">
<LinearLayout
android:id="#+id/ll_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="#+id/iv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:importantForAccessibility="no"
android:paddingEnd="10dp" />
<TextView
android:id="#+id/tv_title"
style="#android:style/TextAppearance.Medium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorPrimary" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<TextView
android:id="#+id/tv_disclaimer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:gravity="center"
android:textColor="?android:attr/textColorPrimary"
app:layout_constraintTop_toBottomOf="#+id/cv_title"
app:layout_constraintBottom_toTopOf="#+id/cv_description"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
style="#android:style/TextAppearance.Medium" />
<androidx.cardview.widget.CardView
android:id="#+id/cv_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardUseCompatPadding="true"
app:contentPadding="10dp"
app:layout_constraintTop_toBottomOf="#+id/tv_disclaimer"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/cl_description"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/ll_allergies"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="#+id/ll_ingredients">
<ImageView
android:id="#+id/iv_allergies"
android:importantForAccessibility="no"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingEnd="5dp"/>
<TextView
android:id="#+id/tv_allergies"
style="#android:style/TextAppearance.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textColor="?android:attr/textColorPrimary" />
</LinearLayout>
<LinearLayout
android:id="#+id/ll_allergyinfo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="#+id/ll_allergies"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="#+id/ll_ingredients">
<TextView
android:id="#+id/tv_allergyinfo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorPrimary"
android:layout_marginBottom="2dp"
android:gravity="start"
style="#android:style/TextAppearance.Medium" />
</LinearLayout>
<LinearLayout
android:id="#+id/ll_ingredients"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="#+id/ll_allergyinfo"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<ImageView
android:id="#+id/iv_ingredients"
android:importantForAccessibility="no"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingEnd="5dp" />
<TextView
android:id="#+id/tv_ingredients"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textColor="?android:attr/textColorPrimary"
style="#android:style/TextAppearance.Large" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
Current result
Two important things are going on here.
The first is that your ScrollView specifies android:fillViewport="true". This means that whenever the contents of the ScrollView are "smaller" than the screen size, they will be stretched to fill the screen. In other words, your wrap_content ConstraintLayout will only actually wrap its content when you have to scroll. In all other cases, your ConstraintLayout will functionally be match_parent in height.
The second is that the constraints on your CardView, TextView, and second CardView form a vertical chain. Chains have special behavior regarding spacing when the parent is larger than the sum of the views in the chain (which it is due to the fillViewport behavior discussed above); the default is "spread", which will position the three views such that all the remaining space is evenly divided into four chunks (one at the start of the chain, one between the first two views, one between the second two views, and the last at the end of the chain).
How to fix it, though?
The best thing to do is to break the chain. Assuming you just want the views all stacked one after the other, starting from the top of the screen, there's no need to use a chain. To break the chain, remove the bottom constraint from each view:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
...>
<androidx.constraintlayout.widget.ConstraintLayout
...>
<androidx.cardview.widget.CardView
android:id="#+id/cv_title"
...
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
...
</androidx.cardview.widget.CardView>
<TextView
android:id="#+id/tv_disclaimer"
...
app:layout_constraintTop_toBottomOf="#+id/cv_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.cardview.widget.CardView
android:id="#+id/cv_description"
...
app:layout_constraintTop_toBottomOf="#+id/tv_disclaimer"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
...
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
Alternatively, you could delete the fillViewport attribute from your ScrollView. Without this, the child ConstraintLayout won't ever be stretched to fill the screen, and wrap_content will mean that there's never any extra space to spread out between your chain views.
Try removing app:layout_constraintTop_toTopOf="parent" from your ll_allergies LinearLayout. When copied on my AS it was this line that was adding the unnecessary space between the textview and the layout. Alternatively, keep the constraint and add 0dp on its height so that it enforces the constraint.
UPDATE after comment: I think your views are overlapping because the bottom constraint of ll_allergies is on the top of ll_ingredients instead of ll_allergyinfo. Try changing this:
app:layout_constraintBottom_toTopOf="#id/ll_ingredients"
to this:
app:layout_constraintBottom_toTopOf="#id/ll_allergyinfo"
and bring back the wrap_content of ll_alergies. Hopefully, now everything will fall into place
Update: response no longer valide after the code was updated
There are some errors in your code:
1.you set the layout to be to bottom of itself
android:id="#+id/ll_ingredients"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintTop_toBottomOf="#+id/ll_ingredients"`
the id "ll_alighting" is not defined
Related
I have two TextViews, one above the other. I would like the vertical middle of the two TextViews to be at the same position as the vertical middle of the ImageView. (This is so, regardless of the amount of text that may go into either TextView, everything will always look neat, vertically.)
I created what I need perfectly using two LinearLayouts (as the space above the title is the same as the space beneath the description):
But Android Studio was unable to covert it to ConstraintLayout successfully, as it just dumped the TextViews at the bottom of the layout. I've played with a lot of attributes, but could not quite arrive at the desired layout.
My question is similar to this one, except that I am trying to center_vertical align a pair of views rather than a single one - which means I have no view edge to align to the centre of the ImageView/container.
Is it possible to achieve what I'm after with ConstraintLayout? (I expect I may be able to do it with a single RelativeLayout, but I would like to use the layout_constraintDimensionRatio attribute on my ImageView which presumably leave me needing to use ConstraintLayout.)
In case it helps, here's the code for my aforementioned LinearLayout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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:orientation="horizontal">
<ImageView
android:id="#+id/imageView"
android:layout_width="#dimen/resources_list_image_size"
android:layout_height="#dimen/resources_list_image_size"
android:layout_marginEnd="#dimen/activity_horizontal_margin"
android:layout_marginRight="#dimen/activity_horizontal_margin"
android:layout_gravity="center_vertical"
android:contentDescription="#string/resource_image"
android:scaleType="centerCrop"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="vertical">
<TextView
android:id="#+id/textViewTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="#style/MyTextAppearanceMedium"
tools:text="Title" />
<TextView
android:id="#+id/textViewDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="#style/MyTextAppearanceSmall"
tools:text="Description" />
</LinearLayout>
</LinearLayout>
Update: Solution
Thanks to Ben P's answer, this is my final 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="wrap_content">
<!-- Add guideline to align imageView to. -->
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.3" />
<ImageView
android:id="#+id/imageView"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="#string/resource_image"
android:scaleType="centerCrop"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="#id/guideline"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<TextView
android:id="#+id/textViewTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/activity_horizontal_margin"
android:layout_marginLeft="#dimen/activity_horizontal_margin"
app:layout_constraintBottom_toTopOf="#id/textViewDescription"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#id/imageView"
app:layout_constraintTop_toTopOf="parent"
android:textAppearance="#style/MyTextAppearanceMedium"
app:fontFamily="#font/roboto_slab_regular"
app:layout_constraintVertical_chainStyle="packed"
tools:text="#string/enter_title_colon" />
<TextView
android:id="#+id/textViewDescription"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/activity_horizontal_margin"
android:layout_marginLeft="#dimen/activity_horizontal_margin"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#id/imageView"
app:layout_constraintTop_toBottomOf="#id/textViewTitle"
app:fontFamily="#font/roboto_slab_light"
android:textAppearance="#style/MyTextAppearanceSmall"
tools:text="Description" />
</androidx.constraintlayout.widget.ConstraintLayout>
It sounds like you could solve this problem by using a packed chain anchored to the top and bottom of the ImageView. You'll also need to use horizontal bias and a constrained width in order to get wrapping to work correctly.
<?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">
<View
android:id="#+id/anchor"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_margin="64dp"
android:background="#caf"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="#+id/one"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintHorizontal_bias="0"
app:layout_constrainedWidth="true"
app:layout_constraintTop_toTopOf="#id/anchor"
app:layout_constraintStart_toEndOf="#id/anchor"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="#id/two"/>
<TextView
android:id="#+id/two"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintHorizontal_bias="0"
app:layout_constrainedWidth="true"
app:layout_constraintTop_toBottomOf="#id/one"
app:layout_constraintStart_toEndOf="#id/anchor"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="#id/anchor"/>
</androidx.constraintlayout.widget.ConstraintLayout>
The important attributes here are:
app:layout_constraintVertical_chainStyle="packed" on the first view, which causes the two textviews to stack right on top of each other
app:layout_constraintHorizontal_bias="0" on both views, which means that when the text is not long enough to reach the edge of the screen it will stick to the edge of the anchor view
app:layout_constrainedWidth="true" on both views, which prevents the textview from ever being wider than its constraints, and so the text wraps to a new line
If you want to use ConstraintLayout you could use something like this:
<?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">
<ImageView
android:id="#+id/imageView"
android:layout_width="200dp"
android:layout_height="200dp"
android:contentDescription="description"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="#+id/linearLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="#+id/imageView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/imageView"
app:layout_constraintTop_toTopOf="#+id/imageView">
<TextView
android:id="#+id/textViewTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="Title" />
<TextView
android:id="#+id/textViewDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="Description" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
You can use this layout:
<?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">
<TextView
android:id="#+id/textView2"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#color/colorAccent"
android:text="I am 5% of the screen height"
app:layout_constraintBottom_toTopOf="#+id/textView3"
app:layout_constraintEnd_toEndOf="#+id/textView3"
app:layout_constraintHeight_percent="0.05"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="#+id/textView3"
app:layout_constraintTop_toTopOf="#+id/imageView2" />
<TextView
android:id="#+id/textView3"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintHeight_percent="0.15"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:background="#color/colorPrimary"
android:text="I am 15% of the screen height (And the image is 20% screen size in height) "
app:layout_constraintBottom_toBottomOf="#+id/imageView2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/imageView2"
app:layout_constraintTop_toBottomOf="#+id/textView2" />
<ImageView
android:id="#+id/imageView2"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintHeight_percent="0.20"
app:layout_constraintDimensionRatio="1:1"
android:scaleType="fitXY"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/rose" />
</android.support.constraint.ConstraintLayout>
It will look like this:
One important thing about this layout:
You can control your aspect ratio (for the image) with app:layout_constraintDimensionRatio="x:y" and by passing "1:1" make it square
And by the way - I am using support library for no reason on this example, you can use androidx
I have ConstraintLayout with two TextView inside:
<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="wrap_content">
<TextView
android:id="#+id/first"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/second"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toRightOf="#+id/first"
app:layout_constraintRight_toRightOf="parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</androidx.constraintlayout.widget.ConstraintLayout>
I want to place the first one at the top left-corner of parent container and to place the second one at the top-right parent's corner. But both textviews can have text with any length. And when first one has very long text it overlaps the second textview. I can solve this problem by wrapping both textviews in LinearLayout. But this way seems to me inelegant. Maybe there is another way to do it? I mean with ConstraintLayout's feautures
First of all, remove the wrap_content from your widths as this makes the left/right constraints irrelevant. Use 0dp in its place to make it adhere to the constraints' rules.
Then create a horizontal chain between the views and have it as spread, so that the views don’t overlap at any point and they also stay at the edges of the screen at all times.
Finally, align the texts accordingly so that the left one aligns to the start of the view and the right one aligns to the end. In the example below you see that no matter how long the text is, the views don’t overlap and stick to their sides.
NOTE: It is considered better practice to use start/end constraints (as in my code) instead of left/right in order to cater for devices with different text direction. You can also modify the code below by adding the appropriate margins to have the text farther away from the edges of the screen.
<?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">
<TextView
android:id="#+id/first"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAlignment="viewStart"
app:layout_constraintEnd_toStartOf="#+id/second"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="#tools:sample/lorem/random" />
<TextView
android:id="#+id/second"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAlignment="viewEnd"
app:layout_constrainedWidth="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toEndOf="#id/first"
app:layout_constraintTop_toTopOf="parent"
tools:text="#tools:sample/lorem/random" />
</androidx.constraintlayout.widget.ConstraintLayout>
use this
<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="wrap_content">
<TextView
android:id="#+id/first"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="chauhan"/>
<TextView
android:id="#+id/second"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="mehul"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<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="wrap_content">
<TextView
android:id="#+id/first"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/second"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toRightOf="#+id/first"
app:layout_constraintRight_toRightOf="parent"
android:gravity="right"
android:layout_width="0dp"
android:layout_height="wrap_content" />
</androidx.constraintlayout.widget.ConstraintLayout>
This way second TextView won't overlap first one for sure, but there might happen that first is too long so that there is no place for second one. If that could be the case I would recommend setting maxWidth to first TextView, so that in worst case scenario there is some space left for second TextView.
Also I added graviti to second one to keep text on right side of TextView.
<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="wrap_content">
<TextView
android:id="#+id/first"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_width="0dp"
app:layout_constraintRight_toLeftOf="#+id/second"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/second"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toRightOf="#+id/first"
app:layout_constraintRight_toRightOf="parent"
android:layout_width="0dp"
android:layout_height="wrap_content" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tvFirst"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toStartOf="#+id/tvSecond"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tvSecond"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/tvFirst"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
I'm trying to build an intro to my AndroidX app with a ViewPager and Fragments (similar to this Intro Slider Tutorial) and have run into a layout problem.
I've got a Fragment in which I want the contents to be vertically centered. I've added a ScrollView as the root element of the Fragment in case the contents are too long to fit the screen.
If the contents aren't too long they are centered perfectly and nothing is cut off:
centering working
If they are too long though, the top of the content gets cut off and some extra space is added to the bottom (seems to be exactly the height of what was cut off the top):
overlong content, title cut off
overlong content, additional space at the bottom
Fragment layout
<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorPrimary"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:scrollbars="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/web_view_fragment_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical">
<TextView
android:id="#+id/web_view_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/colorPrimaryDark"
android:lineSpacingMultiplier="1.2"
android:text="Title"
android:textAlignment="center"
android:textColor="#android:color/white"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<WebView
android:id="#+id/web_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/web_view_title" />
<TextView
android:id="#+id/web_view_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/design_default_color_on_secondary"
android:lineSpacingMultiplier="1.2"
android:padding="0dp"
android:text="Description"
android:textAlignment="center"
android:textColor="#android:color/white"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/web_view" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
Please note:
Android Studio displays a warning for the ScrollView:
ScrollView size validation
This androidx.constraintlayout.widget.ConstraintLayout should use android:layout_height="wrap_content"
ScrollView children must set their layout_width or layout_height attributes to wrap_content rather than fill_parent or match_parent in the scrolling dimension
Issue id: ScrollViewSize
However, changing the layout_height of the ScrollView to wrap_content doesn't fix the problem and an error is shown instead of the warning:
WebViews in wrap_content parents
Placing a in a parent element that uses a wrap_content layout_height can lead to subtle bugs; use match_parent instead
The WebView implementation has certain performance optimizations which will not work correctly if the parent view is using wrap_content rather than match_parent. This can lead to subtle UI bugs.
Issue id: WebViewLayout
Activity layout with ViewPager
<?xml version="1.0" encoding="utf-8"?>
<!-- intro_activity -->
<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:id="#+id/intro_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/blueGray700">
<androidx.viewpager.widget.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#id/divider"
app:layout_constraintEnd_toStartOf="#id/divider"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="spread_inside" />
<View
android:id="#+id/divider"
android:layout_width="0dp"
android:layout_height="1dp"
android:background="?android:attr/listDivider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/intro_tab_layout" />
<com.google.android.material.tabs.TabLayout
android:id="#+id/intro_tab_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/intro_continue_button"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="#+id/intro_skip_button"
app:layout_constraintTop_toBottomOf="#+id/pager"
app:tabBackground="#drawable/tab_selector"
app:tabGravity="center"
app:tabIndicatorHeight="0dp"
app:tabMaxWidth="16dp" />
<Button
android:id="#+id/intro_skip_button"
style="#style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="#string/intro_skip_button"
android:textColor="#android:color/white"
android:onClick="finishTutorial"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/pager" />
<Button
android:id="#+id/intro_continue_button"
style="#style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="nextFragment"
android:padding="5dp"
android:text="#string/intro_continue_button"
android:textColor="#android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="#+id/pager"
app:layout_constraintTop_toBottomOf="#+id/pager" />
</androidx.constraintlayout.widget.ConstraintLayout>
Clearly there's something wrong with my layouts but I can't figure out what the problem is. Any help would be greatly appreciated!
What I have found out so far:
The padding of the TextViews is not the problem.
If I remove android:layout_gravity="center_vertical" from the ConstraintLayout nothing is cut off. But then my contents are obviously not centered (which I want for aesthetical reasons).
How to make View FrameLayout fill all remaining space in ConstraintLayout
I have xml like:
<ImageView
android:background="#FF0000"
android:id="#+id/header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_header"
android:scaleType="fitXY"
android:adjustViewBounds="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_default="wrap"
app:layout_constraintWidth_default="spread"/>
<FrameLayout
android:background="#00FF00"
app:layout_constraintHeight_default="spread"
app:layout_constraintWidth_default="wrap"
app:layout_constraintTop_toBottomOf="#id/header"
app:layout_constrainedHeight="true"
app:layout_constraintBottom_toTopOf="#id/footer"
android:id="#+id/task_fragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<LinearLayout
android:background="#FFFF00"
android:orientation="vertical"
android:id="#+id/footer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_default="spread"
app:layout_constraintWidth_default="spread"
app:layout_constraintBottom_toBottomOf="parent">
<ImageButton
style="?borderlessButtonStyle"
android:id="#+id/btn_ar"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:padding="0dp"
android:scaleType="fitXY"
android:adjustViewBounds="true"
android:src="#drawable/ic_ar"/>
</LinearLayout>
There are header, footer and FrameLayout in the middle I want to make FrameLayout to fill all remaining space.
I don't understand, which properties I have to use to expand FrameLayout. Please help me!
EDIT: Or any solution with different layout.
This is really simple. To make any view fill up the available space either horizontally or vertically, just set the layout_width/layout_height to 0dp according to your need.
This work, go with ConstraintLayout because has better performance.
<?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">
<ImageView
android:background="#FF0000"
android:id="#+id/header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_header"
android:minHeight="30dp"
android:scaleType="fitXY"
android:adjustViewBounds="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_default="wrap"
app:layout_constraintWidth_default="spread"/>
<FrameLayout
android:id="#+id/task_fragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#00FF00"
android:orientation="vertical"
app:layout_constrainedHeight="true"
app:layout_constraintBottom_toTopOf="#+id/btn_ar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_default="spread"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/header"
app:layout_constraintWidth_default="wrap">
</FrameLayout>
<ImageButton
android:id="#+id/btn_ar"
style="?borderlessButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:padding="0dp"
android:scaleType="fitXY"
android:src="#drawable/ic_ar"
app:layout_constraintBottom_toBottomOf="parent" />
</android.support.constraint.ConstraintLayout>
<FrameLayout
android:id="#+id/task_fragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#00FF00"
android:orientation="vertical"
app:layout_constrainedHeight="true"
app:layout_constraintBottom_toTopOf="#+id/btn_ar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_default="spread"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/header"
app:layout_constraintWidth_default="wrap">
</FrameLayout>
This should work.
Explanation:
All you want is to make the top of this FrameLayout constraint to the bottom of the header, and the bottom of this FrameLayout constraint to the top of the footer.
After setting all the constraints, you still need to set the layout param of FrameLayout to match the constraint. In order to do this, you can try to set layout_height to 0dp. This will make the height of FrameLayout have the effect of match_constraint.
For more detail, you can check this doc: https://developer.android.com/reference/android/support/constraint/ConstraintLayout (just search the keyword match constraint and you will find that block), or simply check this similar answer: Set width to match constraints in ConstraintLayout
I am trying to create a layout using constraint layout, with an ImageView on top, a button in the ImageView, a TextView below it and then another TextView below it. Following is the xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
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"
android:layout_margin="10dp"
app:cardElevation="4dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/white">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/news_image1"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="16:9"
android:scaleType="centerCrop"
android:adjustViewBounds="true"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="0dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toTopOf="#+id/news_title1"/>
<ImageButton
android:id="#+id/news_share_button_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:src="#drawable/share_button_big"
app:layout_constraintBottom_toBottomOf="#+id/news_image1"
app:layout_constraintRight_toRightOf="#+id/news_image1"
android:layout_marginRight="15dp"
android:layout_marginEnd="15dp"/>
<TextView
android:id="#+id/news_title1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:gravity="center_vertical|start"
android:layout_alignParentBottom="true"
android:lines="3"
android:ellipsize="end"
app:layout_constraintTop_toBottomOf="#+id/news_image1"
app:layout_constraintBottom_toTopOf="#+id/news_read_more1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:textColor="#color/colorPrimaryText"
android:layout_margin="#dimen/news_cell0_textview_margin"
android:textSize="12sp"
android:typeface="monospace" />
<TextView
android:id="#+id/news_read_more1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:gravity="center_vertical|start"
android:layout_alignParentBottom="true"
android:lines="1"
android:ellipsize="end"
app:layout_constraintTop_toBottomOf="#+id/news_title1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:textColor="#color/read_more_text_color"
android:text="#string/news_read_more"
android:layout_margin="#dimen/news_cell0_textview_margin"
android:textSize="10sp" />
</android.support.constraint.ConstraintLayout>
</RelativeLayout>
</android.support.v7.widget.CardView>
Everything is correct except for a small margin on top of the first ImageView. Whatever I do I cannot get rid of that margin. See the image below.
Please note the margin between the top of the ImageView and the card, that is what is troubling me.
Your news_image, news_title1, and news_read_more1 views form a chain. Apparently, though I cannot find documentation on this, all views in a vertical chain will share vertical margins. In other words, applying a layout_marginTop or layout_marginBottom attribute to one of those three views will wind up applying it to all three of them.
You will have to re-allocate your margins, keeping this in mind.
Edit
Looks like the behavior is a little more sophisticated than I thought. First of all, it looks like it only applies to views with the spread "chain style" (which is the default). Second, it looks like top margin is applied to the view it is specified on as well as all views above that one in the chain, while bottom margin is applied to the view it is specified on as well as all views below that one in the chain. Finally, it appears that margins are cumulative (so if you had 10dp top margin on your bottom view and 20dp top margin on your middle view, the final result would be 30dp on your top view, 30dp on your middle view, and 10dp on your bottom view).
As for how to solve this problem in your specific case? You should be able to use left/right margins without issue. And then probably you should use bottom margin on your top view to space the three views out.
Edit #2
Muthukrishnan's answer made me realize that you could solve this problem by simply removing the chain from your views. If you delete the app:layout_constraintBottom_toTopOf="#+id/news_title1" constraint from your ImageView, that will break the chain and now the vertical margins aren't shared.
Thanks to the great starter by Ben P, I managed to figure out a solution. There are three(plus one weighed) chaining styles available in ConstraintLayout. According to this great tutorial the chaining styles are explained as:
Spread: The views are evenly distributed. E.g.
app:layout_constraintHorizontal_chainStyle=”spread”
app:layout_constraintVertical_chainStyle=”spread”
Spread inside: The first and last view are affixed to the constraints on each end of the chain and the rest are evenly distributed. E.g.
app:layout_constraintHorizontal_chainStyle=”spread_inside”
app:layout_constraintVertical_chainStyle=”spread_inside”
Packed: The views are packed together (after margins are accounted for). You can then adjust the whole chain’s bias (left/right or up/down) by changing the chain’s head view bias. E.g.
app:layout_constraintHorizontal_chainStyle=”packed”
app:layout_constraintVertical_chainStyle=”packed”
Weighted: When the chain is set to either spread or spread inside, you can fill the remaining space by setting one or more views to “match constraints” (0dp). By default, the space is evenly distributed between each view that's set to "match constraints," but you can assign a weight of importance to each view using thelayout_constraintHorizontal_weight and layout_constraintVertical_weight attributes. If you're familiar with layout_weight in a linear layout, this works the same way. So the view with the highest weight value gets the most amount of space; views that have the same weight get the same amount of space.
spread is the default chaining style. I changed it to spread_inside so that the first and last views are affixed to the constraints on each end of the chain(hence obeying the margins provided). The xml now looks like:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
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"
android:layout_margin="10dp"
app:cardElevation="4dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/white">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/news_image1"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="16:9"
android:scaleType="centerCrop"
android:adjustViewBounds="true"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="0dp"
app:layout_constraintVertical_chainStyle="spread_inside"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toTopOf="#+id/news_title1"/>
<ImageButton
android:id="#+id/news_share_button_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:src="#drawable/share_button_big"
app:layout_constraintBottom_toBottomOf="#+id/news_image1"
app:layout_constraintRight_toRightOf="#+id/news_image1"
android:layout_marginRight="15dp"
android:layout_marginEnd="15dp"/>
<TextView
android:id="#+id/news_title1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:gravity="center_vertical|start"
android:layout_alignParentBottom="true"
android:lines="3"
android:ellipsize="end"
app:layout_constraintVertical_chainStyle="spread_inside"
app:layout_constraintTop_toBottomOf="#+id/news_image1"
app:layout_constraintBottom_toTopOf="#+id/news_read_more1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:textColor="#color/colorPrimaryText"
android:layout_margin="#dimen/news_cell0_textview_margin"
android:textSize="12sp"
android:typeface="monospace" />
<TextView
android:id="#+id/news_read_more1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:gravity="center_vertical|start"
android:layout_alignParentBottom="true"
android:lines="1"
android:ellipsize="end"
app:layout_constraintVertical_chainStyle="spread_inside"
app:layout_constraintTop_toBottomOf="#+id/news_title1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:textColor="#color/read_more_text_color"
android:text="#string/news_read_more"
android:layout_margin="#dimen/news_cell0_textview_margin"
android:textSize="10sp" />
</android.support.constraint.ConstraintLayout>
</RelativeLayout>
</android.support.v7.widget.CardView>
Try this, I remove app:layout_constraintTop_toTopOf="parent" in your layout it works
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
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"
android:layout_margin="10dp"
app:cardElevation="4dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/news_image1"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="16:9"
android:scaleType="centerCrop"
android:adjustViewBounds="true"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"/>
<ImageButton
android:id="#+id/news_share_button_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:src="#drawable/ic_menu_share"
app:layout_constraintBottom_toBottomOf="#+id/news_image1"
app:layout_constraintRight_toRightOf="#+id/news_image1"
android:layout_marginRight="15dp"
android:layout_marginEnd="15dp"/>
<AliasTextView
android:id="#+id/news_title1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:gravity="center_vertical|start"
android:layout_alignParentBottom="true"
android:lines="3"
android:ellipsize="end"
app:layout_constraintTop_toBottomOf="#+id/news_image1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:textColor="#color/colorPrimaryText"
android:layout_margin="#dimen/news_cell0_textview_margin"
android:textSize="12sp"
android:typeface="monospace" />
<TextView
android:id="#+id/news_read_more1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:gravity="center_vertical|start"
android:layout_alignParentBottom="true"
android:lines="1"
android:ellipsize="end"
app:layout_constraintTop_toBottomOf="#+id/news_title1"
app:layout_constraintLeft_toLeftOf="parent"
android:textColor="#color/read_more_text_color"
android:text="#string/news_read_more"
android:layout_margin="#dimen/news_cell0_textview_margin"
android:textSize="10sp" />
</android.support.constraint.ConstraintLayout>
</RelativeLayout>
</android.support.v7.widget.CardView>