Reputation: 3265
I need to fill ConstraintLayout
Flow
view from Bottom To Top And Right To Left. This is my code:
<androidx.constraintlayout.helper.widget.Flow
android:id="@+id/flow"
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="vertical"
app:constraint_referenced_ids="tv1,tv2,tv3,tv4,tv5,tv6,tv7,tv8,tv9,tv10"
app:flow_horizontalGap="8dp"
app:flow_verticalGap="8dp"
app:flow_verticalStyle="packed"
app:flow_wrapMode="chain"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHeight_percent=".5"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
And this is the result:
(In fact I'm going to set references dynamically due to the API response, so assume that the views are not in XML
layout)
But I need the below one:
Any help is appreciated.
Upvotes: 4
Views: 2350
Reputation: 40878
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:
Flow
to handle whether the column is a full-sized one or not.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.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:
Upvotes: 1
Reputation: 9113
To arrange items from Bottom-to-Top and Right-to-Left you can use the FlexboxLayout Google Library. All you need is to use the below attributes in FlexboxLayout:
app:flexDirection="column_reverse" This will draw each item from Bottom to Top.
app:flexWrap="wrap_reverse" This will draw each item from Right to Left.
app:justifyContent="flex_start" This will control the alignment of each item and we use flex_start
to align it from bottom.
Xml sample is like below:
<?xml version="1.0" encoding="utf-8"?>
<HorizontalScrollView 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">
<com.google.android.flexbox.FlexboxLayout
android:id="@+id/flexLayout"
android:layout_width="wrap_content"
android:layout_height="300dp"
app:flexDirection="column_reverse"
app:flexWrap="wrap_reverse"
app:justifyContent="flex_start"
android:background="@android:color/white">
<TextView
android:id="@+id/tv1"
android:layout_width="120dp"
android:layout_height="65dp"
android:background="@android:color/holo_orange_dark"
android:gravity="center"
android:layout_margin="5dp"
android:text="1"
android:textSize="18sp" />
<TextView
android:id="@+id/tv2"
android:layout_width="120dp"
android:layout_height="65dp"
android:background="@android:color/holo_orange_dark"
android:gravity="center"
android:layout_margin="5dp"
android:text="2"
android:textSize="18sp" />
<TextView
android:id="@+id/tv3"
android:layout_width="120dp"
android:layout_height="65dp"
android:background="@android:color/holo_orange_dark"
android:gravity="center"
android:layout_margin="5dp"
android:text="3"
android:textSize="18sp" />
<TextView
android:id="@+id/tv4"
android:layout_width="120dp"
android:layout_height="65dp"
android:background="@android:color/holo_orange_dark"
android:gravity="center"
android:layout_margin="5dp"
android:text="4"
android:textSize="18sp" />
<TextView
android:id="@+id/tv5"
android:layout_width="120dp"
android:layout_height="65dp"
android:background="@android:color/holo_orange_dark"
android:gravity="center"
android:layout_margin="5dp"
android:text="5"
android:textSize="18sp" />
<TextView
android:id="@+id/tv6"
android:layout_width="120dp"
android:layout_height="65dp"
android:background="@android:color/holo_orange_dark"
android:gravity="center"
android:layout_margin="5dp"
android:text="6"
android:textSize="18sp" />
<TextView
android:id="@+id/tv7"
android:layout_width="120dp"
android:layout_height="65dp"
android:background="@android:color/holo_orange_dark"
android:gravity="center"
android:layout_margin="5dp"
android:text="7"
android:textSize="18sp" />
<TextView
android:id="@+id/tv8"
android:layout_width="120dp"
android:layout_height="65dp"
android:background="@android:color/holo_orange_dark"
android:gravity="center"
android:layout_margin="5dp"
android:text="8"
android:textSize="18sp" />
<TextView
android:id="@+id/tv9"
android:layout_width="120dp"
android:layout_height="65dp"
android:background="@android:color/holo_orange_dark"
android:gravity="center"
android:layout_margin="5dp"
android:text="9"
android:textSize="18sp" />
<TextView
android:id="@+id/tv10"
android:layout_width="120dp"
android:layout_height="65dp"
android:background="@android:color/holo_orange_dark"
android:gravity="center"
android:layout_margin="5dp"
android:text="10"
android:textSize="18sp" />
</com.google.android.flexbox.FlexboxLayout>
</HorizontalScrollView>
or Programmatically:
val flexLayout: FlexboxLayout = findViewById<FlexboxLayout>(R.id.flexLayout)
val itemWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 120f, resources.displayMetrics).toInt()
val itemHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 65f, resources.displayMetrics).toInt()
val itemMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5f, resources.displayMetrics).toInt()
for (i in 0..9) {
val tv = TextView(this)
tv.text = (i + 1).toString()
tv.setBackgroundColor(ContextCompat.getColor(this, android.R.color.holo_orange_dark))
tv.gravity = Gravity.CENTER
val params = FlexboxLayout.LayoutParams(itemWidth, itemHeight)
params.setMargins(itemMargin, itemMargin, itemMargin, itemMargin)
tv.layoutParams = params
flexLayout.addView(tv)
}
Result:
Upvotes: 5
Reputation: 336
I don't have experience with Flow but as i know, you wanna handle this dynamicly, so if you be able to handle the order of items from server, this will help you a lot.
It can be useful for any item size(not just 10) as long as you want to show them in 4 rows.
Im going to use GridLayoutManager with spanSizeLookup to handle this.
val items = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val layoutManager = GridLayoutManager(context, 4, GridLayoutManager.HORIZONTAL, false)
val targetPosition = (items.size / 4) * 4
val spanSizeLookup: GridLayoutManager.SpanSizeLookup by lazy {
object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return if (position == targetPosition) (items.size % 4) + 1 else 1
}
}
}
layoutManager.spanSizeLookup = spanSizeLookup
recyclerView.layoutManager = layoutManager
adapter.setData(items)
recyclerView.adapter = adapter
This will cause the target item to fill the empty height of recycler view in order to achieve what you want.
The last step you need to do is wrap your root of existing xml item in a FrameLayout and give that root , bottom layout gravity like this:
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="6dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
/>
</FrameLayout>
I know its not completely what you want, but i hope you can manage to sort your array and use this method to create this dynamically , like the picture above
Best regards
Upvotes: 1
Reputation: 102
You don't really need to set constraint_referenced_ids in numerical order, but your layout order. And you can use invisible View/Space as space placeholder.
So I edit following code for your reference:
<?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="match_parent">
<TextView
android:id="@+id/space1"
style="@style/text_style"
android:layout_width="0dp"
android:layout_height="60dp"
android:gravity="center"
android:text=""
android:visibility="invisible" />
<TextView
android:id="@+id/A"
style="@style/text_style"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center"
android:text="8" />
<TextView
android:id="@+id/B"
style="@style/text_style"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center"
android:text="4" />
<TextView
android:id="@+id/space2"
style="@style/text_style"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center"
android:text=""
android:visibility="invisible" />
<TextView
android:id="@+id/C"
style="@style/text_style"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center"
android:text="7" />
<TextView
android:id="@+id/D"
style="@style/text_style"
android:layout_width="0dp"
android:layout_height="60dp"
android:gravity="center"
android:text="3" />
<TextView
android:id="@+id/E"
style="@style/text_style"
android:layout_width="0dp"
android:layout_height="60dp"
android:gravity="center"
android:text="10" />
<TextView
android:id="@+id/F"
style="@style/text_style"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center"
android:text="6" />
<TextView
android:id="@+id/G"
style="@style/text_style"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center"
android:text="2" />
<TextView
android:id="@+id/H"
style="@style/text_style"
android:layout_width="0dp"
android:layout_height="0dp"
android:gravity="center"
android:text="9" />
<TextView
android:id="@+id/I"
style="@style/text_style"
android:layout_width="0dp"
android:layout_height="60dp"
android:gravity="center"
android:text="5" />
<TextView
android:id="@+id/J"
style="@style/text_style"
android:layout_width="0dp"
android:layout_height="60dp"
android:gravity="center"
android:text="1" />
<androidx.constraintlayout.helper.widget.Flow
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:constraint_referenced_ids="space1,A,B,space2,C,D,E,F,G,H,I,J"
app:flow_horizontalGap="8dp"
app:flow_horizontalStyle="packed"
app:flow_maxElementsWrap="3"
app:flow_verticalGap="8dp"
app:flow_verticalStyle="packed"
app:flow_wrapMode="aligned"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Preview:
Upvotes: 0