美文网首页
Compose仿美团左右列表联动

Compose仿美团左右列表联动

作者: 好多个胖子 | 来源:发表于2024-03-31 14:20 被阅读0次

    简介

    仿造美团外卖点餐页面,左边是菜品类型,右边是菜品详情,右侧列表滑动,左侧菜品跟随选中对应的菜品,如果要固定头的话 可以开启注释掉的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)
                            }
                        }
                    }
                }
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:Compose仿美团左右列表联动

          本文链接:https://www.haomeiwen.com/subject/zoxbtjtx.html