Kotlin Multiplatform Development Help

在应用中使用多平台资源

[//]: # (title: 在应用中使用多平台资源) <show-structure depth="2"/> 当您[为项目设置好资源](compose-multiplatform-resources-setup.md)后,构建项目以生成特殊的`Res`类,该类提供对资源的访问权限。如需重新生成`Res`类及所有资源访问器,请再次构建项目或在IDE中重新导入项目。 之后,您可以通过生成的类从代码或外部库访问配置好的多平台资源。 ## 导入生成的类 要使用已准备的资源,请导入生成的类,例如: ```kotlin import project.composeapp.generated.resources.Res import project.composeapp.generated.resources.example_image

说明:

  • project 是您的项目名称

  • composeapp 是存放资源目录的模块

  • Res 是生成类的默认名称

  • example_imagecomposeResources/drawable目录中的图片文件名(例如example_image.png)。

自定义访问器类生成

您可以通过Gradle设置自定义生成的Res类以满足需求。

build.gradle.kts文件的compose.resources {}块中,可指定影响项目生成Res类方式的若干设置。示例配置如下:

compose.resources { publicResClass = false packageOfResClass = "me.sample.library.resources" generateResClass = auto }
  • publicResClass设为true会使生成的Res类变为公开。默认情况下,生成类是internal的。

  • packageOfResClass允许将生成的Res类分配到特定包(便于代码访问及最终产物隔离)。默认情况下,Compose Multiplatform会为类分配{group name}.{module name}.generated.resources包。

  • generateResClass设为always会强制项目无条件生成Res类。当资源库仅以传递依赖形式存在时,这可能有用。默认情况下,Compose Multiplatform使用auto值,仅在当前项目对资源库有显式implementationapi依赖时才生成Res类。

资源使用

图片

您可以将drawable资源作为简单图片、栅格化图片或XML矢量图访问。SVG图片在除Android外的所有平台受支持。

  • 要将drawable资源作为Painter图片访问,使用painterResource()函数:

    @Composable fun painterResource(resource: DrawableResource): Painter {...}

    该函数同步工作于除web外的所有目标平台。对于web目标,首次重组时返回空Painter ,后续重组时替换为加载的图片。

  • 要将drawable资源作为ImageBitmap栅格化图片访问,使用imageResource()函数:

    @Composable fun imageResource(resource: DrawableResource): ImageBitmap {...}
  • 要将drawable资源作为ImageVectorXML矢量图访问,使用vectorResource()函数:

    @Composable fun vectorResource(resource: DrawableResource): ImageVector {...}

示例代码:

Image( painter = painterResource(Res.drawable.my_icon), contentDescription = null )

字符串

将所有字符串资源存储在composeResources/values目录的XML文件中。每个文件的每个条目都会生成静态访问器。

简单字符串

存储简单字符串:

<resources> <string name="app_name">我的超赞应用</string> <string name="title">某个标题</string> </resources>

获取字符串资源:

Text(stringResource(Res.string.app_name))
coroutineScope.launch { val appName = getString(Res.string.app_name) }

字符串中可使用特殊符号如\n (换行)、 \t (制表符)和\uXXXX (Unicode字符),无需像Android字符串那样转义"@"或"?"。

字符串模板

当前支持基础的字符串模板参数。创建模板时使用%<数字>格式,并用$d$s后缀表示变量占位符。例如:

<string name="str_template">你好,%2$s!你有%1$d条新消息。</string>

使用示例:

Text(stringResource(Res.string.str_template, 100, "用户名"))

字符串数组

将相关字符串分组为数组:

<string-array name="str_arr"> <item>项目★</item> <item>项目⌘</item> <item>项目½</item> </string-array>

获取列表:

Text(stringArrayResource(Res.array.str_arr)[0])
coroutineScope.launch { val appName = getStringArray(Res.array.str_arr) }

复数形式

当UI需要显示数量时,可使用复数形式支持不同数量的语法变化(如1本书vs多本书)。实现概念与Android数量字符串相同。

定义复数:

<plurals name="new_message"> <item quantity="one">%1$d条新消息</item> <item quantity="other">%1$d条新消息</item> </plurals>

使用示例:

Text(pluralStringResource(Res.plurals.new_message, 1, 1))
coroutineScope.launch { val appName = getPluralString(Res.plurals.new_message, 1, 1) }

字体

将自定义字体(*.ttf*.otf )存储在composeResources/font目录中。加载示例:

val fontAwesome = FontFamily(Font(Res.font.font_awesome))

原始文件

要加载任何原始文件为字节数组,使用Res.readBytes(path)函数。文件可放置在composeResources/files目录及其子目录中。

var bytes by remember { mutableStateOf(ByteArray(0)) } LaunchedEffect(Unit) { bytes = Res.readBytes("files/myDir/someFile.bin") } Text(bytes.decodeToString())
coroutineScope.launch { val bytes = Res.readBytes("files/myDir/someFile.bin") }

将字节数组转换为图片

若文件是位图或XML矢量图,可使用以下函数转换:

// bytes = Res.readBytes("files/example.png") Image(bytes.decodeToImageBitmap(), null) // bytes = Res.readBytes("files/example.xml") Image(bytes.decodeToImageVector(LocalDensity.current), null)

非Android平台还可将SVG转为Painter对象:

Image(bytes.decodeToSvgPainter(LocalDensity.current), null)

资源与字符串ID的生成映射

为便于访问,Compose Multiplatform还提供了资源与字符串ID的映射:

Image(painterResource(Res.allDrawableResources["compose_multiplatform"]!!), null)

作为Android资产的Compose Multiplatform资源

从Compose Multiplatform 1.7.3开始,所有多平台资源都打包为Android资产。这使得Android Studio能为Android源集中的Compose Multiplatform可组合项生成预览。

示例展示资源HTML页面:

// androidMain/kotlin/com/example/webview/App.kt @OptIn(ExperimentalResourceApi::class) @Composable @Preview fun App() { MaterialTheme { val uri = Res.getUri("files/webview/index.html") AndroidView(factory = { WebView(it).apply { layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) } }, update = { it.loadUrl(uri) }) } }

Web目标的资源预加载

网络资源(如字体和图片)通过fetch API异步加载。初始加载或网络较慢时可能导致视觉问题(如FOUT或显示占位符)。

使用浏览器特性预加载

现代浏览器中可通过<link rel="preload">属性预加载资源。示例:

<link rel="preload" href="./composeResources/username.composeapp.generated.resources/font/FiraMono-Regular.ttf" as="fetch" type="font/ttf" crossorigin/>

使用Compose Multiplatform预加载API

Compose Multiplatform 1.8.0-beta02引入了实验性API用于预加载字体和图片资源: preloadFont()preloadImageBitmap()preloadImageVector()

示例代码:

@OptIn(ExperimentalResourceApi::class) fun main() { configureWebResources { resourcePathMapping { path -> "./$path" } } CanvasBasedWindow("Resources + K/Wasm") { val font1 by preloadFont(Res.font.Workbench_Regular) val font2 by preloadFont(Res.font.font_awesome, FontWeight.Normal, FontStyle.Normal) val emojiFont = preloadFont(Res.font.NotoColorEmoji).value // 使用预加载资源... } }

与其他库和资源的交互

从外部库访问多平台资源

要使用其他库处理多平台资源,可通过Res.getUri()获取平台特定路径:

val uri = Res.getUri("files/my_video.mp4")

远程文件

资源库仅将应用内置文件视为资源。远程文件需使用专门库(如Compose ImageLoader、Kamel或Ktor客户端)加载。

使用Java资源

虽然可在Compose Multiplatform中使用Java资源,但无法享受框架提供的扩展功能(如生成访问器、多模块支持、本地化等)。建议完全迁移到多平台资源库。

下一步?

查看官方演示项目 ,了解如何在面向iOS、Android和桌面的Compose Multiplatform项目中处理资源。

23 四月 2025