【Jetpack Compose】RTL?暗黑模式?这也太简单了吧!

背景

还在为Dark ModeRTL Mode所苦恼吗?快来试试Jetpack Compose!一键切换,提升幸福指数。一起来学习酷炫的Jetpack Compose

预览

070113072987_0device-2021-07-01-124450.png

设计

TitleBar

经典三段式,Back IconSetting IconTitle Text,由settingDialogState来记录设置弹窗的状态:

  TopAppBar(
        backgroundColor = MaterialTheme.colors.background,
        contentPadding = PaddingValues(start = 18.dp, end = 18.dp)
    ) {
        Icon(
            painter = rememberVectorPainter(image = Icons.Default.ArrowBack),
            contentDescription = "back",
            modifier = Modifier.size(24.dp)
        )
        Text(
            text = "橱窗列表",
            fontSize = TextUnit(17f, TextUnitType.Sp),
            fontWeight = FontWeight(700),
            textAlign = TextAlign.Center,
            modifier = Modifier.weight(1f)
        )
        Icon(
            painter = rememberVectorPainter(image = Icons.Default.Settings),
            contentDescription = "setting",
            modifier = Modifier
                .size(24.dp)
                .clickable {
                    settingDialogState.value = true
                }
        )
    }
复制代码
预览效果
image.png

商品

图片文字组合在一起,不是特别复杂的布局使用RowColumn混搭即可:

Row(modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 16.dp)) {
        Image(
            painter = rememberGlidePainter(product.url),
            contentDescription = "product image",
            contentScale = ContentScale.FillBounds,
            modifier = Modifier
                .size(108.dp, 108.dp)
                .clip(RoundedCornerShape(2.dp)),
        )
        Column(
            modifier = Modifier
                .padding(start = 12.dp)
                .requiredHeight(108.dp)
                .fillMaxWidth()
        ) {
            Text(
                text = product.name,
                fontSize = TextUnit(15f, TextUnitType.Sp),
                fontWeight = FontWeight(500),
                maxLines = 2
            )
            Text(
                text = product.price,
                fontWeight = FontWeight(600),
                fontSize = TextUnit(15f, TextUnitType.Sp),
                modifier = Modifier.padding(top = 4.dp)
            )
            Text(
                text = product.origin,
                fontWeight = FontWeight(300),
                fontSize = TextUnit(13f, TextUnitType.Sp),
                modifier = Modifier.padding(top = 4.dp)
            )
            Spacer(modifier = Modifier.weight(1f))

            RedButton(
                text = if (product.added) "Added" else "Add",
                modifier = Modifier
                    .align(Alignment.End)
                    .clickable {
                        if (!product.added) {
                            product.added = true
                            addBtnClick(product.id)
                        }
                    },
                isDark = isDarkMode,
                isAdded = product.added
            )
        }
    }
复制代码
预览效果
image.png

关键的来了,这个按钮有两种状态:AddAdded:

Add Added
image.png image.png

这个在数据里应该如何表示呢?我们知道Jetpack Compose的UI是由State驱动的,因此我们也要使用State去定义这个字段:

data class ProductBean(
    val id: Int,
    val url: String,
    val name: String,
    val price: String,
    val origin: String
) {
    var added by mutableStateOf(false)
}
复制代码

这样则在added字段变更时修改按钮颜色。

字段定义好之后又该如何设计UI呢?按钮仅有两个部分,背景和文字,那么只需要分别控制它们的颜色即可,但又要考虑到暗黑模式,所以算是有4种样式:

class RedButtonColorDelegate(private val isDark: Boolean, private val isAdded: Boolean) {
    val btnColor: Color = if (isAdded) {
        btnColorAdded
    } else {
        btnColorAdd
    }
    val textColor: Color = if (isAdded) {
        textColorAdded
    } else {
        textColorAdd
    }


    private val btnColorAdd: Color
        get() = Color(0xFFFE2C55)
        

    private val textColorAdd: Color
        get() = Color.White


    //added
    private val btnColorAdded: Color
        get() =
            if (isDark) {
                Color(0x14FFFFFF)
            } else {
                Color(0xF161823)
            }

    private val textColorAdded: Color
        get() =
            if (isDark) {
                Color(0x57FFFFFF)
            } else {
                Color(0x57161823)
            }
}
复制代码

定义好颜色之后我们只需要根据状态取颜色即可:

@Composable
fun RedButton(
    modifier: Modifier = Modifier,
    text: String,
    isAdded: Boolean = false,
    isDark: Boolean = false
) {
    Box(
        modifier = modifier
            .size(height = 28.dp, width = 88.dp)
            .clip(RoundedCornerShape(2.dp))
            .background(
                color = RedButtonColorDelegate(
                    isDark = isDark,
                    isAdded = isAdded
                ).btnColor
            )
    ) {
        Text(
            text = text,
            color = RedButtonColorDelegate(
                isDark = isDark,
                isAdded = isAdded
            ).textColor,
            fontSize = TextUnit(14f, TextUnitType.Sp),
            fontWeight = FontWeight(600),
            textAlign = TextAlign.Center,
            modifier = Modifier
                .align(Alignment.Center)
                .padding(start = 8.dp, end = 8.dp)
        )
    }
}

复制代码

长列表

Jetpack Compose长列表实在是太简单啦,再也不用写Adapter啦:

LazyColumn(modifier = Modifier
                        .weight(1f)
                        .fillSize()) {
        items(items = products, key = { it.id }) {
                ProductItem(product = it, isDarkMode =false, productClick)
             }
     }
复制代码

弹窗

Jetpack Compose里我们可以使用ModalBottomSheetLayout来实现底部弹窗,非常简单:

 val scope = rememberCoroutineScope()
 
 val addMenuState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)

 ModalBottomSheetLayout(
                sheetState = addMenuState,
                sheetShape = RoundedCornerShape(topStart = 8.dp, topEnd = 8.dp),
                sheetContent = {
                //弹窗内容
                    AddMenu { scope.launch { addMenuState.hide() } }
                }
            ) {
            
            ...
          
          }
复制代码
预览效果
image.png

DarkMode

使用Jetpack Compose切换Dark Mode更是简单不过,我们可以使用MaterialDesign提供的MaterialTheme,也可以完全自定义。

//定义颜色

private val DarkColorPalette = darkColors(
    primary = Purple200,
    primaryVariant = Purple700,
    secondary = Teal200
)

private val LightColorPalette = lightColors(
    primary = Purple500,
    primaryVariant = Purple700,
    secondary = Teal200
}

//定义主题
@Composable
fun ManageShowCaseComposeTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable() () -> Unit
) {
    val colors = if (darkTheme) {
        DarkColorPalette
    } else {
        LightColorPalette
    }

    MaterialTheme(
        colors = colors,
        typography = Typography,
        shapes = Shapes,
        content = content
    )
}

//使用此主题:

ManageShowCaseComposeTheme(darkTheme = viewModel.darkModeState.value){
      TitileBar()
       ...
}

复制代码
标题
image.png

RTL

有些产品面向阿拉伯地区,需要RTL Mode,这个功能Compose也为我们准备好了:
我们只需要在我们需要RTL的视图外层使用 CompositionLocalProvider即可,这里简单理解为这个一个依赖注入能力,此视图之下的子Compose拿对应的数据,比如这里是LocalLayoutDirection,都是同一个实例。布局时会根据LocalLayoutDirection来进行排版。


 CompositionLocalProvider(LocalLayoutDirection provides if (rtlState.value) LayoutDirection.Rtl else LayoutDirection.Ltr) {
 
   TitileBar()
       ...
   
   
 }
复制代码

注意:目前LocalLayoutDirection仅是对布局进行翻转,对组件内容并没镜像,如果想要图片翻转可能还需要进行一些工作。

预览
image.png

总结

是不是非常简单?Jetpack Compose即将发布Release版本,赶紧学习起来~~

代码示例已传至Github: github.com/Palardin3/C…

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享