Skip to content

Commit

Permalink
优化PagerIndicator
Browse files Browse the repository at this point in the history
  • Loading branch information
ltttttttttttt committed Sep 28, 2022
1 parent 08e0cdb commit 6d4c64a
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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)
)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -164,7 +164,7 @@ fun FlowLayout(
}
}
repeat(lineSize) { lineIndex ->
val placeable = placeables[index]
val placeable = placeableList[index]
//这个子元素相对于这一行(列)的对齐方式
val xOffset = when {
isHorizontal -> 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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))
}
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 6d4c64a

Please sign in to comment.