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>
Related
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" />
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 have an imageview that I will place different images.
This images can have many sizes like 200x200 200x400 400x200...
Above imageview I have an adview and below this imageview I need 4 buttons.
The problem is, I'd like the image be below adview, but stop jumping position when ad loads. It goes down when an ad loads, I'd to stop this move, make the image fix, but above the adview.
My xml:
<?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"
tools:context=".MainActivity">
<LinearLayout
android:id="#+id/ad_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="49dp"
android:orientation="horizontal"
app:layout_constraintBottom_toTopOf="#+id/imageView1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.gms.ads.AdView xmlns:ads="http://schemas.android.com/apk/res-auto"
android:id="#+id/adView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
ads:adSize="SMART_BANNER"
ads:adUnitId="ca-app-pub-xxx/xxx">
</com.google.android.gms.ads.AdView>
</LinearLayout>
<ImageView
android:id="#+id/imageView1"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:layout_marginTop="89dp"
android:contentDescription="img"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/ad_view" />
<TextView
android:id="#+id/a1"
android:layout_width="127dp"
android:layout_height="50dp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.207"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.447" />
<TextView
android:id="#+id/a2"
android:layout_width="127dp"
android:layout_height="50dp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.806"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.447" />
<TextView
android:id="#+id/a3"
android:layout_width="127dp"
android:layout_height="50dp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.207"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.582" />
<TextView
android:id="#+id/a4"
android:layout_width="127dp"
android:layout_height="50dp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.806"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.582" />
</androidx.constraintlayout.widget.ConstraintLayout>
TL;DR
The fastest solution would be to change your ad view android:layout_height="wrap_content" into android:layout_height="fixed size" so your ad view will have a single height and won't change because of its wrap_content attribute (If your ad view is not loaded you can think of wrap_content as height of 0 and only after its loaded it will have height, thus the layout jump).
Why is it bad?
your layout won't be responsive.
How to fix?
Read the answer below.
Long but better answer:
You can use guidelines with app:layout_constraintGuide_percent=".2" to tell your guideline to be at the top of your screen (80%) and constraint your ad view to your guideline - this way your layout will won't jump (it will have its space to load into).
Another thing, different phones got different screen size, in your layout you are using fixed size on your view (fixed size is 50dp for example) and the result is that what may look good on one screen (your android studio preview screen) will not look good on another screen (your actual phone).
Here is an example to achieve your wanted look using ConstaintLayout:
<?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"
tools:context=".MainActivity">
<Button
android:id="#+id/adView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintHeight_percent="0.08"
android:text="add view"
app:layout_constraintBottom_toTopOf="#+id/guideline"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="#+id/imageView1"
android:layout_width="0dp"
android:layout_height="0dp"
android:src="#drawable/ic_launcher_background"
android:scaleType="fitXY"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/guideline"
app:layout_constraintWidth_percent="0.6" />
<TextView
android:id="#+id/a1"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center"
android:text="a1"
app:layout_constraintHeight_percent="0.08"
app:layout_constraintWidth_percent="0.25"
app:layout_constraintBottom_toTopOf="#+id/a3"
app:layout_constraintEnd_toStartOf="#+id/a2"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="#+id/imageView1"
app:layout_constraintTop_toBottomOf="#+id/imageView1" />
<TextView
android:id="#+id/a2"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center"
android:text="a2"
app:layout_constraintHeight_percent="0.08"
app:layout_constraintWidth_percent="0.25"
app:layout_constraintBottom_toBottomOf="#+id/a1"
app:layout_constraintEnd_toEndOf="#+id/imageView1"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="#+id/a1"
app:layout_constraintTop_toTopOf="#+id/a1"
app:layout_constraintVertical_bias="0.0" />
<TextView
android:id="#+id/a3"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center"
android:text="a3"
app:layout_constraintHeight_percent="0.08"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="#+id/a1"
app:layout_constraintStart_toStartOf="#+id/a1"
app:layout_constraintTop_toBottomOf="#+id/a1" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent=".2" />
<TextView
android:id="#+id/a4"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center"
android:text="a4"
app:layout_constraintBottom_toBottomOf="#+id/a3"
app:layout_constraintEnd_toEndOf="#+id/a2"
app:layout_constraintHeight_percent="0.08"
app:layout_constraintStart_toStartOf="#+id/a2"
app:layout_constraintTop_toTopOf="#+id/a3" />
</androidx.constraintlayout.widget.ConstraintLayout>
Here is how it will look (I am adding a screenshot from the layout editor for better understanding ConstaintLayout)
Some extra information:
ConstraintLayout is not meant to have nested view groups (in your case you have LinearLayout inside constraintlayout).
ConstraintLayout allows you to create large and complex layouts with a flat view hierarchy (no nested view groups). It's similar to RelativeLayout in that all views are laid out according to relationships between sibling views and the parent layout, but it's more flexible than RelativeLayout and easier to use with Android Studio's Layout Editor.
You can Use ConstraintLayout with guidelines and Chains to support different screen sizes, you won't have to use fixed sizes on your views and you will be able to use a single layout to support different screen size.
Give the adview or it's surrounding layout a minimum height equal to the smart banner content in your case likely 90px.
It's jumping because with no content loaded your wrap_content height = 0, then it gets a new height of x, so your imageview shifts down x.
I have a simple side bar with 2 buttons vertical chained and i want my side bar to wrap largest button while smaller button expands to match largest one.
Here is a capture where i want "AAA" button to match "VALIDATE" button width
Of course what poped my mind is to use match contraint width for both buttons but when using a wrap_content container this leads to:
my 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:id="#+id/container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:background="?attr/colorSurface"
android:padding="#dimen/screen_padding">
<com.google.android.material.button.MaterialButton
android:id="#+id/redo_button"
style="#style/AppStyle.Button.OutlinedButton.SecondaryVariantStroke"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="#dimen/spacing_normal"
android:text="aaa"
app:layout_constrainedWidth="false"
app:layout_constraintBottom_toTopOf="#+id/validate_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<ImageView
android:layout_width="32dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:background="#drawable/shape_circle_secondary_variant"
android:padding="7dp"
android:src="#drawable/ic_camera"
android:tint="#android:color/white"
app:layout_constraintBottom_toBottomOf="#+id/redo_button"
app:layout_constraintDimensionRatio="h,1:1"
app:layout_constraintEnd_toEndOf="#+id/redo_button"
app:layout_constraintStart_toEndOf="#+id/redo_button"
app:layout_constraintTop_toTopOf="#+id/redo_button"
tools:ignore="ContentDescription" />
<com.google.android.material.button.MaterialButton
android:id="#+id/validate_button"
style="#style/AppStyle.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="#string/global_valid"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/redo_button" />
</androidx.constraintlayout.widget.ConstraintLayout>
If you know which button is going to be wider, then you can make that button wrap_content and constrain the other button to it as follows:
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:background="#android:color/darker_gray"
android:padding="16dp">
<Button
android:id="#+id/redo_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="aaa"
app:layout_constrainedWidth="false"
app:layout_constraintBottom_toTopOf="#+id/validate_button"
app:layout_constraintEnd_toEndOf="#+id/validate_button"
app:layout_constraintStart_toStartOf="#+id/validate_button"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<Button
android:id="#+id/validate_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Validate"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/redo_button" />
</androidx.constraintlayout.widget.ConstraintLayout>
If this solves your problem, stop here.
Things get a little more complicated if you don't know in advance which button will be wider. (Maybe due to language changes.) In that case, you can check the sizes programmatically and explicitly expand the narrower button. You may consider this less than ideal.
As for accomplishing this in XML, I have looked at this type of problem in the past and the problem always reduces to some kind of circular reference issue. (In fact, this is the problem that you are having with match_constraints widgets in a wrap_content container. The widgets are as wide as the container and the container is as wide as the widgets! What?)
If you don't know which button will be wider, you can create an invisible button that has two lines of text that correspond to the labels of your buttons (assuming single line labels.)
Now that there is a dummy button that is the right width, constrain the sides of your buttons to the sides of the dummy button and make their width match_constraints.
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:background="#android:color/darker_gray"
android:padding="16dp">
<Button
android:id="#+id/dummyForSizing"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="aaa\nValidate"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/redo_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="aaa"
app:layout_constrainedWidth="false"
app:layout_constraintBottom_toTopOf="#+id/validate_button"
app:layout_constraintEnd_toEndOf="#+id/dummyForSizing"
app:layout_constraintStart_toStartOf="#+id/dummyForSizing"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<Button
android:id="#+id/validate_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Validate"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="#+id/dummyForSizing"
app:layout_constraintStart_toStartOf="#+id/dummyForSizing"
app:layout_constraintTop_toBottomOf="#+id/redo_button" />
</androidx.constraintlayout.widget.ConstraintLayout>
You would, of course, make the dummy button invisible.
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: