• Compose 打造一个Home页面


    Compose 打造一个Home页面

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

    在这里插入图片描述

    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
    )
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    主体代码

    @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),
                    )
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    模拟数据

    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"
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    预览如下

    在这里插入图片描述

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

    开发中遇到的问题

    资源引用

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

    预览函数

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

    @Preview(showBackground = true)
    @Composable
    fun TabView(
       @PreviewParameter(TabDataSourceMock::class) tabSource: Array<TabModel>,
       tagCallback: ((String) -> Unit)?
    ) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    模型抽象的错误

    开始时,我对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库,万物皆组合。
  • 相关阅读:
    基于SpringBoot+Mybatis-Plus的快速入门案例
    SQL必需掌握的100个重要知识点:使用表别名
    【iOS开发】——KVO与KVC
    声纹技术(一):声纹技术的前世今生
    驱动开发2 CoetexA7核 字符设备驱动(LED亮灯)(单独映射寄存器实现+封装结构体映射实现)
    什么是等保
    GBase 8s数据库级别权限
    2310C++构造对象
    golang gin ShouldBindHeader绑定请求头数据:`header:“Referer“ binding:“required“`
    6.29日刷题题解
  • 原文地址:https://blog.csdn.net/u012131702/article/details/126343020