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
)

主体代码

@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"
    }
}

预览如下

在这里插入图片描述

这里为什么有两个预览呢?主要是在模拟函数返回了两个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库,万物皆组合。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值