iT邦幫忙

2022 iThome 鐵人賽

DAY 11
0

在刻 UI 時,最常使用的元件除了文字以外,也很常用「圖片」!今天的耕讀筆記就以 Image 元件為主軸,研究一下其用法及相關設定。

Image 元件

在 UI 上顯示圖片,需先將要顯示的圖檔放在專案的 resources 路徑底下,接著以 painterResource() 函式載入圖檔後,以 painter 參數傳入 Image 元件即可,contentDescription 參數與 Accessibility 有關,其中的文字會轉換為供視障人士聽取內容時的語音使用,為提升 App 的 Accessibility,這個參數必需手動設定,但允許設定為 null

Image(
    painter = painterResource("..."),
    contentDescription = "...",
)

painterResource() 非常好用,可以支援點陣格式(包括 BMPGIFHEIFICOJPEGPNGWBMPWebP)以及向量格式(包括 SVGXML 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
)

在上面的範例裡,我們先用 ModifierImage 的尺寸設定為 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 查看。

參考資料


上一篇
第 10 天:常用 UI 元件之 TextField
下一篇
第 12 天:常用 UI 元件之 Icon
系列文
傳教士的 Compose for Desktop 耕讀筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言