Jetpack Compose學習(4)——Image(圖片)使用及Coil圖片非同步載入庫使用

原文地址 Jetpack Compose學習(4)——Image(圖片)使用及Coil圖片非同步載入庫使用 | Stars-One的雜貨小窩

本篇講解下關於Image的使用及使用Coil開源庫非同步載入網路圖片顯示

本系列以往文章請查看此分類鏈接Jetpack compose學習

Image使用

首先,先看下參數

fun Image(
    painter: Painter,
    contentDescription: String?,
    modifier: Modifier = Modifier,
    alignment: Alignment = Alignment.Center,
    contentScale: ContentScale = ContentScale.Fit,
    alpha: Float = DefaultAlpha,
    colorFilter: ColorFilter? = null
) 

可以看到Image圖片的參數大體上是和是Icon一樣,基本使用呢,和上一篇講的Icon類似,我們載入一張本地的圖片,程式碼如下

Image(painter = painterResource(id = R.drawable.download),
    contentDescription = null
)

比較基礎的參數這裡就不再贅述了,這裡主要講解下面的其他參數

1.alignment

圖片對齊方向,前提是Image設置了寬高,取值為Alignment的定義的枚舉

設置寬高是通過Modifier.size()來設置

取值有圖中幾種:

注意: 圖中特意方框圍起來的,其返回值不是Alignment類型的,這幾個並不能取,你選了的話編輯器也會貼心的給出錯誤提示的 笑

這個alignment參數有九種取值,將一個固定的長方形分為九塊,如下圖所示

Column() {
    //為了便於區分,這裡使用Modifier添加了個黃色的背景
    Image(modifier = Modifier.size(200.dp,300.dp).background(color = Color.Yellow),
        //圖片自己隨便找張即可
        painter = painterResource(id = R.drawable.download),
        contentDescription = null,
        alignment = Alignment.Center)
}

可以看到實際效果中,圖片是居中對齊的,其他效果也就不一一展示了

圖片居中對齊

2.contentScale

圖片縮放設置,和原生的ImageView的scaleType屬性類似,取值是ContentScale的枚舉,默認是ContentScale.Fit(即自適應)

  • ContentScale.Crop 裁剪
  • ContentScale.FillBounds 拉伸圖片寬高填滿形狀
  • ContentScale.FillHeight 拉伸圖片高度填滿高度
  • ContentScale.FillWidth 拉伸圖片寬填滿寬度
  • ContentScale.Fit
  • ContentScale.Inside
  • ContentScale.None 不縮放

這個也是需要Image設置寬高才可以看出效果

contentScale的取值

下面演示各種不同效果的Image(由於Fit是默認的,下面就沒展示出來了)

contentScale各種效果

None的效果

程式碼如下

Column(Modifier.verticalScroll(rememberScrollState())) {
    //為了便於區分,這裡使用Modifier添加了個黃色的背景
    Image(modifier = Modifier
        .size(200.dp, 150.dp)
        .background(color = Color.Yellow),
        painter = painterResource(id = R.drawable.download),
        contentDescription = null,
    contentScale = ContentScale.Crop)

    Image(modifier = Modifier
        .size(200.dp, 150.dp)
        .background(color = Color.Yellow),
        painter = painterResource(id = R.drawable.download),
        contentDescription = null,
        contentScale = ContentScale.Inside)

    Image(modifier = Modifier
        .size(200.dp, 150.dp)
        .background(color = Color.Yellow),
        painter = painterResource(id = R.drawable.download),
        contentDescription = null,
        contentScale = ContentScale.FillBounds)

    Image(modifier = Modifier
        .size(200.dp, 150.dp)
        .background(color = Color.Yellow),
        painter = painterResource(id = R.drawable.download),
        contentDescription = null,
        contentScale = ContentScale.FillHeight)

    Image(modifier = Modifier
        .size(200.dp, 150.dp)
        .background(color = Color.Yellow),
        painter = painterResource(id = R.drawable.download),
        contentDescription = null,
        contentScale = ContentScale.FillWidth)

    Image(modifier = Modifier
        .size(200.dp, 150.dp)
        .background(color = Color.Yellow),
        painter = painterResource(id = R.drawable.download),
        contentDescription = null,
        contentScale = ContentScale.None)
}

3.alpha

透明度,數值類型為float,數值範圍為0f-1f之間,默認是1f

如下程式碼所示

Image(modifier = Modifier
    .size(200.dp, 150.dp)
    .background(color = Color.Yellow),
    painter = painterResource(id = R.drawable.download),
    contentDescription = null,
    alpha = 1f
)

Image(modifier = Modifier
    .size(200.dp, 150.dp)
    .background(color = Color.Yellow),
    painter = painterResource(id = R.drawable.download),
    contentDescription = null,
    alpha = 0.5f)

效果如下圖所示

透明度效果

4.colorFilter

著色效果,可以使用顏色對圖片進行混合加工,有下面三種方法進行設置

  • ColorFilter.tint(Color, BlendMode) 著色效果
  • ColorFilter.lighting(Color,Color)
  • ColorFilter.colorMatrix(colorMatrix)

這個我不是研究太多,各位可以百度參考下其他大神寫的文章

這裡就講個小例子,改變圖片的顏色

美工給的圖標有些是單一的顏色,比如說選中後是藍色,不選中是灰色的

但有時候項目需要更改顏色,如選中後要改為綠色,這個時候得要美工再次切圖,十分麻煩

這個時候我們可以這個屬性,去修改顏色,如下面程式碼

Image(modifier = Modifier
        .size(200.dp, 150.dp),
        painter = painterResource(id = R.drawable.eye_show),
        contentDescription = null,
        alpha = 1f,
        colorFilter = ColorFilter.tint(color = Color.Green, BlendMode.SrcAtop)
    )

效果:

圖標染色

R.drawable.eye_show圖片原本是灰色的,現在被我們渲染成了綠色,這種方法比較適合那種單一顏色的圖標文件

而且有了這種方法,我們甚至只需要一種灰色的圖標即可,藍色的圖標就不需要,可以省下不少apk的體積大小

至於更多內容,需要各位去研究啦…😄

Coil圖片非同步載入庫使用

上述我們一直使用的是本地圖片,如果是網路圖片,該怎麼辦呢?以往我們都是使用Glide載入,但現在由於Compose是採用State狀態監測機制去渲染UI,所以如果我們自己去實現就會十分麻煩

剛好現在已經有大神整了框架,我們拿來使用即可

Coil這個框架是Accompanist的子庫,那麼Accompanist是什麼呢?

Accompanist 是一組旨在擴充 Jetpack Compose 功能的第三方庫集合,這個庫中所提供的功能是開發者普遍需要的。

注: 寫這篇文章後發現,在Accompanist0.16.0版本更新日誌中,移除了Coil等圖片載入的庫,Coil好像是單獨出來了,下面是摘自官方的更新說明:

Coil, Glide and Image Loading Core have been removed

After being deprecated in v0.14.0, it’s time to remove the coil, glide and imageloading-core libraries. Some discussion about this can be seen on the Kotlin Slack: //kotlinlang.slack.com/archives/CJLTWPH7S/p1627482619344000.

TL;DR: the recommendation is to move to Coil Compose.

遷移到此開源庫coil-kt/coil: Image loading for Android backed by Kotlin Coroutines.

基本使用

基本流程,先導依賴

implementation("io.coil-kt:coil:1.3.2")

圖片的painter參數使用rememberImagePainter此方法,可傳入一個網路圖片

Image(
    painter = rememberImagePainter("//img0.baidu.com/it/u=312301072,1324966529&fm=26&fmt=auto&gp=0.jpg"),
    contentDescription = null,
    modifier = Modifier.size(128.dp)
)

PS:不要忘記加網路許可權<uses-permission android:name="android.permission.INTERNET" />哦!!

效果:

Coil載入網路圖片

占點陣圖 過渡及圓形裁剪

  • 占點陣圖 placeholder
  • 過渡 transitions
  • 變化效果 transformations

由於是網路圖片,我們在預覽介面是無法看到有圖片的,這個時候我們可以考慮設置下占點陣圖片,好方便調試UI

Image(
    modifier = Modifier.size(128.dp),
    painter = rememberImagePainter(
        data = "//img0.baidu.com/it/u=312301072,1324966529&fm=26&fmt=auto&gp=0.jpg",
        builder = {
            //占點陣圖
            placeholder(R.drawable.placeholder)
            //淡出效果
            crossfade(true)
            //圓形效果
            transformations(CircleCropTransformation())
        }
    ),
    contentDescription = null,
)

預覽介面可以看到占點陣圖

預覽介面的占點陣圖

載入網路的圖片過渡效果:

載入網路圖片過渡效果

可能圖中看不出圓形裁剪,我們可以加上背景看看,圖片其實已經是被裁剪了的

圓形的裁剪效果

目前,Coil自帶了兩種過渡效果:

  • CrossfadeTransition 從當前可繪製到成功/錯誤可繪製的淡入淡出(即上文程式碼出現的crossfade)
  • Transition.NONE它在Target沒有動畫的情況下立即設置可繪製對象。
    想要實現自定義,可以參考下CrossfadeTransition源程式碼,本文就不過多展開了

transformations主要是變化的意思,官方的解釋是

transformations允許您在Drawable從請求返回之前修改影像的像素數據。

Coil默認提供了4種類型 blur(高斯模糊) circle crop(圓形裁剪) grayscale(灰度) rounded corners(圓角)

對應的程式碼如下:

//radis是外層圓角,sample為高斯模糊的程度(要大於1f即可)
BlurTransformation(context,radius,sample)

//灰度
GrayscaleTransformation()

//圓角
RoundedCornersTransformation(0.8f)

效果如下所示

效果

源程式碼:

@ExperimentalCoilApi
@Preview(showBackground = true)
@Composable
fun ImagePageDemo() {

    val context = LocalContext.current
    ComposeDemoTheme {
        Column(Modifier.verticalScroll(rememberScrollState())) {
            /*Image(
                painter = rememberImagePainter("//img0.baidu.com/it/u=312301072,1324966529&fm=26&fmt=auto&gp=0.jpg"),
                contentDescription = null,
                modifier = Modifier.size(128.dp),
            )*/
            Image(
                modifier = Modifier.size(128.dp).background(Color.Blue),
                painter = rememberImagePainter(
                    data = "//img0.baidu.com/it/u=312301072,1324966529&fm=26&fmt=auto&gp=0.jpg",
                    builder = {
                        crossfade(true)
                        placeholder(R.drawable.placeholder)
                        transformations(BlurTransformation(context,0.1f,2f))
                    }
                ),
                contentDescription = null,
            )
            Image(
                modifier = Modifier.size(128.dp).background(Color.Blue),
                painter = rememberImagePainter(
                    data = "//img0.baidu.com/it/u=312301072,1324966529&fm=26&fmt=auto&gp=0.jpg",
                    builder = {
                        crossfade(true)
                        placeholder(R.drawable.placeholder)
                        transformations(GrayscaleTransformation())
                    }
                ),
                contentDescription = null,
            )
            Image(
                modifier = Modifier.size(128.dp).background(Color.Blue),
                painter = rememberImagePainter(
                    data = "//img0.baidu.com/it/u=312301072,1324966529&fm=26&fmt=auto&gp=0.jpg",
                    builder = {
                        crossfade(true)
                        placeholder(R.drawable.placeholder)
                        transformations(RoundedCornersTransformation(0.8f))
                    }
                ),
                contentDescription = null,
            )
        }

    }
}

載入gif

需要添加依賴插件包

implementation("io.coil-kt:coil-gif:1.3.2")
val context = LocalContext.current
val imageLoader = ImageLoader.Builder(context)
    .componentRegistry {
        add(GifDecoder())
        add(SvgDecoder(context))
    }
    .build()
    
Image(
    modifier = Modifier.size(128.dp).background(Color.Blue),

    painter = rememberImagePainter(
        data = "//gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F01bdd05a436090a80121974142c9f3.gif&refer=http%3A%2F%2Fimg.zcool.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1634225053&t=eaa097d38a32f901b7096fae16bfb98f",
        builder = {
            //加了這個解碼,如果圖片是jpg等格式,是沒法成功解析出來的
            decoder(GifDecoder())
            crossfade(true)
            placeholder(R.drawable.placeholder)
        }
    ),
    contentDescription = null,
)

效果如下所示

載入動態圖

PS:上述程式碼是我自己大概摸索出來的,沒想到竟然能用…😕

官方給的文檔一直沒有個標準的使用例子,也不知道上面這樣寫對不對,僅供參考😓

下面這個好像是正確的程式碼,image可以同時支援載入gif或者是jpg等圖片

Column() {
    val context = LocalContext.current
    //創建圖片載入器
    val myImageLoader = ImageLoader.Builder(context)
        .componentRegistry {
            //這裡可以加多個圖片解碼器
            add(GifDecoder())
        }
        .build()
    
    Image(
        modifier = Modifier
            .size(128.dp)
            .background(Color.Blue),
        painter = rememberImagePainter(
            data = "//gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F01bdd05a436090a80121974142c9f3.gif&refer=http%3A%2F%2Fimg.zcool.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1634225053&t=eaa097d38a32f901b7096fae16bfb98f",
            //設置圖片載入器
            imageLoader = myImageLoader,
            builder = {
                crossfade(true)
                placeholder(R.drawable.placeholder)

            }
        ),
        contentDescription = null,
    )
}

載入svg

如gif一樣,也是要加個插件依賴,之後註冊即可

implementation("io.coil-kt:coil-svg:1.3.2")
val context = LocalContext.current
//創建圖片載入器
val myImageLoader = ImageLoader.Builder(context)
    .componentRegistry {
        add(GifDecoder())
        add(SvgDecoder(context))
    }
    .build()

補充

下面的都是一樣的,不過使用Coil載入圖片會出現圖片放大後失真問題,所以有另外款開源庫推薦landscapist

implementation "com.github.skydoves:landscapist-coil:1.3.2"
CoilImage(
    imageModel = "//coil-kt.github.io/coil/images/coil_logo_black.svg",
    contentDescription = null,
    imageLoader = myImageLoader
)

svg載入效果

參考