I have an image in my ConstraintLayout that I want to resize according to different screen sizes.
It is fine with large screens, but not with small ones.
Here is my image view
<ImageView
android:id="#+id/iv_app_image_start_screen_fragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:src="#drawable/my_image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
Try with the following property of Imageview
android:scaleType="fitXY"
They key to do that is to use percentages instead of defined widths or height. So that as per different screen sizes, it can adjust.
The property for that is app:layout_constraintDimensionRatio, so you will have to modify your current layout as follows
<ImageView
android:id="#+id/iv_app_image_start_screen_fragment"
android:layout_width="0dp"
android:layout_height="0dp"
android:scaleType="centerCrop"
android:layout_marginTop="32dp"
app:layout_constraintDimensionRatio="1"
android:src="#drawable/my_image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
Note we have made both width and height as 0dp and as you can see the dimension ratio is 1 which mean for now width is equal to height.
You can adjust the dimension ratio as per your need and can read more about it here.
Related
I have a CardView inside a ConstraintLayout, and I want the CardView to have a width of 70%, a min width of 300dp, and a max of 450dp.
The max works fine, if 70% is more than 450dp, the CardView width is set to 450dp.
The min width however, behaves very strangely.
When 70% is less than 300dp, instead of making the CardView 300dp wide, it makes it as wide as the available space, so 100%.
Here is the CardView inside the layout:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.card.MaterialCardView
android:id="#+id/balanceCard"
android:layout_width="0dp"
android:layout_height="0dp"
app:cardCornerRadius="40dp"
app:cardElevation="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_min="300dp"
app:layout_constraintWidth_percent="0.7"/>
<androidx.constraintlayout.widget.ConstraintLayout/>
This is the layout with min width of 280dp
And this is the layout with min width of 300dp
I don't know what is happening, and I would appreciate any help.
This looks like a bug to me. The width of the CardView is incorrect when the minimum width exceeds 70% of the layout's width. For a Nexus 6 emulator (dp==3.5px), the ConstraintLayout width would be 1440px and 70% of that is 1008px. (I am using px instead of dp because I think that it makes what's happening clearer.)
When the minimum width is set to 288dp (1008px), everything looks good.
<View
android:id="#+id/balanceCard"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#android:color/holo_blue_light"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_min="288dp"
app:layout_constraintWidth_percent="0.7" />
I am using a straight View instead of a CardView for simplicity.
However, if we set the minimum width to 289dp (1011.5px), we see the problem.
Notice that 288dp (1008px) is equal to 70% of the layout's width (1008px) while 289dp (1011.5px) is greater than 70% of the layout's width.
It is interesting that, if we keep the minimum width at 289dp but remove the end constraint, the width is correct:
<View
android:id="#+id/balanceCard"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#android:color/holo_blue_light"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_min="289dp"
app:layout_constraintWidth_percent="0.7" />
I will note that I am using version 2.1.4 of ConstraintLayout.
The only work-around that I can see One work-around is, upon layout, check the width of the view and if it exceeds 70% of the layout's width, set the width to the minimum width that you want.
Another work-around that is more complex but involves just XML is to split the 70% behavior and the minimum width into two separate Space views. You can then set a barrier to the start and end of these two views and constrain the card view to the barriers. Remove the minimum width and 70% width attributes from the card view. This looks like this:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Space
android:id="#+id/spacePct"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.7" />
<Space
android:id="#+id/spaceMinwidth"
android:layout_width="289dp"
android:layout_height="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<androidx.constraintlayout.widget.Barrier
android:id="#+id/barrierStart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="start"
app:constraint_referenced_ids="spacePct,spaceMinwidth"/>
<androidx.constraintlayout.widget.Barrier
android:id="#+id/barrierEnd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="end"
app:constraint_referenced_ids="spacePct,spaceMinwidth"/>
<View
android:id="#+id/balanceCard"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#android:color/holo_blue_light"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="#id/barrierEnd"
app:layout_constraintStart_toStartOf="#id/barrierStart"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
If you agree with my assessment, you may want to consider submitting a bug report on the Issue Tracker, but that is totally up to you.
This is a question specifically for ConstraintLayout -
We can use margins as an attribute or layout_constraintWidth_percent as an attribute while working with width for an UI element.
Example - I have a button in centre of my UI which have some empty space to its left and right. Say something like this -
Approach1- uses "marginLeft" and "marginRight"
<Button
android:id="#+id/button"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_marginLeft="24dp"
android:layout_marginRight="24dp"
android:text="#string/button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
or
Approach2- uses "app:layout_constraintWidth_percent"
<Button
android:id="#+id/button"
android:layout_width="0dp"
android:layout_height="40dp"
app:layout_constraintWidth_percent="0.8"
android:text="#string/button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Which of the following ways will be a more efficient way to render the UI element?
For efficiency of the two methods, there won't be any difference, at least that you will be able to measure, between the two (IMO), but the two methods are not the same.
Settting
android:layout_marginLeft="24dp"
android:layout_marginRight="24dp"
will place margins of 24dp to the right and left of the ConstraintLayout regardless of the ConstraintLayout's width. The view's width will take up the remainder of the width which will vary according to the width of the ConstraintLayout.
If you set
app:layout_constraintWidth_percent="0.8"
then the view's width will be 80% of the ConstraintLayout's width and each margin (left and right) will effectively be 10% of the ConstraintLayout's width (1/2 of 20%). So, the width of the view and it's margins will vary based upon the width of the ConstraintLayout.
If it really doesn't matter to your design then just pick one. But since there is a difference in how the two layout, I would look at the layout on the smallest screen you support and the largest and include any other alternate layout for landscape/portrait, etc. You may find that there is a good reason to prefer one over the other.
In case you have more complex structure of the ConstraintLayout layout_constraintWidth_percent will continue measure percents from the ConstraintLayout's width, not from the width it occupies.
For instance, I want to draw this picture:
<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">
<View
android:id="#+id/left"
android:layout_width="50dp"
android:layout_height="20dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="#+id/top"
android:layout_width="0dp"
android:layout_height="20dp"
android:layout_marginStart="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#id/left"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="#+id/bottom"
android:layout_width="0dp"
android:layout_height="20dp"
android:layout_marginTop="10dp"
app:layout_constraintStart_toStartOf="#id/top"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#id/top"
app:layout_constraintWidth_percent="0.8" />
</androidx.constraintlayout.widget.ConstraintLayout>
Then you will get bottom rectangle wider, than upper.
I'm trying to make the background a video but to get the right aspect I need the height to match_parent and then the width to scale relative to the height. Is there any way of doing this?
The only way I have done it so far is to set the width manually, but this means it will look squashed or stretched on some devices as the height changes.
<VideoView
android:id="#+id/vv_menu_card_animation"
android:layout_width="1280dp"
android:layout_height="match_parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent" />
In ConstrainLayout, you can also define one dimension of a widget as a ratio of the other one by layout_constraintDimensionRatio. You can read more here: ConstrainLayout: Ratio.
If you want width = height, you can add this line to your VideoView:
app:layout_constraintDimensionRatio="1:1"
android:layout_width="0dp"
<VideoView
android:id="#+id/vv_menu_card_animation"
android:layout_width="0dp"
android:layout_height="match_parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintDimensionRatio="1:1"
/>
I'm trying to display an image centered in parent with a dimension ratio of 1220:1000 AND a maximum height of 300dp (to keep the image small even with large screen)
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.facebook.drawee.view.SimpleDraweeView
android:id="#+id/image"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="10dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:adjustViewBounds="true"
android:scaleType="centerInside"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1220:1000"
app:layout_constraintHeight_max="200dp" ==> This line break the ratio (the image is not displayed)
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
The app:layout_constraintHeight_max property break the ratio.
Is there is a way to do it?
If you want to enforce max_height and keep the dimension ratio at the same time, you need to constrain the width based on height. You can achieve that by adding W to your ratio:
app:layout_constraintDimensionRatio="W,1220:1000"
This will constrain the height first and then set the width accordingly to satisfy the ratio.
More info on how this dimension ratio works can be found in the documentation.
If you want to maximize the area with a dimension ratio and a max height, but you don't know if width or height is going to be adjusted, better don't to use layout_constraintDimensionRatio + layout_constraintHeight_max. They don't work well together.
Here I put my solution in the case you have an image 16:9 which will use full width while max height is not yet reached, otherwise it will respect max height, and width will be adjusted:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- This space view makes ImageView expands to its corresponding height -->
<Space
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="#+id/imageview" />
<com.a3.sgt.ui.widget.AspectRatioImageView
android:id="#+id/imageview"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:adjustViewBounds="true"
app:dominantMeasurement="height"
app:aspectRatioEnabled="true"
app:aspectRatio="1.778"
android:scaleType="centerCrop"
app:layout_constraintHeight_max="400dp"
tools:src="#drawable/placeholder_glacier"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
I used a custom ImageView I had in my gist which adjusts to aspect ratio, in order to avoid using layout_constraintDimensionRatio which causes the problem together with layout_constraintHeight_max
Have you tried to add the tag maxHeight?
android:maxHeight="200dp"
I have the following layout:
As you can see, the image is constraint and has the scale type fitXY. I've customized his size using percentage as you can see on the following code:
<ImageView
app:layout_constraintHeight_percent="0.3" app:layout_constraintWidth_percent="0.3"
android:layout_width="0dp"
android:layout_height="0dp" tools:src="#tools:sample/avatars"
android:id="#+id/book_image"
app:srcCompat="#mipmap/ic_launcher_round" app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="16dp" app:layout_constraintStart_toStartOf="parent"
android:layout_marginLeft="16dp"
android:contentDescription="#string/text_view_default" android:scaleType="fitXY"/>
But this is the result that I obtain when executing:
Why is changing the size?
EDIT:
I've seen that in some books the text of the description exceeds the limit of the screen, so you can scroll and I understand that the percentage I'm setting is for the whole view, not just the visible piece.
How can I fix it?
That's happening because you are setting your layout_height and layout_width properties to be 0dp. Doing this will tell the view that it should stretch as much as it can. You should be using wrap_content or even a fixed size if all of your images should look the same (dimensions).