diff --git a/app/src/main/java/com/lt/test_compose/PagerIndicatorActivity.kt b/app/src/main/java/com/lt/test_compose/PagerIndicatorActivity.kt index c414178..226b60c 100644 --- a/app/src/main/java/com/lt/test_compose/PagerIndicatorActivity.kt +++ b/app/src/main/java/com/lt/test_compose/PagerIndicatorActivity.kt @@ -232,7 +232,7 @@ class PagerIndicatorActivity : BaseComposeActivity() { indicatorItem = { Spacer( modifier = M - .size(12.dp, 6.dp) + .size(6.dp, 6.dp) .background(Color.Gray, CircleShape) ) }, diff --git a/compose_views/src/main/java/com/lt/compose_views/compose_pager/ComposePager.kt b/compose_views/src/main/java/com/lt/compose_views/compose_pager/ComposePager.kt index d57467e..62d5286 100644 --- a/compose_views/src/main/java/com/lt/compose_views/compose_pager/ComposePager.kt +++ b/compose_views/src/main/java/com/lt/compose_views/compose_pager/ComposePager.kt @@ -274,12 +274,12 @@ fun ComposePager( }) .clipScrollableContainer(orientation) - ) { measurables/* 可测量的(子控件) */, constraints/* 约束条件 */ -> + ) { measurableList/* 可测量的(子控件) */, constraints/* 约束条件 */ -> val selectIndex = composePagerState.currSelectIndex.value var width = 0 var height = 0 //测量子元素,并算出他们的最大宽度 - val placeableList = measurables + val placeableList = measurableList .filter { //只测量有效的布局 val key = it.layoutId diff --git a/compose_views/src/main/java/com/lt/compose_views/flow_layout/FlowLayout.kt b/compose_views/src/main/java/com/lt/compose_views/flow_layout/FlowLayout.kt index 85e0bfd..4a98b5f 100644 --- a/compose_views/src/main/java/com/lt/compose_views/flow_layout/FlowLayout.kt +++ b/compose_views/src/main/java/com/lt/compose_views/flow_layout/FlowLayout.kt @@ -54,7 +54,7 @@ fun FlowLayout( ) { val horizontalMarginPx = LocalDensity.current.run { horizontalMargin.roundToPx() } val verticalMarginPx = LocalDensity.current.run { verticalMargin.roundToPx() } - Layout(content, modifier) { measurables, constraints -> + Layout(content, modifier) { measurableList, constraints -> val maxWidth = constraints.maxWidth val maxHeight = constraints.maxHeight val isHorizontal = orientation == Orientation.Horizontal @@ -69,7 +69,7 @@ fun FlowLayout( var lineWidth = 0 var lineHeight = 0 - val placeables = measurables.map { + val placeableList = measurableList.map { //如果超过了最大行数限制,后续元素不测量且不放置 if (linesSize.size >= maxLines) { return@map NotPlace @@ -164,7 +164,7 @@ fun FlowLayout( } } repeat(lineSize) { lineIndex -> - val placeable = placeables[index] + val placeable = placeableList[index] //这个子元素相对于这一行(列)的对齐方式 val xOffset = when { isHorizontal -> 0 diff --git a/compose_views/src/main/java/com/lt/compose_views/pager_indicator/PagerIndicator.kt b/compose_views/src/main/java/com/lt/compose_views/pager_indicator/PagerIndicator.kt index c3a6a4f..c138744 100644 --- a/compose_views/src/main/java/com/lt/compose_views/pager_indicator/PagerIndicator.kt +++ b/compose_views/src/main/java/com/lt/compose_views/pager_indicator/PagerIndicator.kt @@ -16,37 +16,33 @@ package com.lt.compose_views.pager_indicator +import androidx.annotation.IntRange import androidx.compose.foundation.gestures.Orientation -import androidx.compose.foundation.layout.* import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.Layout import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp -import com.lt.compose_views.modifier.offsetPercent import kotlin.math.roundToInt /** * creator: lt 2022/6/27 lt.dygzs@qq.com * effect : 适用于Pager的指示器 - * warning: 如果方向为横向时,需要指示器的宽度一致,否则会出现ui和滑动逻辑的问题,反之亦然 todo 后续有时间优化为不需要宽高一致 + * warning: * @param size 指示器数量 * @param offsetPercentWithSelect 选中的指示器的偏移百分比 * @param selectIndex 选中的索引 * @param indicatorItem 未被选中的指示器 * @param selectIndicatorItem 被选中的指示器 * @param modifier 修饰 - * @param margin 指示器之间的间距 + * @param margin 指示器之间的间距(两边也有,保证即使选中的指示器较大,也不容易超出控件区域) * @param orientation 指示器排列方向 */ @Composable fun PagerIndicator( - size: Int, + @IntRange(from = 1) size: Int, offsetPercentWithSelect: Float, selectIndex: Int, indicatorItem: @Composable (index: Int) -> Unit, @@ -55,59 +51,96 @@ fun PagerIndicator( margin: Dp = 8.dp, orientation: Orientation = Orientation.Horizontal, ) { + if (size < 1) return val density = LocalDensity.current - val marginPx by remember(margin) { - mutableStateOf(density.run { margin.roundToPx() }) + //indicatorItem的中间位置坐标 + val indicatorItemCenters = remember(size) { + IntArray(size) } - val maxOffset = remember(marginPx) { - (size - 1).toFloat() to ((size - 1) * marginPx) - } - val minOffset = remember { - 0f to 0 - } - Box( - modifier, - contentAlignment = if (orientation == Orientation.Horizontal) Alignment.CenterStart else Alignment.TopCenter - ) { - //未选中的指示器 - if (orientation == Orientation.Horizontal) { - Row(verticalAlignment = Alignment.CenterVertically) { - IndicatorItems(size, indicatorItem, margin) - } - } else { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - IndicatorItems(size, indicatorItem, margin) + Layout(modifier = modifier, content = { + selectIndicatorItem() + repeat(size) { + indicatorItem(it) + } + }, measurePolicy = { measurableList, constraints -> + val marginPx = density.run { margin.roundToPx() } + val mConstraints = constraints.copy(minWidth = 0, minHeight = 0) + val selectPlaceable = measurableList.first().measure(mConstraints) + var width = 0 + var height = 0 + val isHorizontal = orientation == Orientation.Horizontal + //测量indicatorItem,并获取其占用的宽高 + val placeableList = (1 until measurableList.size).mapIndexed { index, i -> + val placeable = measurableList[i].measure(mConstraints) + if (isHorizontal) { + if (index == 0) { + width = marginPx + } + indicatorItemCenters[i - 1] = width + placeable.width / 2 + width += placeable.width + marginPx + height = maxOf(height, placeable.height) + } else { + if (index == 0) { + height = marginPx + } + indicatorItemCenters[i - 1] = height + placeable.height / 2 + width = maxOf(width, placeable.width) + height += placeable.height + marginPx } + placeable } - //选中的指示器 - Box( - Modifier - .offsetPercent( - xOffsetPercent = if (orientation == Orientation.Horizontal) offsetPercentWithSelect + selectIndex else 0f, - yOffsetPercent = if (orientation == Orientation.Vertical) offsetPercentWithSelect + selectIndex else 0f, - pxOffset = IntOffset( - if (orientation == Orientation.Horizontal) (marginPx * (selectIndex + offsetPercentWithSelect)).roundToInt() else 0, - if (orientation == Orientation.Vertical) (marginPx * (selectIndex + offsetPercentWithSelect)).roundToInt() else 0, - ), - maxOffset = maxOffset, - minOffset = minOffset, - ) - ) { - selectIndicatorItem() + width = maxOf(width, selectPlaceable.width) + height = maxOf(height, selectPlaceable.height) + layout(width, height) { + //放置indicatorItem + var coordinate = 0 + placeableList.forEachIndexed { index, placeable -> + if (index == 0) + coordinate += marginPx + coordinate += if (isHorizontal) { + placeable.placeRelative(coordinate, (height - placeable.height) / 2) + placeable.width + marginPx + } else { + placeable.placeRelative((width - placeable.width) / 2, coordinate) + placeable.height + marginPx + } + } + //放置selectIndicatorItem + selectPlaceable.placeRelative( + x = if (isHorizontal) { + //当前索引的中间坐标 + val currCenter = indicatorItemCenters[selectIndex] + //是否是往下一页翻 + val isNext = offsetPercentWithSelect >= 0 + //起始的x轴 + val startX = currCenter - selectPlaceable.width / 2 + //当前索引到下一个索引的差值(偏移量) + var difference = + indicatorItemCenters.getOrElse(selectIndex + if (isNext) 1 else -1) { currCenter } - currCenter + if (!isNext) + difference = 0 - difference + //计算最终的x轴(起始x轴+偏移的x轴) + (startX + difference * offsetPercentWithSelect).roundToInt() + } else + (width - selectPlaceable.width) / 2, + y = if (isHorizontal) + (height - selectPlaceable.height) / 2 + else { + //当前索引的中间坐标 + val currCenter = indicatorItemCenters[selectIndex] + //是否是往下一页翻 + val isNext = offsetPercentWithSelect >= 0 + //起始的y轴 + val startY = currCenter - selectPlaceable.height / 2 + //当前索引到下一个索引的差值(偏移量) + var difference = + indicatorItemCenters.getOrElse(selectIndex + if (isNext) 1 else -1) { currCenter } - currCenter + if (!isNext) + difference = 0 - difference + //计算最终的x轴(起始x轴+偏移的x轴) + (startY + difference * offsetPercentWithSelect).roundToInt() + }, + ) } - } -} - -//未选中的指示器条目 -@Composable -private fun IndicatorItems( - size: Int, - indicatorItem: @Composable (index: Int) -> Unit, - margin: Dp -) { - repeat(size) { - indicatorItem(it) - if (it < size - 1) - Spacer(modifier = Modifier.size(margin)) - } + }) } \ No newline at end of file diff --git a/compose_views/src/main/java/com/lt/compose_views/refresh_layout/RefreshLayout.kt b/compose_views/src/main/java/com/lt/compose_views/refresh_layout/RefreshLayout.kt index e8743f5..599ef3b 100644 --- a/compose_views/src/main/java/com/lt/compose_views/refresh_layout/RefreshLayout.kt +++ b/compose_views/src/main/java/com/lt/compose_views/refresh_layout/RefreshLayout.kt @@ -107,10 +107,10 @@ fun RefreshLayout( it } .clipScrollableContainer(composePosition.orientation) - ) { measurables, constraints -> - val contentPlaceable = measurables[0].measure(constraints.copy(minWidth = 0, minHeight = 0)) + ) { measurableList, constraints -> + val contentPlaceable = measurableList[0].measure(constraints.copy(minWidth = 0, minHeight = 0)) //宽或高不能超过content(根据方向来定) - val refreshContentPlaceable = measurables[1].measure( + val refreshContentPlaceable = measurableList[1].measure( Constraints( maxWidth = if (orientationIsHorizontal) Constraints.Infinity else contentPlaceable.width, maxHeight = if (orientationIsHorizontal) contentPlaceable.height else Constraints.Infinity,