moko-resources
Библиотека moko-resources — это решение для доступа к ресурсам (строки, изображения, цвета, шрифты, файлы) из общего Kotlin Multiplatform кода. Поддерживает Android, iOS, macOS, JVM, JS/Wasm и Compose Multiplatform.
Возможности
- Строки и плюралы с поддержкой локализации — общий код для всех платформ
- StringDesc — lifecycle-safe контейнер для строк, позволяющий отложить получение строки до момента, когда доступен platform context
- Цвета с поддержкой light/dark темы
- Изображения (SVG, PNG, JPG) с light/dark режимом
- Шрифты (TTF, OTF)
- Файлы (raw/assets) для Android
- Compose Multiplatform — все ресурсы доступны как
painterResource,stringResource,colorResourceи т.д.
Подключение
// root build.gradle.kts
buildscript {
dependencies {
classpath("dev.icerock.moko:resources-generator:0.26.4")
}
}
// shared/build.gradle.kts
plugins {
kotlin("multiplatform")
id("dev.icerock.mobile.multiplatform-resources")
}
dependencies {
commonMainApi("dev.icerock.moko:resources:0.26.4")
// для Compose Multiplatform:
commonMainApi("dev.icerock.moko:resources-compose:0.26.4")
commonTestImplementation("dev.icerock.moko:resources-test:0.26.4")
}
multiplatformResources {
resourcesPackage.set("com.example.app") // обязательный package
resourcesClassName.set("SharedRes") // опционально, по умолчанию MR
resourcesVisibility.set(MRVisibility.Internal)
}
Использование
Вот несколько примеров, подробнее смотрите в moko-resources
Строки (strings.xml)
<!-- base/strings.xml -->
<resources>
<string name="hello">Hello</string>
<string name="format">Test data %d</string>
<string name="positional">second string %2$s first decimal %1$d</string>
</resources>
// commonMain — получение StringDesc
val hello: StringDesc = MR.strings.hello.desc()
val formatted: StringDesc = MR.strings.format.format(9)
val positional: StringDesc = MR.strings.positional.format(9, "str")
// Android — преобразование в String
val text: String = hello.toString(context = this)
// iOS
let text: String = hello.localized()
Изображения
PNG/JPG имена файлов должны содержать суффикс плотности (@1x, @2x, @3x, @4x).
val image: ImageResource = MR.images.home_black_18
val vector: ImageResource = MR.images.car_black // SVG
// Android
imageView.setImageResource(image.drawableResId)
// iOS
imageView.image = image.toUIImage()
// Compose
Image(painter = painterResource(MR.images.car_black), contentDescription = null)
Поддержка Dark Mode: добавьте -dark к имени файла:
car.svgcar-dark.svg
Файлы и ассеты
// Чтение текстового файла
val text: String = MR.files.test_txt.readText() // common
val text: String = MR.files.test_txt.getText(context) // Android
// Compose — реактивное чтение
val content: String? by MR.files.test_txt.readTextAsState()
Compose Multiplatform
Если подключён модуль resources-compose, все ресурсы доступны напрямую в commonMain:
// Строки
Text(text = stringResource(MR.strings.hello_world))
// Плюралы
Text(text = pluralStringResource(MR.plurals.chars_count, counter, counter))
// Цвета
Text(color = colorResource(MR.colors.textColor), text = "Hello")
// Изображения
Image(painter = painterResource(MR.images.moko_logo), contentDescription = null)
// Шрифты
Text(fontFamily = fontFamilyResource(MR.fonts.cormorant_italic), text = "Hello")
// Файлы
val fileContent: String? by MR.files.some_file_txt.readTextAsState()
Multi-module проекты
Если ресурсы лежат в отдельном модуле, плагин нужно применить и в модуле с ресурсами, и в модуле, который собирается в framework.
Для вложенного модуля можно задать кастомное имя класса:
multiplatformResources {
resourcesPackage.set("com.example.nested")
resourcesClassName.set("NestedMR")
}
Почему StringDesc не Parcelable
StringDesc не может быть Parcelable, так как у нас есть ResourceFormattedStringDesc:
actual data class ResourceFormattedStringDesc actual constructor(
val stringRes: StringResource,
val args: List<Any>
) : StringDesc {
override fun toString(context: Context): String {
@Suppress("SpreadOperator")
return Utils.resourcesForContext(context).getString(
stringRes.resourceId,
*Utils.processArgs(args, context)
)
}
}
Так как его аргументы типа Any, класс ResourceFormattedStringDesc не может быть Parcelable, а значит и StringDesc не может быть Parcelable.