You can keep the normal order of the Ids as-is in layout, and create a custom Flow
class that rearranges the Ids to the customized order you want (Bottom to Top, Right to Left):
class CustomFlow(context: Context, attrs: AttributeSet?) : Flow(context, attrs) {
init {
val newIds = IntArray(referencedIds.size)
Log.d("LOG_TAG", "init BEFORE: ${Arrays.toString(referencedIds)}")
for ((i, item) in (referencedIds).withIndex())
newIds[referencedIds.size - i - 1] = item
referencedIds = newIds
Log.d("LOG_TAG", "init AFTER: ${Arrays.toString(referencedIds)}")
}
}
And use CustomFlow
in your layout.
Demo 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="wrap_content">
<com.example.android.constraintlayoutflow.CustomFlow
android:id="@+id/flow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:constraint_referenced_ids="button1, button2, button3, button4, button5, button6, button7, button8, button9, button10, button11, button12"
app:flow_horizontalGap="8dp"
app:flow_maxElementsWrap="4"
app:flow_verticalGap="8dp"
app:flow_verticalStyle="packed"
app:flow_wrapMode="chain"
tools:ignore="MissingConstraints" />
<Button
android:id="@+id/button1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@android:color/holo_orange_light"
android:text="1"
android:textSize="18sp"
tools:ignore="MissingConstraints" />
<Button
android:id="@+id/button2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@android:color/holo_orange_light"
android:text="2"
android:textSize="18sp"
tools:ignore="MissingConstraints" />
<Button
android:id="@+id/button3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@android:color/holo_orange_light"
android:text="3"
android:textSize="18sp"
tools:ignore="MissingConstraints" />
<Button
android:id="@+id/button4"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@android:color/holo_orange_light"
android:text="4"
android:textSize="18sp"
tools:ignore="MissingConstraints" />
<Button
android:id="@+id/button5"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@android:color/holo_orange_light"
android:text="5"
android:textSize="18sp"
tools:ignore="MissingConstraints" />
<Button
android:id="@+id/button6"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@android:color/holo_orange_light"
android:text="6"
android:textSize="18sp"
tools:ignore="MissingConstraints" />
<Button
android:id="@+id/button7"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@android:color/holo_orange_light"
android:text="7"
android:textSize="18sp"
tools:ignore="MissingConstraints" />
<Button
android:id="@+id/button8"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@android:color/holo_orange_light"
android:text="8"
android:textSize="18sp"
tools:ignore="MissingConstraints" />
<Button
android:id="@+id/button9"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@android:color/holo_orange_light"
android:text="9"
android:textSize="18sp"
tools:ignore="MissingConstraints" />
<Button
android:id="@+id/button10"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@android:color/holo_orange_light"
android:text="10"
android:textSize="18sp"
tools:ignore="MissingConstraints" />
<Button
android:id="@+id/button11"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@android:color/holo_orange_light"
android:textSize="18sp"
tools:ignore="MissingConstraints" />
<Button
android:id="@+id/button12"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@android:color/holo_orange_light"
android:textSize="18sp"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
Preview:

UPDATE
It doesn't have an acceptable behavior if you delete button11 and button12. Can you make it better, please?
So, you want to keep it with the same behavior by removing button11
and button12
.
I.e. you don't need to have extra buttons or blank fillers of the Flow (this can be a workaround by setting their visibility to INVISIBLE).
But here, I will get rid of them with below changes:
- Modify the custom
Flow
to handle whether the column is a full-sized one or not.
- Reverse the layout with
android:rotationY="180"
to simulate RTL
direction, and do a reverse back for each individual element to recover the issue of the reversed text liked described in this answer.
- Assign
app:flow_verticalBias="1"
in order to bias the elements starting from the bottom.
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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:rotationY="180">
<com.example.android.constraintlayoutflow.CustomFlow
android:id="@+id/flow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:constraint_referenced_ids="button1, button2, button3, button4, button5, button6, button7, button8, button9, button10"
app:flow_horizontalGap="8dp"
app:flow_verticalBias="1"
app:flow_verticalGap="8dp"
app:flow_verticalStyle="packed"
app:flow_wrapMode="chain"
tools:flow_maxElementsWrap="4"
tools:ignore="MissingConstraints" />
<FrameLayout
android:id="@+id/button1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:rotationY="180"
tools:ignore="MissingConstraints">
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_orange_light"
android:text="1"
android:textSize="18sp" />
</FrameLayout>
<FrameLayout
android:id="@+id/button2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:rotationY="180"
tools:ignore="MissingConstraints">
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_orange_light"
android:text="2"
android:textSize="18sp" />
</FrameLayout>
<FrameLayout
android:id="@+id/button3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:rotationY="180"
tools:ignore="MissingConstraints">
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_orange_light"
android:text="3"
android:textSize="18sp" />
</FrameLayout>
<FrameLayout
android:id="@+id/button4"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:rotationY="180"
tools:ignore="MissingConstraints">
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_orange_light"
android:text="4"
android:textSize="18sp" />
</FrameLayout>
<FrameLayout
android:id="@+id/button5"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:rotationY="180"
tools:ignore="MissingConstraints">
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_orange_light"
android:text="5"
android:textSize="18sp" />
</FrameLayout>
<FrameLayout
android:id="@+id/button6"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:rotationY="180"
tools:ignore="MissingConstraints">
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_orange_light"
android:text="6"
android:textSize="18sp" />
</FrameLayout>
<FrameLayout
android:id="@+id/button7"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:rotationY="180"
tools:ignore="MissingConstraints">
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_orange_light"
android:text="7"
android:textSize="18sp" />
</FrameLayout>
<FrameLayout
android:id="@+id/button8"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:rotationY="180"
tools:ignore="MissingConstraints">
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_orange_light"
android:text="8"
android:textSize="18sp" />
</FrameLayout>
<FrameLayout
android:id="@+id/button9"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:rotationY="180"
tools:ignore="MissingConstraints">
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_orange_light"
android:text="9"
android:textSize="18sp" />
</FrameLayout>
<FrameLayout
android:id="@+id/button10"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:rotationY="180"
tools:ignore="MissingConstraints">
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_orange_light"
android:text="10"
android:textSize="18sp" />
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Custom Flow:
class CustomFlow(context: Context, attrs: AttributeSet?) : Flow(context, attrs) {
init {
val size = referencedIds.size
val newIds = IntArray(size)
Log.d("LOG_TAG", "init BEFORE: ${Arrays.toString(referencedIds)}")
val COL_SIZE = 4
setMaxElementsWrap(COL_SIZE)
val extraItemCount = size % COL_SIZE
val fullSize = extraItemCount == 0
val colCount = if (fullSize) size / COL_SIZE else size / COL_SIZE + 1
var counter = 1
var baseIndex = -1
for ((i, item) in (referencedIds).withIndex()) {
val col: Int = i / COL_SIZE + 1
var newI: Int
if (fullSize || col < colCount) {
newI = col * COL_SIZE - i - 1 + (col - 1) * COL_SIZE
} else {
if (baseIndex == -1) baseIndex = i
newI = baseIndex + extraItemCount - counter
counter++
}
newIds[newI] = item
}
referencedIds = newIds
Log.d("LOG_TAG", "init AFTER: ${Arrays.toString(referencedIds)}")
}
}
Result:
