How to make buttons change position according to the screen size and/or font size in Android
Asked Answered
E

2

1

I have this simple layout with 3 buttons that I need to implement (which I already did using ConstraintLayout): enter image description here

But when I have a small screen size or/and the user set the font to max, the texts inside the two large buttons are cut off. To solve this problem, the design team want us to have this layout when these cases happens:

enter image description here

In order to implement these two scenarios, I've tried to use the Flow component from the constraint layout but didn't work for me.

What do you suggest to solve this problem in the same XML file?

Extricate answered 27/4, 2023 at 15:32 Comment(1)
It would be easier in your case to do it in java/kotlin by dynamically creating those buttons, or create another xml file of the same name with qualifiers (such as Screen Width, Height, Ratio, Dimension etc.). I don't know if you can do it in the same file.Absent
V
2

The widgets that can shrink/expand would be added into ConstraintLayout Flow to adjust their right placement "in the same row/line if they can fit to a single line, or to flow to next row(s)/line(s) if they are expanded enough".

In your layout you'd add the two large buttons into the flow, and constraint these buttons with the head widget to be centered vertically.

Here is a demo:

<?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"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/head_view"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_marginStart="8dp"
        android:src="@drawable/ic_android_black_24dp"
        app:layout_constraintBottom_toBottomOf="@+id/view2"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/view1" />

    <androidx.constraintlayout.helper.widget.Flow
        android:id="@+id/flow"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        app:constraint_referenced_ids="view1,view2"
        app:flow_horizontalBias="0"
        app:flow_horizontalGap="10dp"
        app:flow_horizontalStyle="packed"
        app:flow_maxElementsWrap="2"
        app:flow_verticalGap="10dp"
        app:flow_wrapMode="chain"
        app:layout_constraintBottom_toBottomOf="@+id/head_view"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/head_view"
        app:layout_constraintTop_toTopOf="@+id/head_view" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/view1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#6C08FB"
        android:padding="12dp"
        android:text="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        android:textColor="@android:color/black"
        app:layout_constrainedWidth="true" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/view2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#0BDAC6"
        android:padding="12dp"
        android:text="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
        app:layout_constrainedWidth="true" />

</androidx.constraintlayout.widget.ConstraintLayout>

Preview:


Edit

In my case, if they don't fit in the same line, i must break the line in two and make them fit the entire width available ( i see that you put a wrap_content as width). I always need to fit the entire width of the screen also!

In case of 2 lines, if the widths of view1 & view2 are constrained with 0dp to fill the parent width, this will never make them expand to the next line; they will stick to width constraints and will expand towards the bottom side by side which you didn't need as well.

This is because currently these views expand/shrink based on their content. and that will make the flow lines actually work. If their widths is 0dp they won't expand but instead will stick to the constraints regardless of the size of the their content.

What you want to do can be achieved programmatically with a workaround by having a conditional minimum width for view1 & view2 whenever there are two lines.

To decide if the contents requires two lines, we can sum up the occupied view & margin widths and compare that to the Flow width:

Flow flow = findViewById(R.id.flow);
TextView view1 = findViewById(R.id.view1);
TextView view2 = findViewById(R.id.view2);

flow.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
    
        flow.getViewTreeObserver().removeOnGlobalLayoutListener(this);
        
        ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) flow.getLayoutParams();
        int leftMargin = params.leftMargin;
        
        int flowHorizontalGap = (int) convertDpToPixel(10f, MainActivity.this);
        int occupiedSpace = leftMargin + view1.getWidth() + flowHorizontalGap + view2.getWidth();
        
        // Checking if the content need to expand to two lines
        if (occupiedSpace > flow.getWidth()) {
            int width = flow.getWidth() ;
            view1.setMinWidth(width);
            view2.setMinWidth(width);
        }

    }
});


public static float convertDpToPixel(float dp, Context context) {
        return dp * ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT);
}
Valerlan answered 3/5, 2023 at 23:58 Comment(4)
Thanks for the answer! In my case, if they don't fit in the same line, i must break the line in two and make them fit the entire width available ( i see that you put a wrap_content as width). I always need to fit the entire width of the screen also!Extricate
Hey @RodrigoGontijo, if the widths of view1 & view2 are constrained with 0dp to fill the parent width, this will never make them expand to the next line which you didn't need as well; because these views expand/shrink based on their content. If their widths is 0dp they won't expand but instead will stick to the constraints regardless of the size of the their content. What you want to do can be achieved programmatically with a workaround, if you have no problem with that, I would update the answer accordingly.Valerlan
I tried to get the count of lines that the Flow component is showing, but i couldn't find a workaround for it. This way i can change the width of the elements to 0dp programatically. Do you know if this is possible ?Extricate
@RodrigoGontijo Just edited the answer with an approach to get the number of lines, please have a look at the Edit sectionValerlan
O
1

I am not sure that you can do it in the same file, because there will be other constraints.

Therefore the most appropriate approach will be to create separate file in a folder for some qualifier (for example, in layout/sw360dp directory or other). Screen width/height or dimension are appropriate. And define there the layout with constraints for this qualifier.

Osbert answered 27/4, 2023 at 16:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.