简介
仿造美团外卖点餐页面,左边是菜品类型,右边是菜品详情,右侧列表滑动,左侧菜品跟随选中对应的菜品,如果要固定头的话 可以开启注释掉的
stickyHeader
替换item
private const val TAG = "WidgetTabList"
enum class TabState {
Normal, PreCheck, Check, NextCheck
}
enum class ItemType {
Head, Content
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun <T, E> WidgetTabList(
modifier: Modifier = Modifier,
leftModifier: RowScope.() -> Modifier = { Modifier },
rightModifier: RowScope.() -> Modifier = { Modifier },
tabList: List<T>,
itemList: List<E>,
findTabIndex: (Int, E) -> Int,
findItemIndex: (Int, T) -> Int,
getItemType: (E) -> ItemType,
tabItemLayout: @Composable LazyItemScope.(T, TabState) -> Unit,
itemHeadLayout: @Composable LazyItemScope.(E) -> Unit,
itemContentLayout: @Composable LazyItemScope.(E) -> Unit
) {
var isUserClick by remember {
mutableStateOf(false)
}
var index by remember {
mutableIntStateOf(0)
}
val listState = rememberLazyListState()
val tabListState = rememberLazyListState()
val realIndex by remember(itemList) {
derivedStateOf() {
val fvIndex = listState.firstVisibleItemIndex
val i = if (itemList.isEmpty()) {
0
} else if (fvIndex >= itemList.size) {
findTabIndex(fvIndex, itemList.last())
} else {
findTabIndex(fvIndex, itemList[fvIndex])
}
Log.d(TAG, "derivedStateOf $fvIndex i = $i")
i
}
}
LaunchedEffect(key1 = realIndex) {
Log.d(TAG, "Content: index=$index;realIndex=$realIndex;isUserClick=$isUserClick")
if (!isUserClick && index != realIndex) {
index = realIndex
val fi = tabListState.firstVisibleItemIndex
val count = tabListState.layoutInfo.visibleItemsInfo.size
val step = tabListState.layoutInfo.viewportSize.height / 2f
Log.d(TAG, "Content: fi = $fi count=$count index = $index tabHeight.value=${step}")
if (fi + count <= index + 1) {
tabListState.scrollBy(step)
} else if (index < fi) {
tabListState.scrollToItem(index)
}
}
isUserClick = false
}
val scope = rememberCoroutineScope()
Row(modifier = modifier) {
LazyColumn(
modifier = leftModifier(),
state = tabListState,
) {
itemsIndexed(tabList, { i, _ -> i }) { i, item ->
val tabState = when (i) {
index -> TabState.Check
index - 1 -> TabState.PreCheck
index + 1 -> TabState.NextCheck
else -> TabState.Normal
}
Box(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.clickable {
Log.d(TAG, "click tab $i")
if (index == i) return@clickable
index = i
isUserClick = true
scope.launch {
val itemIndex = findItemIndex(index, tabList[index])
listState.scrollToItem(itemIndex)
}
}
) {
tabItemLayout(item, tabState)
}
}
}
LazyColumn(modifier = rightModifier(), state = listState) {
itemList.forEach {
val type = getItemType(it)
when (type) {
ItemType.Head -> {
// stickyHeader {
item {
itemHeadLayout(it)
}
// }
}
ItemType.Content -> {
item {
itemContentLayout(it)
}
}
}
}
}
}
}