这将是一个完整的UI设计教程,是我再次长时间思考后得出的结果。我们将了解如何规划UI设计,并尽可能接近我们的线框图来实现它。作为参考,我将采用WhatsApp真实应用的界面,但请注意,颜色编码可能会有少许不同,因为颜色是从颜色拾取器中提取的。
拆分细节
首先,让我们看看在每个屏幕中如何将设计细分为较小的组件。
Home Screen
在这里,我们可以看到整体屏幕上有一列可能的项目作为头部区域,选项卡区域,选项卡内容区域和一个浮动操作按钮。因此,我们可以说我们的主屏幕组件应该如下所示:
Box {
Column {
AppBar()
TabBar()
HorizontalPager()
}
FloatingActionButton()
}
AppBar
AppBar简单地包含一个带有4个小部件的行。但是,我们在行中没有任何现有的间距排列,这使得我们的期望对齐方式实现:即文本位于左侧,其余三个项目位于右侧。
因此,我们可以采取spacer
,给它一个权重,如下所示:
Row(
modifier = Modifier,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(id = R.string.whatsapp_title),
style = TextStyle(
fontSize = 22.sp,
fontWeight = FontWeight.SemiBold,
color = if (isSystemInDarkTheme()) PrimaryGray_A101 else White
)
)
Spacer(modifier = Modifier.weight(1f))
ActionIcon(id = R.drawable.ic_camera_outline)
Spacer(modifier = Modifier.width(20.dp))
ActionIcon(id = R.drawable.ic_search_outline)
Spacer(modifier = Modifier.width(16.dp))
ActionIcon(id = R.drawable.ic_overflow_filled)
}
TabBar
现在我们来看看如何使用TabRow创建我们的选项卡布局。在WhatsApp中,状态选项卡和通话选项卡带有附加的指示器。
因此,这意味着除了聊天选项卡之外,我们还需要为每个选项卡添加一个包含文本和指示器的行。我们可以这样做:
TabRow(
modifier = Modifier.fillMaxWidth(),
selectedTabIndex = selectedPage,
indicator = { tabPositions ->
}
) {
homeTabs.forEachIndexed { index, tabData ->
Tab(
selected = index == selectedPage,
) {
tabData.unreadCount?.let { count ->
Row( // if we find that either status count or call count is greater than 0 then we show this row
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Text(
text = tabData.tab.value,
style = TextStyle(
fontSize = 16.sp,
)
)
if (tabData.tab.sameAs(Tabs.MESSAGE_STATUS)) {
AnimatedVisibility(visible = statusIndicatorVisible) {
TabIndicator()
}
} else {
AnimatedVisibility(visible = callIndicatorVisible) {
UnreadCountIndicator(count)
}
}
}
}?: Text( // else we show simple text
text = tabData.tab.value,
style = TextStyle(
fontSize = 16.sp,
)
)
}
}
}
HorizontalPager
现在是设置每个选项卡的内容的时候了。为了设置一个水平滑动页面(HorizontalPager),我们可以这样做:
HorizontalPager(
pageCount = 3,
beyondBoundsPageCount = 3,
state = viewPagerState,
modifier = Modifier
.background(MaterialTheme.colors.background)
.fillMaxSize(),
) { page ->
when (page) {
0 -> ChatListScreen()
1 -> StatusListScreen()
2 -> CallsListScreen()
}
}
FloatingActionButton
现在我们看到浮动操作按钮始终在整个屏幕上方,因此我们将列包装在一个盒子内,并将FAB放在底部右下角。
好的!现在我们创建了我们的屏幕,该轮到创建单独的屏幕了。
ChatList Screen
聊天列表屏幕看起来相当简单。我们有一个带有项目的列表,可以使用懒加载列来实现,如下所示:
LazyColumn(
modifier = Modifier.padding(horizontal = 16.dp),
horizontalAlignment = Alignment.Start,
) {
item {
Spacer(modifier = Modifier.height(4.dp))
}
items(
items = chatListItems,
key = { chatData ->
chatData.chatId
}
) { chatData ->
ChatListItem(chatData)
}
item {
Spacer(modifier = Modifier.height(75.dp))
}
}
这里看到的空间是为了保持设计中的边距。ChatListItem再次是一个子组件,其中包含用户图像、姓名和带有时间戳的最后一条消息。
ChatData是用于聊天列表的数据类,您可以在代码库中找到它。
https://github.com/aqua30/WhatsAppDesign-Compose/blob/master/app/src/main/java/com/aqua30/whatsapp/chatscreen/design/data/ChatDataSource.kt
这里,值得注意的是在列表上向用户展示的最后一条消息。它可以是发送的或接收的消息,可以是文本、视频或音频,如果是发送的消息,则可以是已发送、待发送或已读取状态。
为了维护所有这些状态,我们将它们的标志包装在ChatDataSource中,并在组件中进行条件判断。
完成!这就是我们创建Home Screen和ChatList Screen的过程。
GitHub代码库中还包含Status和Calls标签的屏幕,它们如下所示:
light theme主题样式如下
GitHub
https://github.com/aqua30/WhatsAppDesign-Compose