在刻 UI 時,最常使用的元件除了文字以外,也很常用「圖片」!今天的耕讀筆記就以 Image
元件為主軸,研究一下其用法及相關設定。
Image
元件在 UI 上顯示圖片,需先將要顯示的圖檔放在專案的 resources
路徑底下,接著以 painterResource()
函式載入圖檔後,以 painter
參數傳入 Image
元件即可,contentDescription
參數與 Accessibility 有關,其中的文字會轉換為供視障人士聽取內容時的語音使用,為提升 App 的 Accessibility,這個參數必需手動設定,但允許設定為 null
。
Image(
painter = painterResource("..."),
contentDescription = "...",
)
painterResource()
非常好用,可以支援點陣格式(包括 BMP
、GIF
、HEIF
、ICO
、JPEG
、PNG
、WBMP
、WebP
)以及向量格式(包括 SVG
、XML vector drawable
)。
Image
的縮放我們還可以搭配 Modifier
來調整盛裝 Image
容器的大小及形狀,再透過 ContentScale
的設定,就可以控制圖片的縮放:
Image(
painter = painterResource("kotlin-logo.png"),
contentDescription = "Kotlin Logo",
modifier = Modifier
.size(80.dp)
.clip(CircleShape),
contentScale = ContentScale.Crop
)
在上面的範例裡,我們先用 Modifier
把 Image
的尺寸設定為 80.dp
(比原始圖片小)並設定為圓形造型,再把 ContentScale
設定為 Crop
(裁切),就可以做出圓型剪裁濾鏡的效果了!在這邊順便筆記一下 ContentScale
的類型有:
None
Crop
Fit
FillHeight
FillWidth
FillBounds
Inside
Image
的不透明度Image
元件的 alpha
參數讓開發者調整圖片的不透明度,做出圖層堆疊的效果:
Image(
painter = painterResource("..."),
contentDescription = "...",
alpha = 0.2F
)
alpha
值的型別為 Float
,讀者可以試著加入多張圖片,每張圖片設定不同的不透明度並部份重疊,即可看出其效果。
Image
的顏色Image
元件的 colorFilter
參數讓開發者可以傳入一個 ColorFilter
實例來對圖片的顏色進行後製,其支援三種調整方式:
tint
:tint 用 BlendMode 混合指定顏色,其中 color
參數設定用來混合原圖片每個像素的顏色,blendMode
參數則可以設定混合模式。BlendMode
有高達二十九種模式,有興趣深究的讀者可以閱讀 androidx.compose.ui.graphics.BlendMode
的原始碼來了解細節。colorMatrix
:透過傳入一個 RGSA 的 4x5 矩陣去處理顏色變化。lighting
:透過兩個參數為圖片套用燈光效果。Image(
painter = painterResource("..."),
contentDescription = "...",
colorFilter = ColorFilter.tint(
color = Color.Cyan,
blendMode = BlendMode.SrcIn,
)
)
上例在圖片上套用了 tint
顏色濾鏡,將原本的圖片混合 Cyan 的顏色顯示。
Image(
painter: Painter,
contentDescription: String?,
modifier: Modifier = Modifier,
alignment: Alignment = Alignment.Center,
contentScale: ContentScale = ContentScale.Fit,
alpha: Float = DefaultAlpha,
colorFilter: ColorFilter? = null
)
AsyncImage
元件很多時候 App 裡要載入的素材並沒有打包在 resources
裡,而是在本機電腦的其他路徑,甚至是在網路上。由於要透過 IO 或 Network 來抓取這些資料,因此會有一段時間差。這時就要用能非同步顯示的 AsyncImage
元件。
AsyncImage(
load = {
loadSvgPainter(
"...網路上的 SVG 圖片位址...",
density
)
},
painterFor = { it },
contentDescription = "...",
contentScale = ContentScale.FillWidth,
modifier = Modifier.width(200.dp)
)
筆者是在 Compose for Desktop 官方 Tutorial 裡找到這個 AsyncImage
元件,其原始碼如下:
@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 console, you can also write this to log,
// or show some error placeholder
e.printStackTrace()
null
}
}
}
if (image != null) {
Image(
painter = painterFor(image!!),
contentDescription = contentDescription,
contentScale = contentScale,
modifier = modifier
)
}
}
官方範例裡,為了能載入不同來源的素材,實作了許多 Helper Function,在使用 AsyncImage
之前,也需要將這些函式載入:
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) }
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) }
當然,除了自行實作 AsyncImage
外,您也可以使用其他第三方套件如 Kamel 來實作非同步影像顯示,有興趣的讀者可以再去該套件的官方 Repo 查看。