KMP中的资源处理(字符串,图片等)

前言

以安卓开发者的视角,资源有很多种类,不过常用的是这几种

KMP中的资源处理(字符串,图片等)_第1张图片

而KMP中的UI一般用Compose

其中的anim,layout,colors,themes都使用代码的形式实现

KMP中的资源处理(字符串,图片等)_第2张图片

而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

你可能感兴趣的:(1024程序员节)