You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

314 lines
10 KiB

# Image and in-app icons manipulations
## What is covered
In this tutorial we will show you how to work with images using Compose for Desktop.
## Loading images from resources
3 years ago
Using images from application resources is very simple. Suppose we have a PNG image that is placed in the `resources` directory in our project. For this tutorial we will use the image sample:
<img alt="Sample" src="sample.png" height="500" />
```kotlin
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
3 years ago
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.window.singleWindowApplication
fun main() = singleWindowApplication {
Image(
3 years ago
painter = painterResource("sample.png"),
3 years ago
contentDescription = "Sample",
modifier = Modifier.fillMaxSize()
)
}
```
3 years ago
`painterResource` supports raster (BMP, GIF, HEIF, ICO, JPEG, PNG, WBMP, WebP) and vector formats (SVG, [XML vector drawable](https://developer.android.com/guide/topics/graphics/vector-drawable-resources)).
<img alt="Resources" src="image_from_resources.png" height="375" />
3 years ago
## Loading images from device storage or network asynchronously
3 years ago
To load an image stored in the device memory (or from network) you can use `loadImageBitmap`, `loadSvgPainter` or `loadXmlImageVector`. The example below shows how to use them to load an image asynchronously.
```kotlin
import androidx.compose.foundation.Image
3 years ago
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ImageBitmap
3 years ago
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.loadImageBitmap
import androidx.compose.ui.res.loadSvgPainter
import androidx.compose.ui.res.loadXmlImageVector
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.singleWindowApplication
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.xml.sax.InputSource
import java.io.File
3 years ago
import java.io.IOException
3 years ago
import java.net.URL
3 years ago
fun main() = singleWindowApplication {
val density = LocalDensity.current
Column {
AsyncImage(
load = { loadImageBitmap(File("sample.png")) },
painterFor = { remember { BitmapPainter(it) } },
contentDescription = "Sample",
3 years ago
modifier = Modifier.width(200.dp)
)
AsyncImage(
3 years ago
load = { loadSvgPainter("https://github.com/JetBrains/compose-jb/raw/master/artwork/idea-logo.svg", density) },
3 years ago
painterFor = { it },
contentDescription = "Idea logo",
contentScale = ContentScale.FillWidth,
modifier = Modifier.width(200.dp)
)
AsyncImage(
load = { loadXmlImageVector(File("compose-logo.xml"), density) },
painterFor = { rememberVectorPainter(it) },
contentDescription = "Compose logo",
contentScale = ContentScale.FillWidth,
modifier = Modifier.width(200.dp)
)
}
}
3 years ago
@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) {
3 years ago
// instead of printing to console, you can also write this to log,
// or show some error placeholder
3 years ago
e.printStackTrace()
null
}
}
}
if (image != null) {
Image(
painter = painterFor(image!!),
contentDescription = contentDescription,
contentScale = contentScale,
modifier = modifier
)
}
}
3 years ago
3 years ago
/* Loading from file with java.io API */
3 years ago
fun loadImageBitmap(file: File): ImageBitmap =
file.inputStream().buffered().use(::loadImageBitmap)
fun loadSvgPainter(file: File, density: Density): Painter =
file.inputStream().buffered().use { loadSvgPainter(it, density) }
fun loadXmlImageVector(file: File, density: Density): ImageVector =
file.inputStream().buffered().use { loadXmlImageVector(InputSource(it), density) }
3 years ago
/* Loading from network with java.net API */
fun loadImageBitmap(url: String): ImageBitmap =
URL(url).openStream().buffered().use(::loadImageBitmap)
fun loadSvgPainter(url: String, density: Density): Painter =
URL(url).openStream().buffered().use { loadSvgPainter(it, density) }
fun loadXmlImageVector(url: String, density: Density): ImageVector =
URL(url).openStream().buffered().use { loadXmlImageVector(InputSource(it), density) }
/* Loading from network with Ktor client API (https://ktor.io/docs/client.html). */
/*
suspend fun loadImageBitmap(url: String): ImageBitmap =
urlStream(url).use(::loadImageBitmap)
suspend fun loadSvgPainter(url: String, density: Density): Painter =
urlStream(url).use { loadSvgPainter(it, density) }
suspend fun loadXmlImageVector(url: String, density: Density): ImageVector =
urlStream(url).use { loadXmlImageVector(InputSource(it), density) }
@OptIn(KtorExperimentalAPI::class)
private suspend fun urlStream(url: String) = HttpClient(CIO).use {
ByteArrayInputStream(it.get(url))
}
*/
```
<img alt="Storage" src="image_from_resources2.png" height="356" />
3 years ago
[PNG](sample.png)
[SVG](../../artwork/idea-logo.svg)
[XML vector drawable](../../artwork/compose-logo.xml)
3 years ago
## Drawing images using Canvas
```kotlin
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
3 years ago
import androidx.compose.ui.geometry.Offset
3 years ago
import androidx.compose.ui.geometry.Size
3 years ago
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
3 years ago
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.graphics.withSave
import androidx.compose.ui.platform.LocalDensity
3 years ago
import androidx.compose.ui.res.loadImageBitmap
3 years ago
import androidx.compose.ui.res.loadSvgPainter
import androidx.compose.ui.res.loadXmlImageVector
3 years ago
import androidx.compose.ui.res.useResource
import androidx.compose.ui.window.singleWindowApplication
3 years ago
import org.xml.sax.InputSource
3 years ago
fun main() = singleWindowApplication {
3 years ago
val density = LocalDensity.current // to calculate the intrinsic size of vector images (SVG, XML)
val sample = remember {
useResource("sample.png", ::loadImageBitmap)
}
val ideaLogo = remember {
useResource("idea-logo.svg") { loadSvgPainter(it, density) }
}
val composeLogo = rememberVectorPainter(
remember {
useResource("compose-logo.xml") { loadXmlImageVector(InputSource(it), density) }
}
)
3 years ago
Canvas(
modifier = Modifier.fillMaxSize()
) {
drawIntoCanvas { canvas ->
3 years ago
canvas.withSave {
canvas.drawImage(sample, Offset.Zero, Paint())
canvas.translate(sample.width.toFloat(), 0f)
with(ideaLogo) {
draw(ideaLogo.intrinsicSize)
}
canvas.translate(ideaLogo.intrinsicSize.width, 0f)
with(composeLogo) {
draw(Size(100f, 100f))
}
}
}
}
}
3 years ago
```
3 years ago
[PNG](sample.png)
3 years ago
[SVG](../../artwork/idea-logo.svg)
3 years ago
[XML vector drawable](../../artwork/compose-logo.xml)
## Setting the application window icon
3 years ago
You can set the icon for the window via parameter in the `Window` function.
Note that to change the icon on the taskbar on some OS (macOs), you should change icon in [build.gradle](/tutorials/Native_distributions_and_local_execution#app-icon)
```kotlin
3 years ago
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
3 years ago
import androidx.compose.ui.draw.paint
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
3 years ago
fun main() = application {
val icon = painterResource("sample.png")
Window(
3 years ago
onCloseRequest = ::exitApplication,
icon = icon
) {
3 years ago
Box(Modifier.paint(icon).fillMaxSize())
}
}
```
<img alt="Window icon" src="window_icon.png" height="371" />
In case of `singleWindowApplication` usage, you can use the following approach:
```kotlin
import androidx.compose.material.Text
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.res.loadImageBitmap
import androidx.compose.ui.res.useResource
import androidx.compose.ui.window.singleWindowApplication
fun main() {
val icon = BitmapPainter(useResource("sample.png", ::loadImageBitmap))
singleWindowApplication(icon = icon) {
Text("Hello World!")
}
}
```
## Setting the application tray icon
4 years ago
You can create a tray icon for your application:
```kotlin
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
3 years ago
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.window.Tray
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
fun main() = application {
val icon = painterResource("sample.png")
Tray(
icon = icon,
menu = {
Item("Quit App", onClick = ::exitApplication)
}
3 years ago
)
3 years ago
Window(onCloseRequest = ::exitApplication, icon = icon) {
Image(
3 years ago
painter = icon,
contentDescription = "Icon",
modifier = Modifier.fillMaxSize()
)
}
}
```
3 years ago
<img alt="Tray icon" src="tray_icon.png" height="479" />