美文网首页
Compose 打造一个Home页面

Compose 打造一个Home页面

作者: owant | 来源:发表于2022-08-18 13:44 被阅读0次

    Compose 打造一个Home页面

    一般的APP首页都是由多个Tab组成。在Compose中,要实现这个会变得异常的简单,这个得益于Compose自带的组合函数功能。下面是轻松打造一个Home页面的过程。

    home_preview.png

    BottomNavigationView的实现

    由于Compose布局的组合化的灵活。这里直接实现一个 Image +Text的Tab,通过遍历数组进行生成即可。拆分步骤如下:

    1. 一个横向布局,嵌套多个竖向Tab
    2. Tab具备的信息:图片(选中和未选中)、文本
    3. 每一个Tab点击的回调:tag->Unit

    模型

    data class TabModel(
        val tagTag: String,
        val tabName: Int,
        val normalIcon: Int,
        val selectedIcon: Int
    )
    
    

    主体代码

    @Preview(showBackground = true)
    @Composable
    fun TabView(
        @PreviewParameter(TabDataSourceMock::class) tabSource: Array<TabModel>,
        tagCallback: ((String) -> Unit)?
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .shadow(4.dp, RectangleShape, false)
                .wrapContentHeight(Alignment.CenterVertically)
                .background(Color.White)
        ) {
    
            val imageModifier = Modifier.padding(0.dp, 8.dp, 0.dp, 0.dp)
            val tabModifier = Modifier.padding(0.dp, 0.dp, 0.dp, 5.dp)
    
            val selectIndex = rememberSaveable {
                mutableStateOf(0)
            }
    
            tabSource.forEachIndexed { index, currentModel ->
    
                Column(
                    modifier = Modifier
                        .weight(1F, true)
                        .clickable {
                            selectIndex.value = index
                            tagCallback?.invoke(currentModel.tagTag)
                        },
                    horizontalAlignment = Alignment.CenterHorizontally,
                    verticalArrangement = Arrangement.Center,
                ) {
                    Image(
                        painter = if (index == selectIndex.value) painterResource(
                            id = currentModel.selectedIcon
                        ) else
                            painterResource(
                                id = currentModel.normalIcon
                            ),
                        contentDescription = stringResource(id = currentModel.tabName),
                        modifier = imageModifier
                    )
                    Text(
                        stringResource(id = currentModel.tabName),
                        modifier = tabModifier,
                        textAlign = TextAlign.Center,
                        fontSize = 12.sp,
                        color = if (index == selectIndex.value) Color(0xFF07C160) else Color(0xFFAFB2B0),
                    )
                }
            }
        }
    }
    

    模拟数据

    class TabDataSourceMock : PreviewParameterProvider<Array<TabModel>> {
    
        override val values: Sequence<Array<TabModel>>
            get() = listOf<Array<TabModel>>(
                tabDataAll(),
                tabDataWithoutMall()
            ).asSequence()
    }
    
    fun tabDataAll(): Array<TabModel> {
        return arrayOf<TabModel>(
            TabModel(
                TabTags.TAG_HOME,
                R.string.tab_home,
                R.drawable.icon_home_normal,
                R.drawable.icon_home_selected
            ),
            TabModel(
                TabTags.TAG_SMART,
                R.string.tab_smart,
                R.drawable.icon_smart_normal,
                R.drawable.icon_smart_selected
            ),
            TabModel(
                TabTags.TAG_MALL,
                R.string.tab_mall,
                R.drawable.icon_mall_normal,
                R.drawable.icon_mall_selected
            ),
            TabModel(
                TabTags.TAG_MORE,
                R.string.tab_more,
                R.drawable.icon_me_normal,
                R.drawable.icon_me_selected
            )
        )
    }
    
    fun tabDataWithoutMall(): Array<TabModel> {
        return arrayOf<TabModel>(
            TabModel(
                TabTags.TAG_HOME,
                R.string.tab_home,
                R.drawable.icon_home_normal,
                R.drawable.icon_home_selected
            ),
            TabModel(
                TabTags.TAG_SMART,
                R.string.tab_smart,
                R.drawable.icon_smart_normal,
                R.drawable.icon_smart_selected
            ),
            TabModel(
                TabTags.TAG_MORE,
                R.string.tab_more,
                R.drawable.icon_me_normal,
                R.drawable.icon_me_selected
            )
        )
    }
    class TabTags {
        companion object {
            const val TAG_HOME = "home"
            const val TAG_SMART = "smart"
            const val TAG_MALL = "mall"
            const val TAG_MORE = "more"
        }
    }
    

    预览如下

    mock_preview2.png

    这里为什么有两个预览呢?主要是在模拟函数返回了两个source。

    开发中遇到的问题

    资源引用

    1. 引用字符串 stringResource(id = currentModel.tabName)
    2. 引用图片资源 painterResource(id = currentModel.selectedIcon)

    预览函数

    PreviewParameterProvider<Array<TabModel>> 这个函数正确使用方式如下

    @Preview(showBackground = true)
    @Composable
    fun TabView(
       @PreviewParameter(TabDataSourceMock::class) tabSource: Array<TabModel>,
       tagCallback: ((String) -> Unit)?
    ) 
    

    模型抽象的错误

    开始时,我对TabModel的抽象是直接使用了 Painter 导致了一个错误 Functions which invoke @Composable functions must be marked with the @Composable annotation
    这个错误已经非常明显,所以不能在非@Composable 函数下。尽可能抽象出不依赖Compose的model。

    Compose的便捷

    通过这个简单的例子发现。

    1. 简约到极致。 Compose的便捷不单单在简约,而且特别省时间,开发效率可以说是翻了好几倍。你想想,如果之前要实现这个,你要多少的封装,可能还要使用到Fragment的管理。真不敢想象。
    2. 依赖减少。 开发这个首页居然可以0依赖,没有什么第三方控件的引入,也没有去找控件的烦恼。这里需要注意的是,很多人担心Compose没有那么多开源框架,其实这个担心是多余的。因为Compose简化了布局的实现,已经不需要那些什么约束布局和线性布局的思路了,也不需要什么UI库,万物皆组合。

    相关文章

      网友评论

          本文标题:Compose 打造一个Home页面

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