Kotlin Compose 04 控件 Image

文章目录

文档地址

简单说明

首先,代码部分,我会以 LazyColumn 作为 App 的根容器,这个容器可以简单理解为 ScrollViewListView 的混合体,后面会详细说

然后每一个子元素会被包裹在一个 item {}闭包内,但每一个都包的话,很不oop,所以,我会略微封装一下,变成下面这样,这样我就可以直接在 MyColumn闭包内写每一个item了

 1@Composable
 2fun App() {
 3    var text by remember { mutableStateOf("Hello, World!") }
 4    val platformName = getPlatformName()
 5
 6    MyColumn {
 7        Button(onClick = {
 8            text = "Hello, $platformName"
 9        }) {
10            Text(text)
11        }
12    }
13
14}
15
16@Composable
17fun MyColumn(content: @Composable LazyItemScope.() -> Unit) {
18    LazyColumn {
19        item {
20            content()
21        }
22    }
23}

加载资源图片

compose 的图片是依托于 androidx.compose.foundation.Image控件来显示的 一个入门级的示例

 1package top.kikt.common.image
 2
 3import androidx.compose.foundation.Image
 4import androidx.compose.foundation.layout.width
 5import androidx.compose.runtime.Composable
 6import androidx.compose.ui.Modifier
 7import androidx.compose.ui.graphics.ImageBitmap
 8import androidx.compose.ui.graphics.painter.Painter
 9import androidx.compose.ui.graphics.vector.ImageVector
10import androidx.compose.ui.res.loadImageBitmap
11import androidx.compose.ui.res.loadSvgPainter
12import androidx.compose.ui.res.loadXmlImageVector
13import androidx.compose.ui.res.painterResource
14import androidx.compose.ui.unit.Density
15import androidx.compose.ui.unit.dp
16import org.xml.sax.InputSource
17import java.io.File
18import java.net.URL
19
20@Composable
21actual fun ImageExample() {
22    Image(
23        painter = painterResource("header.svg"), // 支持格式:(BMP, GIF, HEIF, ICO, JPEG, PNG, WBMP, WebP) and vector formats (SVG, XML vector drawable).
24        contentDescription = "Sample", // 用于无障碍
25        modifier = Modifier.size(200.dp, 200.dp), // 设置约束
26        contentScale = ContentScale.FillBounds, // 设置缩放模式
27    )
28}

1.png

painterResource 支持的格式很多

supports raster (BMP, GIF, HEIF, ICO, JPEG, PNG, WBMP, WebP) and vector formats (SVG, ).

异步加载图片

官方给的一个示例,用于使用 io 加载网络图片或 java 的本地文件

  1import androidx.compose.foundation.Image
  2import androidx.compose.foundation.layout.Column
  3import androidx.compose.foundation.layout.width
  4import androidx.compose.runtime.Composable
  5import androidx.compose.runtime.getValue
  6import androidx.compose.runtime.produceState
  7import androidx.compose.runtime.remember
  8import androidx.compose.ui.Modifier
  9import androidx.compose.ui.graphics.ImageBitmap
 10import androidx.compose.ui.graphics.painter.BitmapPainter
 11import androidx.compose.ui.graphics.painter.Painter
 12import androidx.compose.ui.graphics.vector.ImageVector
 13import androidx.compose.ui.graphics.vector.rememberVectorPainter
 14import androidx.compose.ui.layout.ContentScale
 15import androidx.compose.ui.platform.LocalDensity
 16import androidx.compose.ui.res.loadImageBitmap
 17import androidx.compose.ui.res.loadSvgPainter
 18import androidx.compose.ui.res.loadXmlImageVector
 19import androidx.compose.ui.unit.Density
 20import androidx.compose.ui.unit.dp
 21import androidx.compose.ui.window.singleWindowApplication
 22import kotlinx.coroutines.Dispatchers
 23import kotlinx.coroutines.withContext
 24import org.xml.sax.InputSource
 25import java.io.File
 26import java.io.IOException
 27import java.net.URL
 28
 29fun main() = singleWindowApplication {
 30    val density = LocalDensity.current
 31    Column {
 32        AsyncImage(
 33            load = { loadImageBitmap(File("sample.png")) },
 34            painterFor = { remember { BitmapPainter(it) } },
 35            contentDescription = "Sample",
 36            modifier = Modifier.width(200.dp)
 37        )
 38        AsyncImage(
 39            load = { loadSvgPainter("https://github.com/JetBrains/compose-jb/raw/master/artwork/idea-logo.svg", density) },
 40            painterFor = { it },
 41            contentDescription = "Idea logo",
 42            contentScale = ContentScale.FillWidth,
 43            modifier = Modifier.width(200.dp)
 44        )
 45        AsyncImage(
 46            load = { loadXmlImageVector(File("compose-logo.xml"), density) },
 47            painterFor = { rememberVectorPainter(it) },
 48            contentDescription = "Compose logo",
 49            contentScale = ContentScale.FillWidth,
 50            modifier = Modifier.width(200.dp)
 51        )
 52    }
 53}
 54
 55@Composable
 56fun <T> AsyncImage(
 57    load: suspend () -> T,
 58    painterFor: @Composable (T) -> Painter,
 59    contentDescription: String,
 60    modifier: Modifier = Modifier,
 61    contentScale: ContentScale = ContentScale.Fit,
 62) {
 63    val image: T? by produceState<T?>(null) {
 64        value = withContext(Dispatchers.IO) {
 65            try {
 66                load()
 67            } catch (e: IOException) {
 68                // instead of printing to console, you can also write this to log,
 69                // or show some error placeholder
 70                e.printStackTrace()
 71                null
 72            }
 73        }
 74    }
 75
 76    if (image != null) {
 77        Image(
 78            painter = painterFor(image!!),
 79            contentDescription = contentDescription,
 80            contentScale = contentScale,
 81            modifier = modifier
 82        )
 83    }
 84}
 85
 86/* Loading from file with java.io API */
 87
 88fun loadImageBitmap(file: File): ImageBitmap =
 89    file.inputStream().buffered().use(::loadImageBitmap)
 90
 91fun loadSvgPainter(file: File, density: Density): Painter =
 92    file.inputStream().buffered().use { loadSvgPainter(it, density) }
 93
 94fun loadXmlImageVector(file: File, density: Density): ImageVector =
 95    file.inputStream().buffered().use { loadXmlImageVector(InputSource(it), density) }
 96
 97/* Loading from network with java.net API */
 98
 99fun loadImageBitmap(url: String): ImageBitmap =
100    URL(url).openStream().buffered().use(::loadImageBitmap)
101
102fun loadSvgPainter(url: String, density: Density): Painter =
103    URL(url).openStream().buffered().use { loadSvgPainter(it, density) }
104
105fun loadXmlImageVector(url: String, density: Density): ImageVector =
106    URL(url).openStream().buffered().use { loadXmlImageVector(InputSource(it), density) }
107
108/* Loading from network with Ktor client API (https://ktor.io/docs/client.html). */
109
110/*
111
112suspend fun loadImageBitmap(url: String): ImageBitmap =
113    urlStream(url).use(::loadImageBitmap)
114
115suspend fun loadSvgPainter(url: String, density: Density): Painter =
116    urlStream(url).use { loadSvgPainter(it, density) }
117
118suspend fun loadXmlImageVector(url: String, density: Density): ImageVector =
119    urlStream(url).use { loadXmlImageVector(InputSource(it), density) }
120
121@OptIn(KtorExperimentalAPI::class)
122private suspend fun urlStream(url: String) = HttpClient(CIO).use {
123    ByteArrayInputStream(it.get(url))
124}
125
126 */

这里使用了 withContext ,也就是协程来加载图片,然后将 inputStream 传给官网提供的对应方法来加载图片为Painter

Canvas 类“图片”

其实称呼这种为图片不太准确,只是 Canvas 支持将图片写到画布上而已

 1package top.kikt.common.image
 2
 3import androidx.compose.runtime.Composable
 4import androidx.compose.foundation.Canvas
 5import androidx.compose.foundation.layout.size
 6import androidx.compose.runtime.remember
 7import androidx.compose.ui.Modifier
 8import androidx.compose.ui.geometry.Offset
 9import androidx.compose.ui.graphics.*
10import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
11import androidx.compose.ui.unit.IntOffset
12import androidx.compose.ui.unit.IntSize
13import androidx.compose.ui.unit.dp
14
15@Composable
16fun ImageExample() {
17    ResImageExample()
18    CanvasImageExample()
19}
20
21@Composable
22fun CanvasImageExample() {
23    val image = Logo()
24
25    Canvas(modifier = Modifier.size(100.dp, 100.dp)) {
26        val redPaint = Paint().apply {
27            color = Color.Red
28        }
29        val bluePaint = Paint().apply {
30            color = Color.Blue
31        }
32        val greenPaint = Paint().apply {
33            color = Color.Green
34        }
35        drawIntoCanvas { canvas: Canvas ->
36            canvas.withSave {
37                canvas.drawCircle(Offset(100f, 100f), 100f, redPaint)
38                canvas.drawCircle(Offset(100f, 100f), 66f, bluePaint)
39                canvas.drawCircle(Offset(100f, 100f), 33f, greenPaint)
40            }
41        }
42    }
43
44    Canvas(modifier = Modifier.size(400.dp, 400.dp)) {
45        drawIntoCanvas {
46            it.withSave {
47                it.drawImageRect(
48                    image,
49                    IntOffset(0, 0),
50                    IntSize(image.width, image.height),
51                    IntOffset(0, 0),
52                    IntSize(400, 400),
53                    Paint(),
54                )
55            }
56        }
57    }
58}

2.png

设置窗口图标

所谓Icon,指的是这个

3.png

macOS 上如果是debug运行可以通过最小化窗口来查看

4.png

设置

窗口图标是通过设置 Window 的 icon 属性来配置的 Painter 的实现类有这些,理论上可以通过任意一个来实现,支持位图

5.png

最简单的,可以设置纯色

 1package top.kikt.image
 2
 3import androidx.compose.runtime.Composable
 4import androidx.compose.ui.graphics.Color
 5import androidx.compose.ui.graphics.painter.ColorPainter
 6import androidx.compose.ui.graphics.painter.Painter
 7
 8@Composable
 9fun ImageIconPainter(): Painter {
10    return ColorPainter(Color.Blue)
11}

菜单栏图标设置

6.png

菜单栏就是这个东西,WIndows 应该是右下角任务栏的那个图标 代码如下

 1package top.kikt.image
 2
 3import androidx.compose.runtime.Composable
 4import androidx.compose.ui.graphics.painter.BitmapPainter
 5import androidx.compose.ui.window.ApplicationScope
 6import androidx.compose.ui.window.Tray
 7
 8@Composable
 9fun ApplicationScope.TrayWindowPaint() {
10    val icon = Logo()
11    Tray(
12        icon = BitmapPainter(icon),
13        menu = {
14            Item("Quit App", onClick = ::exitApplication)
15        },
16    )
17}

这里是使用了扩展方法来定义的,因为Tray方法本身是 ApplicatioinScope 的扩展方法,为了在单独文件内定义控件,所以这里使用这种定义方法

7.png

menu 就是定义弹出的选项, 使用 Item 方法来定义每个选项即可