Spec-Zone.ru › Kotlin 2

Использование мультиплатформенных ресурсов в приложении

После того как вы настроили ресурсы для проекта, соберите проект, чтобы сгенерировать специальный класс Res, предоставляющий доступ к ресурсам. Чтобы повторно сгенерировать класс Res и все средства доступа к ресурсам, снова соберите проект или повторно импортируйте его в IDE.

После этого вы сможете использовать сгенерированный класс для доступа к настроенным мультиплатформенным ресурсам из кода или внешних библиотек.

Подробнее о следующих темах:

  • Импорт сгенерированного класса Res и средств доступа.

  • Настройка генерации класса средств доступа: как сделать его общедоступным, назначить ему пакет или генерировать безусловно.

  • Работа с определенными типами ресурсов:

    • Ресурсы Drawable, например обычные изображения, растровые изображения или векторная графика XML.

    • Векторные значки Android XML из библиотеки Material Symbols.

    • Строки, в том числе обычные строки, шаблоны, массивы и строки для множественного числа.

    • Хранение и загрузка пользовательских шрифтов.

    • Файлы Raw и преобразование массивов байтов в изображения.

  • Доступ к ресурсам, сопоставленным со строковыми идентификаторами.

  • Использование мультиплатформенных ресурсов в качестве ресурсов Android.

  • Работа с ресурсами, специфичными для веб-платформы:

    • Предварительная загрузка ресурсов с помощью функций браузера и API предварительной загрузки.

    • Автоматический подбор шрифта для отсутствующих символов.

    • Кэширование веб-ресурсов.

  • Работа с внешними ресурсами: из внешних библиотек, удаленными файлами и ресурсами Java.

Импорт сгенерированного класса

Чтобы использовать подготовленные ресурсы, импортируйте сгенерированный класс, например:

import project.shared.generated.resources.Res
import project.shared.generated.resources.example_image

Здесь:

  • project — имя вашего проекта

  • shared — модуль, в котором расположены каталоги ресурсов

  • Res — имя сгенерированного класса по умолчанию

  • example_image — имя файла изображения в каталоге composeResources/drawable (например, example_image.png).

Настройка генерации класса средств доступа

Вы можете настроить сгенерированный класс Res в соответствии со своими потребностями с помощью параметров Gradle.

В блоке compose.resources {} файла build.gradle.kts можно указать несколько параметров, влияющих на генерацию класса 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, чтобы генерировать класс Res, только если в текущем проекте есть явная зависимость implementation или api от библиотеки ресурсов.

Использование ресурсов

Изображения

Вы можете обращаться к ресурсам Drawable как к обычным изображениям, растровым изображениям или векторной графике XML. Изображения SVG поддерживаются на всех платформах, кроме Android.

  • Чтобы обращаться к ресурсам Drawable как к изображениям Painter, используйте функцию painterResource():

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

    Функция painterResource() принимает путь к ресурсу и возвращает значение Painter. На всех целевых платформах, кроме веб-платформы, функция работает синхронно. На веб-платформе она возвращает пустой Painter при первой рекомпозиции; в последующих рекомпозициях он заменяется загруженным изображением.

    • painterResource() загружает либо BitmapPainter для растровых форматов изображений, например .png, .jpg, .bmp, .webp, либо VectorPainter для формата векторных Drawable Android XML.

    • Векторные Drawable XML имеют тот же формат, что и Android, за исключением того, что они не поддерживают внешние ссылки на ресурсы Android.

  • Чтобы обращаться к ресурсам Drawable как к растровым изображениям ImageBitmap, используйте функцию imageResource():

    @Composable
    fun imageResource(resource: DrawableResource): ImageBitmap {...}
    
  • Чтобы обращаться к ресурсам Drawable как к векторной графике Android XML ImageVector, используйте функцию vectorResource():

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

Вот пример доступа к изображениям из кода Compose Multiplatform:

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

Отрисовка изображений на холсте

Чтобы самостоятельно нарисовать изображение из ресурса, а не отображать его с помощью компонуемого элемента Image(), загрузите его как ImageBitmap или ImageVector и используйте в DrawScope:

  • Чтобы нарисовать ImageBitmap, используйте DrawScope.drawImage().

  • Чтобы нарисовать ImageVector, создайте Painter с помощью rememberVectorPainter() и используйте Painter.draw().

Указывайте смещения и размеры в DrawScope, в пикселях:

val myImageRaster = imageResource(Res.drawable.my_image_raster)
val myImageVectorPainter = rememberVectorPainter(vectorResource(Res.drawable.my_image_vector))

Canvas(modifier = Modifier.fillMaxSize()) {
    // Inside the lambda, `this` is an instance of `DrawScope`, which provides `drawImage()` and `translate()`
    drawImage(image = myImageRaster, topLeft = Offset(20f, 20f))
    translate(left = 20f, top = myImageRaster.height + 40f) {
        // The `Painter.draw()` function uses both the `DrawScope` and `Painter` as receivers
        with(myImageVectorPainter) {
            draw(Size(200f, 200f))
        }
    }
}

Значки

Вы можете использовать векторные значки Android XML из библиотеки Material Symbols:

  1. Откройте галерею значков Google Fonts, выберите значок, перейдите на вкладку Android и нажмите Download.

  2. Добавьте загруженный файл значка XML в каталог drawable мультиплатформенных ресурсов.

  3. Откройте файл значка XML и задайте для android:fillColor значение #000000. Удалите все остальные атрибуты Android, специфичные для настройки цвета, например android:tint.

    До:

    <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="24dp"
         android:height="24dp"
         android:viewportWidth="960"
         android:viewportHeight="960"
         android:tint="?attr/colorControlNormal">
         <path
             android:fillColor="@android:color/white"
             android:pathData="..."/>
     </vector>
    

    После:

    <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="24dp"
         android:height="24dp"
         android:viewportWidth="960"
         android:viewportHeight="960">
         <path
             android:fillColor="#000000"
             android:pathData="..."/>
    </vector>
    
  4. Соберите проект, чтобы сгенерировать средства доступа к ресурсам, или позвольте плагину Kotlin Multiplatform для IDE сделать это автоматически.

Вот пример доступа к значку и настройки его цветов с помощью параметра colorFilter в коде Compose Multiplatform:

Image(
    painter = painterResource(Res.drawable.ic_sample_icon),
    contentDescription = "Sample icon",
    modifier = Modifier.size(24.dp),
    colorFilter = ColorFilter.tint(Color.Blue)
)

Строки

Храните все строковые ресурсы в XML-файлах в каталогах composeResources/values. Для каждого элемента в каждом файле генерируется статическое средство доступа.

Compose Multiplatform поддерживает сокращенный синтаксис в стиле Emmet для добавления строковых ресурсов, массивов строк и строк для множественного числа непосредственно в XML-файлы. Например, если ввести test{Example} или s.test{Example} в strings.xml и нажать Tab, запись автоматически развернется в <string name="test">Example</string>.

Чтобы узнать, как локализовать строки для разных языковых стандартов, ознакомьтесь с руководством по локализации строк.

Обычные строки

Чтобы сохранить обычную строку, добавьте в XML элемент <string>:

<resources>
    <string name="app_name">My awesome app</string>
    <string name="title">Some title</string>
</resources>

Чтобы получить строковые ресурсы в виде String, используйте следующий код:

@Composable
fun stringResource(resource: StringResource): String {...}

@Composable
fun stringResource(resource: StringResource, vararg formatArgs: Any): String {...}

Например:

Text(stringResource(Res.string.app_name))
suspend fun getString(resource: StringResource): String

suspend fun getString(resource: StringResource, vararg formatArgs: Any): String

Например:

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

В строковых ресурсах можно использовать специальные символы:

  • \n — символ новой строки

  • \t — символ табуляции

  • \uXXXX — определенный символ Unicode

Вам не нужно экранировать специальные символы XML, например «@» или «?», как это требуется для строк Android.

Используйте синтаксис в стиле Emmet и нажмите Tab, чтобы развернуть сокращение в теги строк:

  • test → <string name="test"></string>

  • test{Example} → <string name="test">Example</string>

Шаблоны строк

В настоящее время для строковых ресурсов доступна базовая поддержка аргументов. Создавая шаблон, используйте формат %<number> для размещения аргументов в строке и добавляйте суффикс $d или $s, чтобы указать, что это заполнитель переменной, а не обычный текст. Например:

<resources>
    <string name="str_template">Hello, %2$s! You have %1$d new messages.</string>
</resources>

Создав и импортировав ресурс шаблона строки, вы можете обратиться к нему, передав аргументы для заполнителей в правильном порядке:

Text(stringResource(Res.string.str_template, 100, "User_name"))

Между суффиксами $s и $d нет разницы, другие суффиксы не поддерживаются. Вы можете поместить заполнитель %1$s в строковый ресурс и использовать его для отображения дробного числа, например:

Text(stringResource(Res.string.str_template, "User_name", 100.1f))

Вместо того чтобы вручную вводить %1$s или %2$d для заполнителей, можно использовать встроенные числовые сокращения. Например, если ввести 1 или 1s в значении строки, запись развернется в %1$s. Аналогично, при вводе 2d запись развернется в %2$d.

Массивы строк

Вы можете сгруппировать связанные строки в массив и автоматически обращаться к ним как к объекту List<String>:

<resources>
    <string name="app_name">My awesome app</string>
    <string name="title">Some title</string>
    <string-array name="str_arr">
        <item>item \u2605</item>
        <item>item \u2318</item>
        <item>item \u00BD</item>
    </string-array>
</resources>

Чтобы получить соответствующий список, используйте следующий код:

@Composable
fun stringArrayResource(resource: StringArrayResource): List<String> {...}

Например:

val arr = stringArrayResource(Res.array.str_arr)
if (arr.isNotEmpty()) Text(arr[0])
suspend fun getStringArray(resource: StringArrayResource): List<String>

Например:

coroutineScope.launch {
    val appName = getStringArray(Res.array.str_arr)
}

Для быстрого создания массивов строк можно использовать синтаксис в стиле Emmet. Используйте string-array, sa или оператор > для создания пустого шаблона массива. Чтобы создать именованный массив с заданным количеством элементов и начальным текстом, введите test>2{Hello} и нажмите Tab:

<string-array name="test">
   <item>Hello</item>
   <item>Hello</item>
</string-array>

Множественное число

Если интерфейс показывает количество объектов, может понадобиться поддержка грамматического согласования для разных чисел одного и того же объекта (одна книга, много книг и так далее), чтобы не создавать программно не связанные между собой строки.

Концепция и базовая реализация в Compose Multiplatform такие же, как и для строк количества в Android. Дополнительную информацию о рекомендациях и особенностях использования множественного числа в проекте см. в документации Android.

  • Поддерживаются варианты zero, one, two, few, many и other. Обратите внимание: не для каждого языка учитываются все варианты. Например, zero игнорируется в английском языке, поскольку не отличается от других форм множественного числа, кроме формы для числа 1. Чтобы определить, какие различия действительно важны для языка, обратитесь к специалисту по языку.

  • Часто можно обойтись без строк количества, используя нейтральные формулировки, например «Книги: 1», если это не ухудшает взаимодействие с пользователем.

Чтобы определить множественное число, добавьте элемент <plurals> в любой файл .xml каталога composeResources/values. Набор plurals — это простой ресурс, на который ссылаются по атрибуту name (а не по имени XML-файла). Поэтому ресурсы plurals можно объединять с другими простыми ресурсами в одном XML-файле внутри одного элемента <resources>:

<resources>
    <string name="app_name">My awesome app</string>
    <string name="title">Some title</string>
    <plurals name="new_message">
        <item quantity="one">%1$d new message</item>
        <item quantity="other">%1$d new messages</item>
    </plurals>
</resources>

Чтобы обратиться к форме множественного числа как к String, используйте следующий код:

@Composable
fun pluralStringResource(resource: PluralStringResource, quantity: Int): String {...}

@Composable
fun pluralStringResource(resource: PluralStringResource, quantity: Int, vararg formatArgs: Any): String {...}

Например:

Text(pluralStringResource(Res.plurals.new_message, 1, 1))
suspend fun getPluralString(resource: PluralStringResource, quantity: Int): String

suspend fun getPluralString(resource: PluralStringResource, quantity: Int, vararg formatArgs: Any): String

Например:

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

Для генерации ресурсов множественного числа можно использовать синтаксис в стиле Emmet. Например, используйте plurals, p или : для создания пустого шаблона строки по умолчанию. Если вы работаете в values-en/strings.xml, IDE автоматически определяет языковой стандарт, необходимые формы количества и тот факт, что в английском языке требуются только one и other. Введите p.test или plurals.test и нажмите Tab, чтобы развернуть сокращение в блок plurals:

<plurals name="test">
    <item quantity="one"></item>
    <item quantity="other"></item>
</plurals>

Шрифты

Храните пользовательские шрифты в каталоге composeResources/font. Compose Multiplatform поддерживает форматы шрифтов TTF, OTF, TTC и вариативные шрифты на всех платформах. Форматы WOFF и WOFF2 доступны только в веб-версии и macOS.

Чтобы загрузить шрифт как тип Font, используйте компонуемую функцию Font():

@Composable
fun Font(
    resource: FontResource,
    weight: FontWeight = FontWeight.Normal,
    style: FontStyle = FontStyle.Normal
): Font

Например:

@Composable private fun InterTypography(): Typography { val interFont = FontFamily( Font(Res.font.Inter_24pt_Regular, FontWeight.Normal), Font(Res.font.Inter_24pt_SemiBold, FontWeight.Bold), ) return with(MaterialTheme.typography) { copy( displayLarge = displayLarge.copy(fontFamily = interFont, fontWeight = FontWeight.Bold), displayMedium = displayMedium.copy(fontFamily = interFont, fontWeight = FontWeight.Bold), displaySmall = displaySmall.copy(fontFamily = interFont, fontWeight = FontWeight.Bold), headlineLarge = headlineLarge.copy(fontFamily = interFont, fontWeight = FontWeight.Bold), headlineMedium = headlineMedium.copy(fontFamily = interFont, fontWeight = FontWeight.Bold), headlineSmall = headlineSmall.copy(fontFamily = interFont, fontWeight = FontWeight.Bold), titleLarge = titleLarge.copy(fontFamily = interFont, fontWeight = FontWeight.Bold), titleMedium = titleMedium.copy(fontFamily = interFont, fontWeight = FontWeight.Bold), titleSmall = titleSmall.copy(fontFamily = interFont, fontWeight = FontWeight.Bold), labelLarge = labelLarge.copy(fontFamily = interFont, fontWeight = FontWeight.Normal), labelMedium = labelMedium.copy(fontFamily = interFont, fontWeight = FontWeight.Normal), labelSmall = labelSmall.copy(fontFamily = interFont, fontWeight = FontWeight.Normal), bodyLarge = bodyLarge.copy(fontFamily = interFont, fontWeight = FontWeight.Normal), bodyMedium = bodyMedium.copy(fontFamily = interFont, fontWeight = FontWeight.Normal), bodySmall = bodySmall.copy(fontFamily = interFont, fontWeight = FontWeight.Normal), ) } }

Если Font является компонуемым элементом, убедитесь, что его зависимые компоненты, например TextStyle и Typography, также являются компонуемыми.

В веб-приложениях специальные символы, например эмодзи и символы арабского письма, поддерживаются автоматически благодаря автоматическому подбору шрифта, который при необходимости загружает шрифты Noto.

Если вам нужно полностью контролировать используемый шрифт, включите в пакет определенный шрифт и зарегистрируйте его вручную с помощью API предварительной загрузки.

Файлы Raw

Чтобы загрузить любой файл Raw в виде массива байтов, используйте функцию Res.readBytes(path):

suspend fun readBytes(path: String): ByteArray

Файлы Raw можно размещать в каталоге composeResources/files и создавать внутри него любую иерархию.

Например, для доступа к файлам Raw используйте следующий код:

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")
}

Преобразование массивов байтов в изображения

Если файл, который вы читаете, является растровым изображением (JPEG, PNG, BMP, WEBP) или векторным изображением XML, можно использовать следующие функции для преобразования его в объект ImageBitmap или ImageVector, подходящий для компонуемого элемента Image().

Получите доступ к файлам Raw, как показано в разделе «Файлы Raw», а затем передайте результат компонуемому элементу:

// 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:

// bytes = Res.readBytes("files/example.svg")
Image(bytes.decodeToSvgPainter(LocalDensity.current), null)

Сгенерированные карты ресурсов и строковых идентификаторов

Для удобства Compose Multiplatform также сопоставляет ресурсы со строковыми идентификаторами. Обращаться к ним можно, используя имя файла в качестве ключа:

val Res.allDrawableResources: Map<String, DrawableResource>
val Res.allStringResources: Map<String, StringResource>
val Res.allStringArrayResources: Map<String, StringArrayResource>
val Res.allPluralStringResources: Map<String, PluralStringResource>
val Res.allFontResources: Map<String, FontResource>

Пример передачи сопоставленного ресурса компонуемому элементу:

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

Мультиплатформенные ресурсы Compose в качестве ресурсов Android

Начиная с Compose Multiplatform 1.7.0 все мультиплатформенные ресурсы упаковываются в ресурсы Android. Благодаря этому Android Studio может создавать предварительный просмотр компонуемых элементов Compose Multiplatform в исходных наборах Android.

Предварительный просмотр в Android Studio доступен только для компонуемых элементов в исходном наборе Android. Также требуется одна из последних версий AGP: 8.5.2, 8.6.0-rc01 или 8.7.0-alpha04.

Использование мультиплатформенных ресурсов в качестве ресурсов Android также позволяет напрямую обращаться к ним из WebView и компонентов медиаплеера Android, поскольку к ресурсам можно перейти по простому пути, например Res.getUri("files/index.html").

Пример компонуемого элемента Android, отображающего HTML-страницу ресурса со ссылкой на изображение из ресурсов:

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

В примере используется следующий простой HTML-файл:

<html> <header> <title> Cat Resource </title> </header> <body> <img src="cat.jpg"> </body> </html>

Оба файла ресурсов в этом примере находятся в исходном наборе commonMain:

File structure of the composeResources directory

Взаимодействие с другими библиотеками и ресурсами

Доступ к мультиплатформенным ресурсам из внешних библиотек

Если вы хотите обрабатывать мультиплатформенные ресурсы с помощью других библиотек, подключённых к проекту, можно передавать в эти API пути к файлам, специфичные для платформы. Чтобы получить путь, специфичный для платформы, вызовите функцию Res.getUri(), указав путь проекта к ресурсу:

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

Теперь, когда переменная uri содержит абсолютный путь к файлу, любая внешняя библиотека может использовать этот путь для доступа к файлу удобным для неё способом.

Для задач, специфичных для Android, мультиплатформенные ресурсы также упаковываются как ресурсы Android.

Удалённые файлы

В контексте библиотеки ресурсов ресурсами считаются только файлы, являющиеся частью приложения.

Для загрузки удалённых файлов из интернета по URL можно использовать специализированные библиотеки:

  • Compose ImageLoader

  • Kamel

  • Клиент Ktor

Если вы вручную скачиваете изображение или считываете его байты, воспользуйтесь функциями декодирования библиотеки ресурсов, чтобы преобразовать их. Пример загрузки изображений из сети в настольном приложении см. в разделе Использование изображений в Compose Multiplatform для настольных приложений.

Если вы самостоятельно скачиваете изображение или считываете его байты, преобразуйте их в изображение с помощью функций декодирования библиотеки ресурсов. Пример загрузки изображений из сети в настольном приложении см. в руководстве по использованию изображений в Compose Multiplatform для настольных приложений.

Использование ресурсов Java

Хотя ресурсы Java можно использовать с Compose Multiplatform, они не поддерживают расширенные возможности платформы: генерируемые средства доступа, поддержку нескольких модулей, локализацию и так далее. Чтобы воспользоваться этими возможностями, рассмотрите возможность полного перехода на библиотеку мультиплатформенных ресурсов.

В Compose Multiplatform 1.7.0 API ресурсов, доступный в пакете compose.ui, объявлен устаревшим. Если вам по-прежнему нужно работать с ресурсами Java, скопируйте в проект следующую реализацию, чтобы код продолжал работать после обновления до Compose Multiplatform 1.7.0 или более поздней версии:

@Composable internal fun painterResource( resourcePath: String ): Painter = when (resourcePath.substringAfterLast(".")) { "svg" -> rememberSvgResource(resourcePath) "xml" -> rememberVectorXmlResource(resourcePath) else -> rememberBitmapResource(resourcePath) } @Composable internal fun rememberBitmapResource(path: String): Painter { return remember(path) { BitmapPainter(readResourceBytes(path).decodeToImageBitmap()) } } @Composable internal fun rememberVectorXmlResource(path: String): Painter { val density = LocalDensity.current val imageVector = remember(density, path) { readResourceBytes(path).decodeToImageVector(density) } return rememberVectorPainter(imageVector) } @Composable internal fun rememberSvgResource(path: String): Painter { val density = LocalDensity.current return remember(density, path) { readResourceBytes(path).decodeToSvgPainter(density) } } private object ResourceLoader private fun readResourceBytes(resourcePath: String) = ResourceLoader.javaClass.classLoader.getResourceAsStream(resourcePath).readAllBytes()

Что дальше?

  • Посмотрите официальный демонстрационный проект, в котором показана работа с ресурсами в проекте Compose Multiplatform для iOS, Android и настольных платформ.

  • Узнайте, как управлять средой ресурсов приложения, например темой и языком внутри приложения.

2 сентября 2026 г.
Настройка и конфигурация мультиплатформенных ресурсовУправление локальной средой ресурсов

© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/multiplatform/compose-multiplatform-resources-usage.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API