1. 前言
上篇文章 我们实现了 Compose Bloom项目的开发页,这篇文章接着上文,来介绍主页的开发。
2. 分析页面布局
根据UI
稿我们可知,这个页面有一个底部切换的BottomBar
,还有一个搜索框,一个横向的列表以及一个竖向的列表。
3. 定义数据
首先先定义数据
data class ImageItem(val name: String, val resId: Int)
导航栏的数据
val navList = listOf(
ImageItem("Home", R.drawable.ic_home),
ImageItem("Favorites", R.drawable.ic_favorite_border),
ImageItem("Profile", R.drawable.ic_account_circle),
ImageItem("Cart", R.drawable.ic_shopping_cart)
)
植物的数据
val gardenList = listOf(
ImageItem("Monstera", R.drawable.monstera),
ImageItem("Aglaonema", R.drawable.aglaonema),
ImageItem("Peace lily", R.drawable.peace_lily),
ImageItem("Fiddle leaf", R.drawable.fiddle_leaf),
ImageItem("Snake plant", R.drawable.snake_plant),
ImageItem("Pothos", R.drawable.pothos),
)
主题集合的数据
val themeList = listOf(
ImageItem("Desert chic", R.drawable.desert_chic),
ImageItem("Tiny terrariums", R.drawable.tiny_terrariums),
ImageItem("Jungle vibes", R.drawable.jungle_vibes),
ImageItem("Easy care", R.drawable.easy_care),
ImageItem("Statements", R.drawable.statements),
)
4. 实现底部Tab栏BottomBar
Scaffold
是Android
中的脚手架Scaffold
,Compose
提供了大量基于 Material Design
的可组合项以及依赖项,旨在简化界面的构建。Scaffold
可以实现具有基本 Material Design
布局结构的界面,比如Drawer
、FloatingActionButton
、 TopAppBar
以及BottomBar
@Preview
@Composable
fun HomePage() {
Scaffold(bottomBar = {
BottomBar()
}) {
}
}
BottomBar
里其实就是通过for
循环navList
,然后创建每一项的BottomNavigationItem
每个BottomNavigationItem
里都包含一个icon
和一个lable
@Preview(showBackground = true, backgroundColor = 0xFFFFFF)
@Composable
fun BottomBar() {
BottomNavigation(
modifier = Modifier
.fillMaxWidth()
.height(56.dp),
backgroundColor = pink100,
) {
navList.forEach {
val isSelected = "Home" == it.name
BottomNavigationItem(selected = isSelected, onClick = { }, icon = {
Image(
painter = painterResource(id = it.resId),
contentDescription = "",
modifier = Modifier.size(24.dp),
colorFilter = ColorFilter.tint(
if (isSelected) gray else Color(
0xff757575
)
)
)
}, label = {
Text(
text = it.name,
style = caption,
color = if (isSelected) gray else Color(0xff757575)
)
})
}
}
}
预览效果如下所示
5. 实现搜索框
搜索框其实就是OutlinedTextField
,带边框的输入框,leadingIcon
中传入Icon
,用来显示搜索图标。
OutlinedTextField
是material design
包下的,说明它是个material design
风格的组件
@Preview(showBackground = true)
@Composable
fun SearchBar() {
val keyword = remember { mutableStateOf("") }
Box(
modifier = Modifier
.height(96.dp)
.padding(top = 40.dp)
) {
OutlinedTextField(
value = keyword.value, onValueChange = {
keyword.value = it
}, modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp),
maxLines = 1,
placeholder = { Text(text = "Search", style = MaterialTheme.typography.body1) },
leadingIcon = {
Icon(
imageVector = Icons.Outlined.Search,
contentDescription = null,
modifier = Modifier.size(18.dp)
)
}
)
}
}
OutlinedTextField
针对样式进行了封装,我们直接用更底层的foundation
包下的BasicTextField
,也可以实现同样的效果
预览效果如下所示
6. 实现横向列表
首先实现Item
BloomRowTitle
是标题
@Composable
private fun BloomRowTitle() {
Text(
text = "Browse themes",
modifier = Modifier.padding(start = 16.dp, top = 16.dp, bottom = 6.dp),
style = h1
)
}
然后实现横向列表
这里就用到LazyRow
这个组件了
LazyRow
和LazyColumn
都是列表组件,用来替代传统View体系中的Recyclerview
LazyRow
和LazyColumn
内部都是基于LazyList
组件实现的(这是一个internal
的内部组件,我们无法直接使用)
可以看到传参中的content
是一个LazyListScope.()->Unit
使用方式如下所示
LazyRow {
//添加一个Item
item {
Text("XXXXXXX")
}
//继续添加5个Item
item(5) {
Text("YYYYY"+it)
}
//继续添加一个List (等于添加了List.size个Item)
item(myList){ data ->
Text("$data.name $data.age")
}
}
知道了怎么用后,再来看下在本项目中怎么使用
代码如下所示
@Composable
fun BloomRowList() {
LazyRow(content = {
items(themeList.size) {
ThemeCard(themeList[it])
}
}, modifier = Modifier.padding(start = 10.dp, end = 10.dp, top = 3.dp))
}
ThemeCard
是每一项的Item
,实现如下
包含一个Box
,一个Image
,还有一个Text
,最外层是一个Card
,所以带有卡片阴影的效果
@Composable
fun ThemeCard(item: ImageItem) {
Card(elevation = 5.dp, modifier = Modifier.padding(6.dp)) {
Surface(
color = white
) {
Box(
modifier = Modifier
.height(136.dp)
.width(136.dp)
) {
Image(
painter = painterResource(id = item.resId),
contentDescription = null,
modifier = Modifier.height(96.dp),
contentScale = ContentScale.Crop
)
Text(
text = item.name,
style = h2,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
modifier = Modifier
.align(Alignment.BottomStart)
.padding(start = 16.dp, end = 16.dp, bottom = 12.dp)
.fillMaxWidth()
)
}
}
}
}
进行组装
@Preview(showBackground = true, backgroundColor = 0xFFFFFF)
@Composable
fun BloomRowPreView() {
Column {
BloomRowTitle()
BloomRowList()
}
}
预览效果如下
7. 实现竖向的列表
首先实现Item
@Composable
fun BloomListTitle() {
Box(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
) {
Text(
text = "Design you home garden",
modifier = Modifier.align(Alignment.CenterStart),
style = h1,
)
Image(
imageVector = Icons.Default.FilterList,
contentDescription = null,
modifier = Modifier.align(Alignment.CenterEnd),
colorFilter = ColorFilter.tint(MaterialTheme.colors.onBackground)
)
}
}
然后实现竖向列表
@Composable
fun BloomList() {
LazyColumn(content = {
items(gardenList.size) {
ItemGarden(it == 0, gardenList[it])
}
})
}
来实现具体的Item
,包含一个Image
、Checkbox
、主标题
和副标题
,以及一条分隔线。
最外层用的一个Box
布局进行包裹,通过fillMaxWidth
使其宽度设为最大。
@Composable
fun ItemGarden(checked: Boolean, item: ImageItem) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp, horizontal = 16.dp)
.height(64.dp),
) {
Image(
painter = painterResource(id = item.resId),
contentDescription = "",
contentScale = ContentScale.Crop,
modifier = Modifier
.height(64.dp)
.width(64.dp)
.clip(shape = RoundedCornerShape(4.dp))
)
Column(modifier = Modifier.padding(start = 80.dp)) {
Text(text = item.name, style = h2, modifier = Modifier.padding(top = 12.dp))
Text(text = "This is a description", style = body1)
}
Checkbox(
checked = checked,
onCheckedChange = {},
modifier = Modifier
.align(Alignment.CenterEnd)
.padding(end = 16.dp)
)
Divider(
modifier = Modifier
.padding(start = 72.dp, end = 16.dp)
.align(Alignment.BottomStart)
)
}
}
这里的Checkbox
暂时只是一个展示的用途,并无法让其真正进行点击
如果要让其可以点击,那么需要传入mutableStateOf
给Checkbox
,并在onCheckedChange
回调的时候修改该mutableStateOf
的值
var selectedby remember {
mutableStateOf(true)
}
Checkbox(
checked = selected,
onCheckedChange = {
selected = it
},
modifier = Modifier
.align(Alignment.CenterEnd)
.padding(end = 16.dp)
)
预览效果如下
8. 组装所有组件
这里我们使用Column
竖向列表组装本文上述内容的组件
@Composable
fun HomePage() {
Scaffold(bottomBar = {
BottomBar()
}) {
Column {
SearchBar()
BloomRowTitle()
BloomRowList()
BloomListTitle()
BloomList()
}
}
}
最终显示效果如下
9. 小结
至此,我们就完成了主页的UI效果,下一篇文章,我们将来处理Compose页面间的跳转
Compose 项目实战 系列文章
Android Compose Bloom 项目实战 (一) : 项目说明与配置
Android Compose Bloom 项目实战 (二) : 欢迎页
Android Compose Bloom 项目实战 (三) : 登录页
Android Compose Bloom 项目实战 (四) : 主页
Android Compose Bloom 项目实战 (五) : 使用Navigation实现页面跳转
本文源码下载 : ComposeBloom