本文同步发表于我的微信公众号,在微信搜索
OpenCV or Android即可关注。
前言
第三周挑战赛是速度比拼,按照官方发出的设计图最快完成且符合所有设计规范者胜出。不仅要做得快,还要做得好,奖品自然不会少。这一期的奖品是:Google Pixel 5。深知干不过业界大佬们,花个半天纯当练手完成题目。
设计图
设计风格

界面导航

界面标注

完整设计图:https://github.com/android/android-dev-challenge-compose/blob/assets/Bloom.zip
知识点
- 主题:自定义主题
- 列表:LazyColumn、LazyRow
- 文字:文本输入框、风格化文本、自定义字体
- 导航:基础使用、底部导航栏集成
关键实现
定义主题
界面布局中使用主题元素配置color、shape、typography等内容。localImages、localElevations为自定义内容。
@Composable fun BloomTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable() () -> Unit) { val colors = if (darkTheme) { DarkColorPalette } else { LightColorPalette } val images = if (darkTheme) DarkImages else LightImages val elevations = AllElevations CompositionLocalProvider( localImages provides images, localElevations provides elevations ) { MaterialTheme( colors = colors, typography = typography, shapes = shapes, content = content ) } } object BloomTheme { val colors: Colors @Composable get() = MaterialTheme.colors val typography: Typography @Composable get() = MaterialTheme.typography val shapes: Shapes @Composable get() = MaterialTheme.shapes val images: Images @Composable get() = localImages.current val elevations: Elevations @Composable get() = localElevations.current }
@Immutable data class Images( @DrawableRes val welcomeBackground: Int, @DrawableRes val welcomeIllos: Int, @DrawableRes val welcomeLogo: Int ) internal val localImages = staticCompositionLocalOf<Images> { error("No LocalImages specified") }
风格化文本
val termsString = buildTermsString( stringResource(R.string.login_terms), listOf( stringResource(R.string.terms), stringResource(R.string.privancy) ) ) Text( text = termsString, textAlign = TextAlign.Center, modifier = Modifier.paddingFromBaseline( top = 24.dp, bottom = 16.dp ) )
fun buildTermsString(source: String, segments: List<String>) = buildAnnotatedString { append(source) for (segment in segments) { val startIndex = source.indexOf(segment) val endIndex = startIndex + segment.length addStyle( style = SpanStyle(textDecoration = TextDecoration.Underline), start = startIndex, end = endIndex ) } }
带边框密码文本输入
TextField( modifier = Modifier .height(56.dp) .fillMaxWidth() .border( // 设置边框 width = 1.dp, color = Color(0xFF9E9E9E), shape = BloomTheme.shapes.small ), value = password, onValueChange = { password = it }, singleLine = true, placeholder = { Text( text = stringResource(R.string.password_hint), style = BloomTheme.typography.body1, ) }, textStyle = BloomTheme.typography.body1, colors = TextFieldDefaults.textFieldColors( backgroundColor = Color.Transparent, textColor = BloomTheme.colors.onPrimary ), visualTransformation = PasswordVisualTransformation(), // 密码形式 keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password) // 密码安全键盘 )
底部导航栏
sealed class Screen( val route: String, @StringRes val resourceId: Int, val icon: ImageVector ) { object Home : Screen("home", R.string.home, Icons.Filled.Home) object Favorites : Screen("favorite", R.string.favorites, Icons.Filled.FavoriteBorder) object Profile : Screen("profile", R.string.profile, Icons.Filled.AccountCircle) object Cart : Screen("cart", R.string.cart, Icons.Filled.ShoppingCart) } val items = listOf( Screen.Home, Screen.Favorites, Screen.Profile, Screen.Cart ) @Composable fun Main() { val navController = rememberNavController() Scaffold( bottomBar = { BottomNavigation( modifier = Modifier .navigationBarsPadding() .height(56.dp), backgroundColor = MaterialTheme.colors.primary, contentColor = MaterialTheme.colors.onPrimary, elevation = BloomTheme.elevations.bottomNavigation ) { val navBackStackEntry by navController.currentBackStackEntryAsState() val currentRoute = navBackStackEntry?.arguments?.getString(KEY_ROUTE) items.forEach { screen -> BottomNavigationItem( selected = currentRoute == screen.route, onClick = { navController.navigate(screen.route) { popUpTo = navController.graph.startDestination launchSingleTop = true } }, icon = { Icon( imageVector = screen.icon, contentDescription = stringResource(id = screen.resourceId), modifier = Modifier.size(24.dp, 24.dp), tint = MaterialTheme.colors.onPrimary ) }, label = { Text( stringResource(id = screen.resourceId), style = MaterialTheme.typography.caption ) } ) } } } ) { NavHost(navController, startDestination = Screen.Home.route) { composable(Screen.Home.route) { Home() } composable(Screen.Favorites.route) { Favorites() } composable(Screen.Profile.route) { Profile() } composable(Screen.Cart.route) { Cart() } } } }
效果
Welcome

Login

Home

