从下到上和从右到左排列 ConstraintLayout 流引用

Arranging ConstraintLayout Flow References From Bottom To Top And Right To Left

我需要从下到上和从右到左填充 ConstraintLayout Flow 视图。这是我的代码:

<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" />

这是结果:

(事实上,由于 API 响应,我将动态设置引用,因此假设视图不在 XML 布局中)

但我需要下面的:

感谢任何帮助。

你真的不需要按数字顺序设置 constraint_referenced_ids,而是你的布局顺序。您可以使用不可见的 View/Space 作为 space 占位符。

所以我编辑了以下代码供您参考:

<?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>

预览:

我没有使用 Flow 的经验,但据我所知,您想动态地处理这个问题, 因此,如果您能够处理来自服务器的项目顺序,这将对您有很大帮助。

它对任何项目大小(不仅仅是 10 个)都有用,只要你想在 4 行中显示它们。

我将使用 GridLayoutManagerspanSizeLookup 来处理这个问题。

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

这将导致目标项目填充回收器视图的空高度,以实现您想要的。

您需要做的最后一步是将现有 xml 项目的根包装在 FrameLayout 中,并赋予该根底部布局引力,如下所示:

<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>

我知道这不完全是你想要的,但我希望你能设法对你的数组进行排序并使用此方法动态创建它,如上图

此致

您可以在布局中保持 ID 的正常顺序,并创建一个自定义 Flow class 将 ID 重新排列为您想要的自定义顺序(从下到上,右向左):

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)}")
    }

}

并在布局中使用 CustomFlow

演示布局:

<?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>

预览:

更新

It doesn't have an acceptable behavior if you delete button11 and button12. Can you make it better, please?

因此,您想通过删除 button11button12.

来保持相同的行为

即您不需要 Flow 的额外按钮或空白填充符(这可以通过将它们的可见性设置为 INVISIBLE 来解决)。

但在这里,我将通过以下更改摆脱它们:

  • 修改自定义 Flow 以处理列是否为全尺寸列。
  • android:rotationY="180" 反转布局模拟 RTL 方向,并对每个单独的元素进行反转,以恢复 中描述的反转文本问题。
  • 分配 app:flow_verticalBias="1" 以便从底部开始偏移元素。

演示:

<?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>

自定义流程:

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)}")
    }


}

结果:

要从下到上和从右到左排列项目,您可以使用 FlexboxLayout Google Library。您只需要在 FlexboxLayout 中使用以下属性:

  1. app:flexDirection="column_reverse" 这将从下到上绘制每个项目。

  2. app:flexWrap="wrap_reverse" 这将从右到左绘制每个项目。

  3. app:justifyContent="flex_start" 这将控制每个项目的对齐方式,我们使用 flex_start 来对齐它从底部开始。

Xml 示例如下:

<?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>

或以编程方式:

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)
}

结果: