Compose Multiplatform для настольных компьютеров загружает изображения из библиотеки мультимплатформенных ресурсов, как и другие платформы. Настольные приложения также могут считывать изображения из файловой системы или сети с помощью API JVM и использовать изображения в качестве значков окна и системного трея.
В примерах на этой странице используются логотипы Kotlin и Compose Multiplatform. Оба логотипа входят в пакет Kotlin Brand Assets.
Отображение изображений из ресурсов
Чтобы показать изображение, включённое в приложение, добавьте его в мультимплатформенные ресурсы проекта и соберите проект, чтобы сгенерировать средства доступа к ресурсам. Создайте экземпляр Painter, передав средство доступа в painterResource(), и передайте полученный Painter в компонуемую функцию Image():
painterResource() поддерживает растровые форматы изображений, такие как .png, .jpg, .bmp и .webp, а также формат векторных изображений Android XML. Подробнее о доступе к ресурсам в виде значений ImageBitmap или ImageVector, а также об использовании значков, шрифтов и строк см. в разделе Использование мультимплатформенных ресурсов в приложении.
Загрузка изображений из файловой системы или сети
Изображения, не входящие в состав приложения (выбранные пользователем файлы или загруженные во время выполнения), не являются ресурсами. Считайте их байты с помощью любого API JVM и декодируйте их одной из следующих функций библиотеки ресурсов:
Формат изображения
Функция декодирования
Результат
Растровое изображение
decodeToImageBitmap()
ImageBitmap
Векторное изображение XML
decodeToImageVector()
ImageVector
SVG
decodeToSvgPainter()
Painter
Чтение файла или сетевого ответа блокирует вызывающий поток, поэтому всегда выполняйте эти операции вне потока пользовательского интерфейса.
В следующем примере объявлена компонуемая функция AsyncImage(), которая загружает изображение в контексте Dispatchers.IO и отображает его, когда оно готово:
import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.BitmapPainter import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window import androidx.compose.ui.window.WindowState import androidx.compose.ui.window.application import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.jetbrains.compose.resources.decodeToImageBitmap import org.jetbrains.compose.resources.decodeToImageVector import org.jetbrains.compose.resources.decodeToSvgPainter import java.io.File import java.io.IOException import java.net.URI fun main() = application { val density = LocalDensity.current Window( onCloseRequest = ::exitApplication, title = "Images from the file system and the network", state = WindowState(size = DpSize(380.dp, 480.dp)) ) { Column( modifier = Modifier.fillMaxSize().padding(24.dp), verticalArrangement = Arrangement.spacedBy(12.dp), horizontalAlignment = Alignment.CenterHorizontally ) { Text("PNG from the file system") AsyncImage( load = { File("kotlin-logo.png").readBytes().decodeToImageBitmap() }, painterFor = { remember { BitmapPainter(it) } }, contentDescription = "Kotlin logo", modifier = Modifier.width(260.dp) ) Text("XML vector from the file system") AsyncImage( load = { File("compose-logo.xml").readBytes().decodeToImageVector(density) }, painterFor = { rememberVectorPainter(it) }, contentDescription = "Compose Multiplatform logo", contentScale = ContentScale.FillWidth, modifier = Modifier.width(100.dp) ) Text("SVG from the network") AsyncImage( load = { loadBytes(COMPOSE_LOGO_URL).decodeToSvgPainter(density) }, painterFor = { it }, contentDescription = "Compose Multiplatform logo", contentScale = ContentScale.FillWidth, modifier = Modifier.width(100.dp) ) } } } private const val COMPOSE_LOGO_URL = "https://github.com/JetBrains/compose-multiplatform/raw/master/artwork/compose-logo.svg" fun loadBytes(url: String): ByteArray = URI(url).toURL().openStream().use { it.readBytes() } @Composable fun <T> AsyncImage( load: suspend () -> T, painterFor: @Composable (T) -> Painter, contentDescription: String, modifier: Modifier = Modifier, contentScale: ContentScale = ContentScale.Fit ) { val image: T? by produceState<T?>(null) { value = withContext(Dispatchers.IO) { try { load() } catch (e: IOException) { // Instead of printing to the console, you can log the error // or show a placeholder image. e.printStackTrace() null } } } if (image != null) { Image( painter = painterFor(image!!), contentDescription = contentDescription, contentScale = contentScale, modifier = modifier ) } }
Пути к файлам в примере задаются относительно рабочего каталога приложения.
Настройка значка окна
Чтобы использовать изображение в качестве значка окна, передайте экземпляр Painter в компонуемую функцию Window() в качестве параметра icon:
Расположение значка зависит от операционной системы:
В Windows и Linux это значок окна и его элемента на панели задач.
В macOS значок приложения берётся из пакета приложения. Чтобы изменить значок в Dock, задайте его в конфигурации дистрибутива.
На следующем снимке экрана показано упакованное приложение в macOS. В окне отображается то же изображение, которое передано в параметр icon, а значок Dock берётся из файла .icns, указанного в конфигурации дистрибутива:
Значок приложения с одним окном
Функция singleWindowApplication() вычисляет параметр icon вне композиции, где painterResource() недоступен. Вместо этого считайте ресурс с помощью Res.readBytes(), принимающего путь к файлу внутри каталога composeResources, и декодируйте его в BitmapPainter:
import androidx.compose.material.Text import androidx.compose.ui.graphics.painter.BitmapPainter import androidx.compose.ui.window.singleWindowApplication import kotlinx.coroutines.runBlocking import org.jetbrains.compose.resources.decodeToImageBitmap import com.example.composeapp.generated.resources.Res fun main() { val iconBytes = runBlocking { Res.readBytes("drawable/kotlin-logo.png") } val icon = BitmapPainter(iconBytes.decodeToImageBitmap()) singleWindowApplication(icon = icon, title = "Single window icon") { Text("Hello, World!") } }
Настройка значка системного трея
Чтобы использовать изображение в качестве значка системного трея, передайте экземпляр Painter в компонуемую функцию Tray() в качестве параметра icon: