做一名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中我该怎么做呀?!
!
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);
}
哈哈 开个玩笑。
我们可以看到,使用此接口,可以获取已加载完的图像的位图,如发生故障,是由于误差本身所带来具有错误状态的图形,如果为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
原文链接