Compose中的国际化与本地化、暗黑模式与夜间模式

2022-08-25  本文已影响0人  _Jun

开篇闲谈

这两年负责的都是面向海外(欧美、中东等)的项目,之前在View的时代下总结了一套国际化与本地化的经验,见《Android 国际化与本地化探索》,文中事无巨细的从 语言翻译UI设计代码规范 三个方面阐述了我的解决方案。

切换到到Compose后,又完全处理了一遍国际化的流程。同时发现在适配暗黑模式中Compose提供了开箱即用的支持,大大简化了我们的开发难度,这篇文章就将经验分享给大家。文中若有纰漏之处,还望大家不吝赐教。

国际化与本地化

关于国际化中的翻译规范以及UI设计规范,这里就不再赘述了,大家可以翻翻开篇提到的文章,我们直接从代码层面入手。

文字处理

如上图所示,我们需要实现简体中文和阿拉伯语的相关页面。

首先我们需要准备相关的语言资源文件,然后消息列表的整体页面只是使用了Row和Column来构建,没有其他多余操作,代码不做过多展示,预览代码如下所示:

@Preview(locale = "zh")
@Composable
fun PreviewMessageListScreen() {
    MessageListScreen()
}

@Preview(locale = "ar")
@Composable
fun PreviewMessageListScreenRTL() {
    MessageListScreen()
}

可以看到我们只是给Preview注解添加了local参数,预览RTL的预览效果就完全显示出来了,而且运行在手机上的话,是会根据手机系统设置的语言以及布局方向来进行展示的。

再来看这样一种场景,在View体系下,例如阿拉伯语环境下有中文的时候,如果不对TextView的gravity、textAlignment属性进行合适的处理,那么情况可能就会出现下图左侧的样子。

按道理来说,文本内容应该是贴着图标一侧的,可是在切换了RTL语言后,中文文本内容却还是位于屏幕的左侧,这是由于在View体系下,Gravity和TextAlignment都会控制文本的布局方向。而到了Compose中,Text布局方向则是默认听从父布局容器的安排,自己不会做任何多余的处理。所以在Compose中按照正常写法开发下来,效果图就是如上右侧图片所示,完全不用做过多的设置。

所以可能一开始大家在处理Compose中Text居中等情况下都遇到了难题,到底要如何居中呢?其实就是需要在外层嵌套一个Row或者Box等布局,然后将布局方向改为居中就可以了,或者使用其参数TextAlign也可以。

图标处理

上文是基本的布局处理,涉及到一些图片的处理,我们还是使用在View体系下的方案,利用缩放来处理图片的左右翻转,自定义Modifier代码如下所示:

@Composable
fun Modifier.rtl(): Modifier = composed {
    val layoutDirection = LocalLayoutDirection.current
    scale(
        scale = if (layoutDirection == LayoutDirection.Rtl) {
            -1f
        } else {
            1f
        }
    )
}

如果布局方向是RTL的那么将图片进行左右镜像,我们将下图的返回按钮图标应用上述的自定义Modifier,那么切换到RTL布局后,返回按钮图标的方向就会产生镜像变化。而另一个图标没有应用该Modifier,那么它的方向就不会发生变化。

暗黑模式与夜间模式

关于暗黑模式与夜间模式其实还是有区别的:

针对上述示例图,我们只能说基本实现了暗黑模式,如果再针对图片等整体进一步的处理,降低亮度、减少对比度,减少对用户眼睛的刺激,那么这才能称的上是一个更加提升用户在晚上使用体验的夜间模式。

文字颜色及背景色的处理

接下来我们主要说下暗黑模式在Compose中的实现,但是并不使用Compose官方提供的Material Theme,而是使用CompositonLocal重新自定义一套类似的颜色主题。如下颜色数据类所示,我们大致需要一个主文本颜色,次文本颜色以及背景颜色,分别设置Light、Dark Model来实现上方图片所示效果。

data class MyColor(
    val textPrimary: Color,
    val textSecondary: Color,
    val background: Color
)

//Light Mode
val myLightColors = MyColor(
    textPrimary = Color(0xFF333333),
    textSecondary = Color(0xFF666666),
    background = Color(0xFFFFFFFF)
)

//Dark Mode
val myDarkColors = MyColor(
    textPrimary = Color(0xFFFFFFFF),
    textSecondary = Color(0xCCFFFFFF),
    background = Color(0xFF333333)
)

因为颜色主题的值是确定的,不会发生更改,所以我们使用staticCompositionLocalOf来创建CompositionLocal,以此来提高性能。

然后创建单例模式的主题,在后续的关于颜色的使用中,我们都需要使用该主题所提供的颜色。

//创建CompositionLocal
val LocalMyColors = staticCompositionLocalOf {
    myLightColors
}

//创建自己的主题
object MyTheme {
    val colors: MyColor
        @Composable
        get() = LocalMyColors.current
}

@Composable
fun ComposeSampleTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit,
) {

    val myColors = if (darkTheme) {
        myDarkColors
    } else {
        myLightColors
    }

    CompositionLocalProvider(
        LocalMyColors provides myColors
    ) {
        MaterialTheme(
            colors = colors,
            typography = Typography,
            shapes = Shapes,
            content = content
        )
    }
}

最后我们需要使用CompositionLocalProvider来将我们的主题颜色提供出去,默认是Light Mode,这样的话在ComposeSampleTheme下的可组合函数都可以有效的使用我们自定义的颜色信息了。


//使用自定义的主题颜色--MyTheme.colors.textPrimary
Text(
    text = title,
    fontSize = 16.sp,
    color = MyTheme.colors.textPrimary,
    fontWeight = FontWeight.Bold,
    maxLines = 1,
    overflow = TextOverflow.Ellipsis,
)

预览的时候则传递是否使用Dark Mode的参数即可:

@Preview()
@Composable
fun PreviewMessageListScreenDark() {
    ComposeSampleTheme(darkTheme = true) {
        MessageListScreen()
    }
}

图标颜色处理

如下图所示,在Light及Dark模式下,图标的颜色是需要做区别处理的。如果根据不同的模式,分别使用不同的图片资源,这是一种解决方案,但是使用多套资源文件会造成APP体积的增大。

所以我们可以考虑,当使用Dark模式时,使用ColorFilter来改变图标的颜色,这样既不会增加体积,也方便我们处理各种不同的颜色,代码如下所示:

@Composable
fun ThemeColorFilter(
    color: Color? = null
): ColorFilter? {
    val isDarkTheme = isSystemInDarkTheme()

    if (isDarkTheme) {
        return ColorFilter.tint(color = Color(0xCCFFFFFF))
    }

    return if (color == null) {
        null
    } else {
        ColorFilter.tint(color = color)
    }
}

当处于暗黑模式时,我们就给图标使用灰白色的色值,否则就不使用滤色器或者使用自定义的图标色值。

至此基本的暗黑模式框架已经完成了,后续就需要根据自己App的情况添加不同的颜色数据来一步步完善。

总结

Compose作为现代化的UI工具包,也吸取了View时代下的各种开发经验,在处理国际化及暗黑模式中着实方便很多。

作者:乐翁龙
链接:https://juejin.cn/post/7064406092658245645

上一篇下一篇

猜你喜欢

热点阅读