Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android ConstraintLayout - position view to center when there is enough space and move to edges if necessary

This is my case I'm trying to solve:

enter image description here

So, my goal is:

  1. To keep View1 in centre if there is enough space, that means View2 isn't that wide as to overlap with View1.
  2. If View2 is wide enough to make it impossible to keep View1 in centre without overlapping, then in this case move View1 more to the left.

What I have already tried is this:

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/view1"
        android:layout_width="wrap_content"
        android:layout_height="44dp"
        android:layout_marginEnd="8dp"
        android:src="@drawable/ic_img"
        app:layout_constrainedWidth="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/view2"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintWidth_min="wrap" />

    <View
        android:id="@+id/view2"
        android:layout_width="0dp"
        android:background="@color/black"
        android:layout_height="50dp"
        app:layout_constrainedWidth="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintWidth_min="wrap"
        app:layout_constraintWidth_percent="0.43" />

</androidx.constraintlayout.widget.ConstraintLayout>

With these constraints I tell View2 to take at least 43% of parent's width and if that is not enough then just apply wrap_content. And then in View1 just do:

app:layout_constraintEnd_toStartOf="@+id/view2"

The value 43% is just an estimation that would pretty much position View1 to the centre in the 1st case of the picture. But it's not very accurate in some devices.

So would there be a solution that would position View1 exactly in the centre for the 1st scenario, and then move it to the left if necessary?

like image 800
user677767 Avatar asked Jan 25 '26 07:01

user677767


1 Answers

This is not something that can be done with just XML, AFAIK. You can accomplish it with a little code, though.

Change the layout so view2 is constrained to the right of the ConstraintLayout and the right of view1 is constrained to the left of view2.

<androidx.constraintlayout.widget.ConstraintLayout 
    android:id="@+id/layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <View
        android:id="@+id/view1"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:layout_marginEnd="8dp"
        android:background="#2196F3"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/view2"
        app:layout_constraintTop_toTopOf="parent" />

    <View
        android:id="@+id/view2"
        android:layout_width="200dp"
        android:layout_height="50dp"
        android:background="#FF5722"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Now, place the following code in your main activity. (It can go elsewhere, but the main activity is used here for demo purposes.) This code will manipulate the right margin of view1 to place it either in the center (if it doesn't overlap with view2) or pushed to the left in case of overlap.

    val layout = findViewById<ConstraintLayout>(R.id.layout)
    layout.doOnNextLayout {
        val view1 = findViewById<View>(R.id.view1)
        val view2 = findViewById<View>(R.id.view2)
        val view1Margin =
            if ((layout.width + view1.width) / 2 < view2.x) {
                // view1 can be centered.
                (view2.x - (layout.width + view1.width) / 2).toInt()
            } else {
                // view1 is pushed to left of center. 16dp is the right margin now.
                (16 * resources.displayMetrics.density).toInt()
            }
        val lp = view1.layoutParams as ViewGroup.MarginLayoutParams
        lp.rightMargin = view1Margin
        view1.layoutParams = lp
    }

Now when view2 is short enough, view1 will be centered:

enter image description here

And when view2 is longer, view1 will be pushed to the side.

enter image description here

like image 183
Cheticamp Avatar answered Jan 27 '26 20:01

Cheticamp