做一名Android开发者是一件喜忧参半的事。

一方面,我们能创造出很多优质的app,设计出既连贯又清晰的基础代码让用户坐享我们的开发成果。

另一方面,如果你问经验丰富的Adroid 开发者关于开发Android的重点是什么,他们大多数都会给出一个相同的答案——用劣质的XML文件构建UI。

启动Jetpack Compose

Jetpack Compose是用Kotlin for Android来编写UI代码的一种新的声明式方法,针对Jetpack Compose有很多资源可供参考,我推荐这个由 Leland Richardson(Android工具包团队的主要工程师之一)提供的 视频,很多出色的代码实验室 也是按照此视频进行操作的。

接下来让我们一起创建图像吧!

Compose中的所有UI组件都是窗口小部件,并且这些小部件基本上都是使用 Composable 批注来进行批注的函数。

与Kotlin中的 suspend 关键字相似,Compose中的可组合函数只能从其他可组合函数中调用,有关更多信息,请参见 本视频

让我们看一下Compose中的一个基本小部件:

@Composable
     fun MyImage() {
    Image(imageResource(id = R.drawable.ic_launcher)) }
    

如我们所见,这是一个显示带有可绘制资源图像的窗口小部件。

但是,等等……有可能我在app中所展示的大多数图像都是从网络加载的,这属于异步工作,在Android视图系统中我有大量的文件库来对其进行处理,但这如果放在Compose中我该怎么做呀?!

!1_EDziGp2Ryr0GxymMH8bkFw

Compose社区

幸运的是,Compose社区真的很棒。我在 Leland Richardson的#compose slack频道上看到了一个帖子,最后,我终于开窍了。

对于这个小部件我们使用 Picasso ,但是只要能为我们提供图像加载的 Target 接口,那么其他任何图像加载库都可以对其进行使用。

要想将Picasso合并到你的Android app中,请在 build.gradle 文件中添加以下依赖项:

dependencies { implementation 'com.squareup.picasso:picasso:2.71828' }
    

下面就可以开始构建自定义部件啦!

首先,我们像往常一样,创建一个以 Composable 批注进行批注的函数。

        @Composable
     fun NetworkImage(url: Sting) { 
    Image(asset = <Where can we get an asset for the image?>) }
    

当使用图像加载器将URL加载成图像时,我们通常需要引用一个视图,且可以从中查看图像。但是在Compose中我们没有视图,那我们会把图像加载到哪里了呢?

Picasso和其他大多数图像加载器都具有Target接口,该接口提供了诸如onSuccess和onFail之类的回调。 Picasso中的界面如下所示:

public interface Target { 
         void onBitmapLoaded(Bitmap var1, LoadedFrom var2); 
         void  onBitmapFailed(Exception var1, Drawable var2);
         void onPrepareLoad(Drawable var1);
    }
   

哈哈 开个玩笑。
t01bb3bd17cc3655753

我们可以看到,使用此接口,可以获取已加载完的图像的位图,如发生故障,是由于误差本身所带来具有错误状态的图形,如果为Picasso.提供图片,则可以为占位符绘制图形。

现在该到使用由Compose framwork提供的新的给力工具的时候了,也就是onCommit lambd.


onCommit效果是每次对已改变了的效果进行回调输入的一种生命周期效果。

听起来好像有点复杂……那就简化一下!

事实上,这是一个带参数的lambda,它提供了一个范围,你可以在这个范围中运行代码,并在必要时进行处理,这在处理异步操作(如从Internet下载图像)时非常方便。

我们可以这样用:

哇……这要接收好多代码……我解释一下:

// We declare remembered values first, to avoid redoing the
     work on recomposition 
    var image by remember { mutableStateOf<ImageAsset?>(null) } 
    var drawable by remember { mutableStateOf<Drawable?>(null) }
    

首先,我们得强调需要记住的值,即我们要从网络上获取的图像资源素材,以及一个图片,它可能是从我们的app资源中获取一个占位符或错误图像。

它们将帮助我们通过重组来维持状态。

此处了解有关Jetpack Compose中 remember 关键字和状态的更多信息。

然后,我们宣布onCommit lambd能提供一个供我们可以执行异步代码的范围:

onCommit(url) { 
    val picasso = Picasso.get() // 
    We load the image to this target
     // | 
    // v 
    val target = 
    object : Target { override fun onBitmapLoaded(bitmap: Bitmap?, from: 
    Picasso.LoadedFrom?) {
     //Here we get the loaded
     image image = bitmap?.asImageAsset() 
    }
     }
    

我们还解决了代码中的占位符和错误,并需要处理此块中的代码。

onDispose { 
    image = null 
    drawable = null 
    picasso.cancelRequest(target)
     }
    

最后,我们可以将获得的图像加载到目标中:

picasso
     .load(url)
     .into(target) // <- See how we load into the target?
    

因此,在我们退出 onCommit 代码块后,我们得到了一个图像和一个可绘制对象,最后就可以组成我们的小部件了:

if (image != null) {
    // Image is a pre-defined composable that lays out and 
    draws a given [ImageAsset]. 
    Image(asset = image, modifier = modifier) 
    }
    

如果我们需要一个占位符图像,直到该图像加载完毕,或者为防止在绘制图像过程中出现错误,我们可以添加以下代码以在Canvas小部件上对其进行绘制:

else if (drawable != null) {
     // Canvas is a pre-defined composable 
    Canvas(modifier = modifier) { 
    drawIntoCanvas { canvas -> 
    drawable.draw(canvas.nativeCanvas) 
     }
     }
    }

就是这样了! 我们现在就能有一个可以从网上下载图像并能显示出来的小部件啦! 小部件完整的代码是:

@Composable
     fun NetworkImage(url: String?, modifier: Modifier) {

     var image by remember { mutableStateOf<ImageAsset?>(null) } 
    var drawable by remember { mutableStateOf<Drawable?>(null) }

     onCommit(url) {
     val picasso = Picasso.get() 

    val target = object : Target { 
    override fun onPrepareLoad(placeHolderDrawable: Drawable?) {
     drawable = placeHolderDrawable
     } 
    override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) { drawable = errorDrawable } override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) {
     image = bitmap?.asImageAsset() 
    }
     } 

    picasso
      .load(imagePath)
     .placeHolder(R.drawable.placeholder) .error(R.drawable.error)
     .into(target) 
    onDispose { 
    image = null
     drawable = null 
    picasso.cancelRequest(target)
     }
     } 

    if (image != null) {
     Image(asset = image, modifier = modifier) } 
    else if (theDrawable != null) {
     Canvas(modifier = modifier) {
    drawIntoCanvas { canvas ->
    drawable.draw(canvas.nativeCanvas)
     }
}

感谢亲的阅读!

希望你能立即创建一个复杂小部件呢!

WRITTEN BY

Ziv Kesten

ProAndroidDev

原文链接