【问题标题】:How to make Jetpack Compose FlowRow RTL如何制作 Jetpack Compose FlowRow RTL
【发布时间】:2022-09-23 16:18:43
【问题描述】:

我想在FlowRow 中显示一个列表元素,并且我希望FlowRow 方向为RTL。
我尝试使用 CompositionLocalProvider 但它没有改变任何东西:

CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
    FlowRow(
        mainAxisSpacing = 10.dp,
        crossAxisSpacing = 10.dp,
        modifier = Modifier
            .fillMaxWidth()
            .padding(4.dp),
        mainAxisAlignment = MainAxisAlignment.Center
    ) {
        state.myList.forEach {
            MyItem(item = it)
        }
    }
}  

    标签: android android-jetpack-compose


    【解决方案1】:

    这是known issue。在 Google 修复此问题之前,您可以使用以下两个文件(最初由 Sven 提供),而不是使用 accompanist-flowlayout 库。 API 完全相同。

    布局.kt

    import androidx.compose.ui.unit.Constraints
    
    internal enum class LayoutOrientation {
        Horizontal,
        Vertical
    }
    
    internal data class OrientationIndependentConstraints(
        val mainAxisMin: Int,
        val mainAxisMax: Int,
        val crossAxisMin: Int,
        val crossAxisMax: Int
    ) {
        constructor(c: Constraints, orientation: LayoutOrientation) : this(
            if (orientation === LayoutOrientation.Horizontal) c.minWidth else c.minHeight,
            if (orientation === LayoutOrientation.Horizontal) c.maxWidth else c.maxHeight,
            if (orientation === LayoutOrientation.Horizontal) c.minHeight else c.minWidth,
            if (orientation === LayoutOrientation.Horizontal) c.maxHeight else c.maxWidth
        )
    }
    

    流.kt

    import androidx.compose.foundation.layout.Arrangement
    import androidx.compose.runtime.Composable
    import androidx.compose.ui.Alignment
    import androidx.compose.ui.Modifier
    import androidx.compose.ui.layout.Layout
    import androidx.compose.ui.layout.Placeable
    import androidx.compose.ui.unit.Constraints
    import androidx.compose.ui.unit.Dp
    import androidx.compose.ui.unit.IntSize
    import androidx.compose.ui.unit.LayoutDirection
    import androidx.compose.ui.unit.dp
    import kotlin.math.max
    
    /**
     * A composable that places its children in a horizontal flow. Unlike [Row], if the
     * horizontal space is too small to put all the children in one row, multiple rows may be used.
     *
     * Note that just like [Row], flex values cannot be used with [FlowRow].
     *
     * @param modifier The modifier to be applied to the FlowRow.
     * @param mainAxisSize The size of the layout in the main axis direction.
     * @param mainAxisAlignment The alignment of each row's children in the main axis direction.
     * @param mainAxisSpacing The main axis spacing between the children of each row.
     * @param crossAxisAlignment The alignment of each row's children in the cross axis direction.
     * @param crossAxisSpacing The cross axis spacing between the rows of the layout.
     * @param lastLineMainAxisAlignment Overrides the main axis alignment of the last row.
     */
    @Composable
    public fun FlowRow(
        modifier: Modifier = Modifier,
        mainAxisSize: SizeMode = SizeMode.Wrap,
        mainAxisAlignment: FlowMainAxisAlignment = FlowMainAxisAlignment.Start,
        mainAxisSpacing: Dp = 0.dp,
        crossAxisAlignment: FlowCrossAxisAlignment = FlowCrossAxisAlignment.Start,
        crossAxisSpacing: Dp = 0.dp,
        lastLineMainAxisAlignment: FlowMainAxisAlignment = mainAxisAlignment,
        content: @Composable () -> Unit
    ) {
        Flow(
            modifier = modifier,
            orientation = LayoutOrientation.Horizontal,
            mainAxisSize = mainAxisSize,
            mainAxisAlignment = mainAxisAlignment,
            mainAxisSpacing = mainAxisSpacing,
            crossAxisAlignment = crossAxisAlignment,
            crossAxisSpacing = crossAxisSpacing,
            lastLineMainAxisAlignment = lastLineMainAxisAlignment,
            content = content
        )
    }
    
    /**
     * A composable that places its children in a vertical flow. Unlike [Column], if the
     * vertical space is too small to put all the children in one column, multiple columns may be used.
     *
     * Note that just like [Column], flex values cannot be used with [FlowColumn].
     *
     * @param modifier The modifier to be applied to the FlowColumn.
     * @param mainAxisSize The size of the layout in the main axis direction.
     * @param mainAxisAlignment The alignment of each column's children in the main axis direction.
     * @param mainAxisSpacing The main axis spacing between the children of each column.
     * @param crossAxisAlignment The alignment of each column's children in the cross axis direction.
     * @param crossAxisSpacing The cross axis spacing between the columns of the layout.
     * @param lastLineMainAxisAlignment Overrides the main axis alignment of the last column.
     */
    @Composable
    public fun FlowColumn(
        modifier: Modifier = Modifier,
        mainAxisSize: SizeMode = SizeMode.Wrap,
        mainAxisAlignment: FlowMainAxisAlignment = FlowMainAxisAlignment.Start,
        mainAxisSpacing: Dp = 0.dp,
        crossAxisAlignment: FlowCrossAxisAlignment = FlowCrossAxisAlignment.Start,
        crossAxisSpacing: Dp = 0.dp,
        lastLineMainAxisAlignment: FlowMainAxisAlignment = mainAxisAlignment,
        content: @Composable () -> Unit
    ) {
        Flow(
            modifier = modifier,
            orientation = LayoutOrientation.Vertical,
            mainAxisSize = mainAxisSize,
            mainAxisAlignment = mainAxisAlignment,
            mainAxisSpacing = mainAxisSpacing,
            crossAxisAlignment = crossAxisAlignment,
            crossAxisSpacing = crossAxisSpacing,
            lastLineMainAxisAlignment = lastLineMainAxisAlignment,
            content = content
        )
    }
    
    /**
     * Used to specify the alignment of a layout's children, in cross axis direction.
     */
    public enum class FlowCrossAxisAlignment {
        /**
         * Place children such that their center is in the middle of the cross axis.
         */
        Center,
        /**
         * Place children such that their start edge is aligned to the start edge of the cross axis.
         */
        Start,
        /**
         * Place children such that their end edge is aligned to the end edge of the cross axis.
         */
        End,
    }
    
    public typealias FlowMainAxisAlignment = MainAxisAlignment
    
    /**
     * Layout model that arranges its children in a horizontal or vertical flow.
     */
    @Composable
    private fun Flow(
        modifier: Modifier,
        orientation: LayoutOrientation,
        mainAxisSize: SizeMode,
        mainAxisAlignment: FlowMainAxisAlignment,
        mainAxisSpacing: Dp,
        crossAxisAlignment: FlowCrossAxisAlignment,
        crossAxisSpacing: Dp,
        lastLineMainAxisAlignment: FlowMainAxisAlignment,
        content: @Composable () -> Unit
    ) {
        fun Placeable.mainAxisSize() =
            if (orientation == LayoutOrientation.Horizontal) width else height
        fun Placeable.crossAxisSize() =
            if (orientation == LayoutOrientation.Horizontal) height else width
    
        Layout(content, modifier) { measurables, outerConstraints ->
            val sequences = mutableListOf<List<Placeable>>()
            val crossAxisSizes = mutableListOf<Int>()
            val crossAxisPositions = mutableListOf<Int>()
    
            var mainAxisSpace = 0
            var crossAxisSpace = 0
    
            val currentSequence = mutableListOf<Placeable>()
            var currentMainAxisSize = 0
            var currentCrossAxisSize = 0
    
            val constraints = OrientationIndependentConstraints(outerConstraints, orientation)
    
            val childConstraints = if (orientation == LayoutOrientation.Horizontal) {
                Constraints(maxWidth = constraints.mainAxisMax)
            } else {
                Constraints(maxHeight = constraints.mainAxisMax)
            }
    
            // Return whether the placeable can be added to the current sequence.
            fun canAddToCurrentSequence(placeable: Placeable) =
                currentSequence.isEmpty() || currentMainAxisSize + mainAxisSpacing.roundToPx() +
                        placeable.mainAxisSize() <= constraints.mainAxisMax
    
            // Store current sequence information and start a new sequence.
            fun startNewSequence() {
                if (sequences.isNotEmpty()) {
                    crossAxisSpace += crossAxisSpacing.roundToPx()
                }
                sequences += currentSequence.toList()
                crossAxisSizes += currentCrossAxisSize
                crossAxisPositions += crossAxisSpace
    
                crossAxisSpace += currentCrossAxisSize
                mainAxisSpace = max(mainAxisSpace, currentMainAxisSize)
    
                currentSequence.clear()
                currentMainAxisSize = 0
                currentCrossAxisSize = 0
            }
    
            for (measurable in measurables) {
                // Ask the child for its preferred size.
                val placeable = measurable.measure(childConstraints)
    
                // Start a new sequence if there is not enough space.
                if (!canAddToCurrentSequence(placeable)) startNewSequence()
    
                // Add the child to the current sequence.
                if (currentSequence.isNotEmpty()) {
                    currentMainAxisSize += mainAxisSpacing.roundToPx()
                }
                currentSequence.add(placeable)
                currentMainAxisSize += placeable.mainAxisSize()
                currentCrossAxisSize = max(currentCrossAxisSize, placeable.crossAxisSize())
            }
    
            if (currentSequence.isNotEmpty()) startNewSequence()
    
            val mainAxisLayoutSize = if (constraints.mainAxisMax != Constraints.Infinity &&
                mainAxisSize == SizeMode.Expand
            ) {
                constraints.mainAxisMax
            } else {
                max(mainAxisSpace, constraints.mainAxisMin)
            }
            val crossAxisLayoutSize = max(crossAxisSpace, constraints.crossAxisMin)
    
            val layoutWidth = if (orientation == LayoutOrientation.Horizontal) {
                mainAxisLayoutSize
            } else {
                crossAxisLayoutSize
            }
            val layoutHeight = if (orientation == LayoutOrientation.Horizontal) {
                crossAxisLayoutSize
            } else {
                mainAxisLayoutSize
            }
    
            layout(layoutWidth, layoutHeight) {
                sequences.forEachIndexed { i, placeables ->
                    val childrenMainAxisSizes = IntArray(placeables.size) { j ->
                        placeables[j].mainAxisSize() +
                                if (j < placeables.lastIndex) mainAxisSpacing.roundToPx() else 0
                    }
                    val alignment = if (i < sequences.lastIndex) {
                        mainAxisAlignment
                    } else {
                        lastLineMainAxisAlignment
                    }
    
                    val mainAxisPositions = IntArray(childrenMainAxisSizes.size) { 0 }
                    when (orientation) {
                        LayoutOrientation.Horizontal -> with(alignment.toHorizontalArrangement()) {
                            arrange(
                                mainAxisLayoutSize,
                                childrenMainAxisSizes,
                                layoutDirection,
                                mainAxisPositions
                            )
                        }
                        LayoutOrientation.Vertical -> with(alignment.toVerticalArrangement()) {
                            arrange(mainAxisLayoutSize, childrenMainAxisSizes, mainAxisPositions)
                        }
                    }
    
                    placeables.forEachIndexed { j, placeable ->
                        val crossAxisSize = placeable.crossAxisSize()
                        val crossAxis = when (crossAxisAlignment) {
                            FlowCrossAxisAlignment.Start -> 0
                            FlowCrossAxisAlignment.End ->
                                crossAxisSizes[i] - crossAxisSize
                            FlowCrossAxisAlignment.Center ->
                                Alignment.Center.align(
                                    IntSize.Zero,
                                    IntSize(
                                        width = 0,
                                        height = crossAxisSizes[i] - crossAxisSize
                                    ),
                                    LayoutDirection.Ltr
                                ).y
                        }
    
                        val crossAxisPosition = crossAxisPositions[i] + crossAxis
                        if (orientation == LayoutOrientation.Horizontal) {
                            placeable.place(
                                x = mainAxisPositions[j],
                                y = crossAxisPosition
                            )
                        } else {
                            placeable.place(
                                x = when (layoutDirection) {
                                    LayoutDirection.Ltr ->
                                        crossAxisPosition
                                    LayoutDirection.Rtl ->
                                        crossAxisLayoutSize - crossAxisPosition - crossAxisSize
                                },
                                y = mainAxisPositions[j]
                            )
                        }
                    }
                }
            }
        }
    }
    
    private fun FlowMainAxisAlignment.toHorizontalArrangement() =
        when (this) {
            FlowMainAxisAlignment.Center -> Arrangement.Center
            FlowMainAxisAlignment.Start -> Arrangement.Start
            FlowMainAxisAlignment.End -> Arrangement.End
            FlowMainAxisAlignment.SpaceEvenly -> Arrangement.SpaceEvenly
            FlowMainAxisAlignment.SpaceBetween -> Arrangement.SpaceBetween
            FlowMainAxisAlignment.SpaceAround -> Arrangement.SpaceAround
        }
    
    private fun FlowMainAxisAlignment.toVerticalArrangement() =
        when (this) {
            FlowMainAxisAlignment.Center -> Arrangement.Center
            FlowMainAxisAlignment.Start -> Arrangement.Top
            FlowMainAxisAlignment.End -> Arrangement.Bottom
            FlowMainAxisAlignment.SpaceEvenly -> Arrangement.SpaceEvenly
            FlowMainAxisAlignment.SpaceBetween -> Arrangement.SpaceBetween
            FlowMainAxisAlignment.SpaceAround -> Arrangement.SpaceAround
        }
    
    /**
     * Used to specify how a layout chooses its own size when multiple behaviors are possible.
     */
    // TODO(popam): remove this when Flow is reworked
    public enum class SizeMode {
        /**
         * Minimize the amount of free space by wrapping the children,
         * subject to the incoming layout constraints.
         */
        Wrap,
        /**
         * Maximize the amount of free space by expanding to fill the available space,
         * subject to the incoming layout constraints.
         */
        Expand
    }
    
    /**
     * Used to specify the alignment of a layout's children, in main axis direction.
     */
    public enum class MainAxisAlignment {
        /**
         * Place children such that they are as close as possible to the middle of the main axis.
         */
        Center,
    
        /**
         * Place children such that they are as close as possible to the start of the main axis.
         */
        Start,
    
        /**
         * Place children such that they are as close as possible to the end of the main axis.
         */
        End,
    
        /**
         * Place children such that they are spaced evenly across the main axis, including free
         * space before the first child and after the last child.
         */
        SpaceEvenly,
    
        /**
         * Place children such that they are spaced evenly across the main axis, without free
         * space before the first child or after the last child.
         */
        SpaceBetween,
    
        /**
         * Place children such that they are spaced evenly across the main axis, including free
         * space before the first child and after the last child, but half the amount of space
         * existing otherwise between two consecutive children.
         */
        SpaceAround;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-30
      • 2022-11-28
      • 1970-01-01
      • 2021-12-21
      • 2023-02-10
      • 1970-01-01
      相关资源
      最近更新 更多