以安卓开发者的视角,资源有很多种类,不过常用的是这几种
而KMP中的UI一般用Compose
其中的anim,layout,colors,themes都使用代码的形式实现
而KMP中目前貌似没有通用的字符串和图片资源管理和获取的方式,于是我们自己实现一下
字符串的实现我的实现很简单,就是直接用一个单例类来记录各种字符串资源
大概实现是这样:
- /**
- * creator: lt
- * effect : 字符串资源
- * warning:
- */
- object Strings {
- val separator: String
- //单词间的分隔符
- get() {
- return when (LanguageManager.currLanguage) {
- EN -> " "
- ZH -> ""
- }
- }
- val respose_error: String
- get() {
- return when (LanguageManager.currLanguage) {
- EN -> "Server error, please try again later!"
- ZH -> "服务器错误,请稍后再试!"
- }
- }
- }
其中 LanguageManager.currLanguage 是一个自定义的state,所以文本是可以响应式的根据语言改变app内的文本的(ps:可以这么做,不过目前我是用的是隐式参数的方式)
使用方式为:
Text(Strings.respose_error)
简单方便
图片资源我们也可以像字符串资源一样使用代码的形式来实现
实现方式:
- object Painters {
- val select: Painter
- @Composable
- get() = PainterGenerator.generate("select", "")
- val back: Painter
- @Composable
- get() {
- return when (LanguageManager.currLanguage) {
- EN -> PainterGenerator.generate("back", "")
- ZH -> PainterGenerator.generate("back", "zh")
- }
- }
- }
select是单语言时的形式,back是有多种语言时的形式,同样可以响应式的根据语言自动切换图片
使用方式:
Image(Painters.back,null)
同样很简单
然后我们看一下 PainterGenerator.generate 是怎么实现的
common:
- /**
- * creator: lt
- * effect : Painter生成器,用于生成静态的图片资源
- * warning:
- */
- expect object PainterGenerator {
- /**
- * 生成Painter
- * drawable-zh-xxhdpi/ic_launcher.webp
- * [imageName]文件名: ic_launcher
- * [languageName]资源的语言: zh 如果zh目录中没有,则需传空字符串(要保证传的资源的语言的目录中要有这个图片才行)
- */
- @Composable
- fun generate(
- imageName: String,
- languageName: String,
- ): Painter
- }
android:
- actual object PainterGenerator {
- private var currLocale =
- private val androidResource =
- Resources(
- app.assets,
- app.resources.displayMetrics,
- Configuration(app.resources.configuration)
- )
-
- /**
- * 生成Painter
- * drawable-zh-xxhdpi/ic_launcher.webp
- * [imageName]文件名: ic_launcher
- * [languageName]资源的语言: zh
- */
- @Composable
- actual fun generate(
- imageName: String,
- languageName: String
- ): Painter = remember(imageName, languageName) {
- //如果没有指定语言,就是用默认语言的图片,如果指定了语言,就设置一下资源的语言
- if (languageName.isEmpty())
- androidResource.configuration.setLocale(currLocale)
- else
- androidResource.configuration.setLocale(Locale(languageName))
- //获取drawable的id
- val id = ExceptionUtil.releaseCatchThrowable({
- androidResource.getIdentifier(
- imageName, "drawable", app.packageName
- )
- }) ?: R.drawable.load_error
- //根据id,使用带有语言设置的资源来加载
- BitmapPainter(
- androidResource.getDrawable(id).asT
().bitmap.asImageBitmap() - )
- }
- }
安卓的实现复杂一些,其实就是通过 resource 对象的 getIdentifier 方法,然后设置相应的语言并通过文件夹名和文件名来找到相应的资源id,并加载,然后将bitmap转换为相应资源
ps:使用此方式安卓不能混淆资源名和混淆移除无用的resource文件
desktop 和 iOS:
- actual object PainterGenerator {
- /**
- * 生成Painter
- * drawable-zh-xxhdpi/ic_launcher.webp
- * [imageName]文件名: ic_launcher
- * [languageName]资源的语言: zh
- */
- @Composable
- actual fun generate(
- imageName: String,
- languageName: String
- ): Painter = painterResource(remember(imageName, languageName) {
- "drawable%s-xxhdpi/%s.webp".format(
- if (languageName.isEmpty()) "" else "-$languageName",
- imageName
- )
- })
- }
desktop和iOS没啥说的,其实就是用了自带的api,内部是通过classLoder(desktop)加载的资源
js:
- actual object PainterGenerator {
- /**
- * 生成Painter
- * drawable-zh-xxhdpi/ic_launcher.webp
- * [imageName]文件名: ic_launcher
- * [languageName]资源的语言: zh
- */
- @OptIn(ExperimentalResourceApi::class)
- @Composable
- actual fun generate(
- imageName: String,
- languageName: String
- ): Painter {
- val state: MutableState
> = - remember(imageName, languageName) { mutableStateOf(LoadState.Loading()) }
- LaunchedSafeEffect(imageName, languageName) {
- state.value = try {
- LoadState.Success(
- resource(
- "drawable%s-xxhdpi/%s.webp".format(
- if (languageName.isEmpty()) "" else "-$languageName",
- imageName
- )
- ).readBytes()
- .toImageBitmap()
- )
- } catch (e: Exception) {
- e.w()
- LoadState.Error(e)
- }
- }
- return BitmapPainter(state.value.orEmpty())
- }
- }
js的实现也是使用了自带的api,不过是异步的,因为内部是走的网络请求加载的资源
然后我们可以通过配置资源目录,将每个端的资源目录指向一个目录
这样就实现了图片资源管理的功能
ps:其实图片资源的类我们完全可以通过ksp去生成,不过这不是这篇文章的重点,感兴趣的同学可以自己动手试试,参考:使用KSP处理注解和生成Kotlin代码
end
对Kotlin或KMP感兴趣的同学可以进Q群 101786950
如果这篇文章对您有帮助的话
可以扫码请我喝瓶饮料或咖啡(如果对什么比较感兴趣可以在备注里写出来)