关于android:Jetpack-Compose-重磅更新最全的新组件上手指南

4次阅读

共计 9600 个字符,预计需要花费 24 分钟才能阅读完成。

文章首发于公众号:「技术最 TOP」,每天都有干货文章继续更新,能够微信搜寻 「技术最 TOP」 第一工夫浏览,回复【思维导图】【面试】【简历】有我筹备一些 Android 进阶路线、面试领导和简历模板送给你

Jetpack Compose 是 Google 公布的一个 Android 原生古代 UI 工具包,它齐全采纳 Kotlin 编写,能够应用 Kotlin 语言的全副个性,能够帮忙你轻松、疾速的构建高质量的 Android 应用程序。如果你还不理解 Jetpack Compose 是什么?倡议你读一下我后面的 2 篇文章:

Android Jetpack Compose 最全上手指南

Jetpack Compose, 不止是一个 UI 框架!

去年的 Google IO 大会上,Google 发表了 Jetpack Compose 的面世,然而在去年 11 月份,它才公布第一个预览版 -Developer Preview1,尔后,根本放弃每两周公布一个小版本,到当初,半年的工夫过来了,两头公布了十多个小版本,明天,终于迎来了重大更新,Developer Preview2 公布了。

Jetpack Compose Developer Preview1 公布后,开发者最关怀的几个问题是,没有 Compose 版本的 RecyclerView、Constriantlayout、动画等一系列问题。这些问题在 Preview2 都解决了。

当然,从 Preview1 到当初公布的 Preview2,变动十分大,甚至很多 API 都曾经变了,有的属性或者类的减少或者删除。具体的变换化太多,就不在这里一一解说,感兴趣的能够看看官网的每个小版本的更新日志。明天就带大家一起看看 PreView2 减少的一些重磅性能。

  • 1、Modifier
  • 2、RecyclerView
  • 3、Constriantlayout
  • 4、动画
  • 5、原生 View 引入 Compose

好戏收场了!

1、Modifier

首先,说一下 Modifier(修改器),在 Preview1 版本,就曾经有了 modifier, 不过应用的中央不多,并且对于它的定位比拟含糊,令人困惑,因为 modifier 无能的事儿,通过组合函数也能做到。然而咱们发现了一件事,例如,要在 Compose 函数中减少 padding 的时候,会产生大量的嵌套,因为要给嵌套一个容器能力设置 padding, 因而,当初将很多性能都挪动到了 Modifier, 它当初应用十分宽泛,能够润饰一个元素、一个布局或者一些其余行为。如何应用 Modifier? 先来看一个例子:

首先,咱们写一个 Compose 函数(即 Compose 组件),展现一张图片

@Composable
fun Greeting() {val (shape,setShape) = state<Shape> {CircleShape}
    Image(asset = imageResource(R.drawable.androidstudio),
        contentScale = ContentScale.Crop )
}

图片显示的是原来的尺寸,而后给图片指定一个大小,比方:256dp, 此时就须要应用 Modifier 了。

@Composable
fun Greeting() {val (shape,setShape) = state<Shape> {CircleShape}
    Image(asset = imageResource(R.drawable.androidstudio),
        contentScale = ContentScale.Crop,
     modifier = Modifier.size(256.dp))
}

批改后如下,宽高都为 256dp。

modifier 中有很多能够配的参数,比方,减少一个 padding,将图片裁剪成一个圆形

@Composable
fun Greeting() {val (shape,setShape) = state<Shape> {CircleShape}
    Image(asset = imageResource(R.drawable.androidstudio),
        contentScale = ContentScale.Crop,
     modifier = Modifier.size(256.dp)
         .padding(16.dp)
         .drawShadow(8.dp,shape = shape)
        )
}

成果就成了这样:

还能够再圆形头像加一个border,代码如下:

@Composable
fun Greeting() {val (shape,setShape) = state<Shape> {CircleShape}
    Image(asset = imageResource(R.drawable.androidstudio),
        contentScale = ContentScale.Crop,
     modifier = Modifier.size(256.dp)
         .padding(16.dp)
         .drawShadow(8.dp,shape = shape)
         .drawBorder(6.dp,MaterialTheme.colors.primary,shape = shape)
        )
}

成果如下:

还能够同时增加多个 border,比方我再减少 2 个:

@Composable
fun Greeting() {val (shape,setShape) = state<Shape> {CircleShape}
    Image(asset = imageResource(R.drawable.androidstudio),
        contentScale = ContentScale.Crop,
     modifier = Modifier.size(256.dp)
         .padding(16.dp)
         .drawShadow(8.dp,shape = shape)
         .drawBorder(6.dp,MaterialTheme.colors.primary,shape = shape)
         .drawBorder(12.dp,MaterialTheme.colors.secondary,shape = shape)
         .drawBorder(18.dp,MaterialTheme.colors.background,shape = shape)
        )
}

成果就成这样了:

设置点击事件也是再 modifier 中,比方咱们要在点击这个图片后,扭转形态,以前的 View 可麻烦了,然而 Jetpack compose 却非常简单,modifier 中减少如下代码:

@Composable
fun Greeting() {val (shape,setShape) = state<Shape> {CircleShape}
    Image(asset = imageResource(R.drawable.androidstudio),
        contentScale = ContentScale.Crop,
     modifier = Modifier.size(256.dp)
         .padding(16.dp)
         .drawShadow(8.dp,shape = shape)
         .drawBorder(6.dp,MaterialTheme.colors.primary,shape = shape)
         .drawBorder(12.dp,MaterialTheme.colors.secondary,shape = shape)
         .drawBorder(18.dp,MaterialTheme.colors.background,shape = shape)
         .clickable { // 点击事件
             setShape(if(shape == CircleShape)
                     CutCornerShape(topLeft = 32.dp,bottomRight = 32.dp)
                else
                     CircleShape
             )
         }
        )
}

下面的代码中,咱们还减少了判断,如果以后 shape 是CircleShape, 咱们就扭转形态,否则就设置为CircleShape, 成果就是点击图片,形态在这两种成果之间来回切换。

成果如下:

2. Jetpack Compose 中的 RecyclerView

RecyclerView 是咱们 Android 开发中用来展现大数据列表的罕用组件,它能帮忙咱们回收复用视图,有很好的性能体验。在 Jetpack Developer PreView1 刚进去的时候,我就在官网或者代码库中找这个组件。很遗憾翻遍了所有材料都每找到,是的确没有,最终只找到了一个叫做 VerticalScroller 的组件你。它能够用来展现列表,然而它不是 RecyclerView, 它相似咱们的 ScrollView, 也就是说,展现大量数据的列表是能够的,因为它没有复用机制,展现大列表时,内存堪忧,会 OOM。

然而在这次的 Preview2 中,RecyclerView 终于被盼来了,组件名字叫做:AdapterList, 它就对应咱们原生 Android 开发的 RecyclerView。以前咱们要写一个列表是非常复杂的,用写 xml,Adapter,ViewHolder 等,最终还要在 Activity/Fragment 初始化和绑定数据,十分麻烦。Jetpack Compose 中的列表应用则是非常简单,简略到令人发指。来看一下咱们如何展现一个列表:

@Composable
fun generateList(context: Context) {val list = mutableListOf<String>()
    // 筹备数据
    for (i in 1..100) {list.add(i.toString())
    }
    AdapterList(data = list) {
        Card(
            shape = MaterialTheme.shapes.medium,
            modifier = Modifier
                .preferredSize(context.resources.displayMetrics.widthPixels.dp, 60.dp)
                .padding(10.dp)
        ) {Box(gravity = ContentGravity.Center) {ProvideEmphasis(EmphasisAmbient.current.medium) {
                    Text(
                        text = it,
                        style = MaterialTheme.typography.body2
                    )
                }
            }

        }
        Spacer(modifier = Modifier.preferredHeight(10.dp))
    }
}

看到了没,就是这样几行代码,咱们的列表就实现了,解释一下代码:最开始的筹备数据没啥说的,向 list 中增加了 100 个数据,而后将数据源传给 AdapterList, 列表的每一个 Item 是一个卡片,用的是 Card 组件,卡片里展现了一个 Text 文本,最初的Spacer 用来设置 item 之间的间距,相当于ItemDecoration, 看一下成果:

3. Constriantlayout

Constriantlayout是一个性能十分弱小的布局,也是当初 Android 开发中最受欢迎的布局之一,当 Jetpack Compose Preview1 版本才进去的时候,很多开发者都有一个疑难,Compose 中该如何应用 Constriantlayout 呢?它将如何运作,这的确是个有意思的问题。因为在 Jetpack Compose 中,所有的组件都是组合函数,获取不到 View 饮用,如何束缚彼此之间的关系的确是一个难题。好在当初这个难题解决了,上面通过几个小例子一起来看看 Compose 中的 Constriantlayout 应用。

如下图所示,有两个 View,A 和 B,ViewB 在 ViewA 的左边,顶部和 ViewA 的底部对齐,如何应用 Constriantlayout 形容它们的地位关系?

代码:

@Composable
fun GreetConstraintLayout(context: Context) {
    ConstraintLayout(constraintSet = ConstraintSet {val viewA = tag("ViewTagA").apply {
            left constrainTo parent.left
            centerVertically()}
       val viewB =  tag("ViewTagB").apply {
            left constrainTo viewA.right
            centerVertically()
            top constrainTo viewA.bottom
        }
    }, modifier = Modifier.preferredSize(context.screenWidth().dp,400.dp).drawBackground(Color.LightGray)) {

        Box(modifier = Modifier.tag("ViewTagA").preferredSize(100.dp, 100.dp),
            backgroundColor = Color.Blue,
            gravity = ContentGravity.Center
        ) {Text(text = "A")
        }
        Box(modifier = Modifier.tag("ViewTagB").preferredSize(100.dp, 100.dp),
            backgroundColor = Color.Green,
            gravity = ContentGravity.Center
        ) {Text(text = "B")
        }
    }
}

解释一下下面的代码:在 ConstraintSet 中来定义束缚,应用 Tag 来创立一个束缚,前面咱们就能够通过这个 tag 来应用咱们定义的束缚,返回的是一个ConstrainedLayoutReference,ViewA 的右边与父组件的右边对齐,垂直居中。ViewB 的右边与 ViewA 的左边对齐,top 与 ViewA 的底部对齐。也垂直居中。

比方 ViewB 中就是应用 ViewA 来作为约束条件了。

前面应用的时候,间接用 Modifier.tag() 利用束缚到组件上。

这还不是最牛逼,还有一个弱小的性能是能够在布局束缚中增加逻辑,比方:我有一个 ViewC 它的地位可能有两种状况:

  • 1、ViewC 的右边与 ViewA 的左边对齐
  • 2、View C 的右边与 ViewB 的左边对齐

该怎么写代码?先定一个一个 Boolean 变量叫hasFlag(轻易其的名,它的值依据你的业务逻辑某些状况是 true, 某些状况是 false)

 val hasFlag = true // 它的值依据你的业务逻辑某些状况是 true, 某些状况是 false
 
 tag("ViewC").apply {
            // 依据判断条件扭转,束缚也扭转
            left constrainTo (if(hasFlag) viewA else viewB).right
            bottom constrainTo viewB.top
        }

残缺代码如下:

@Composable
fun GreetConstraintLayout(context: Context) {
    ConstraintLayout(constraintSet = ConstraintSet {
        val hasFlag = true // 它的值依据你的业务逻辑某些状况是 true, 某些状况是 false
        val viewA = tag("ViewTagA").apply {
            left constrainTo parent.left
            centerVertically()}
       val viewB =  tag("ViewTagB").apply {
            left constrainTo viewA.right
            centerVertically()
            top constrainTo viewA.bottom
        }
        tag("ViewC").apply {
            // 依据判断条件扭转,束缚也扭转
            left constrainTo (if(hasFlag) viewA else viewB).right
            bottom constrainTo viewB.top
        }
    }, modifier = Modifier.preferredSize(context.screenWidth().dp,400.dp).drawBackground(Color.LightGray)) {

        Box(modifier = Modifier.tag("ViewTagA").preferredSize(100.dp, 100.dp),
            backgroundColor = Color.Blue,
            gravity = ContentGravity.Center
        ) {Text(text = "A")
        }
        Box(modifier = Modifier.tag("ViewTagB").preferredSize(100.dp, 100.dp),
            backgroundColor = Color.Green,
            gravity = ContentGravity.Center
        ) {Text(text = "B")
        }
        Box(modifier = Modifier.tag("ViewC").preferredSize(100.dp, 100.dp),
            backgroundColor = Color.Red,
            gravity = ContentGravity.Center
        ) {Text(text = "C")
        }
    }
}

hasFlag=true 成果如下:

hasFlag=false 成果如下:

其余的一些束缚布局属性同当初咱们应用的 ConstraintLayout 雷同,有趣味的能够去试试。

4. 动画

Jetpack Compose 对动画的反对也是开发者十分关怀的一个问题,这一大节就看看 Compose 中,动画的应用,还是来看一个小例子,先看效果图:

如上,一个简略的属性动画,图片有 选中 / 未选中 两种状态,由 未选中 -> 选中 时,有一个 正方形 -> 圆形 的动画,并且随同着 alpha 动画。

代码如下:

@Composable
fun GreetAnimate(){
    //
    val (selected,onValueChange) = state {false}
    // radius 变动
    val radius = animate(if(selected) 100.dp else 8.dp)
    // alpha 动画
    val selectAlpha = animate(if(selected) 0.4f else 1.0f)

   Surface(shape = RoundedCornerShape(
       topLeft = radius,
       topRight = radius,
       bottomRight = radius,
       bottomLeft = radius
   )) {
       Toggleable(
           value = selected,
           onValueChange = onValueChange,
           modifier = Modifier.ripple()) {

           Image(asset = imageResource(R.drawable.androidstudio),
               modifier = Modifier.preferredSize(200.dp,200.dp),
               contentScale = ContentScale.Crop,
               alpha = selectAlpha
           )
       }
   }
}

动画应用animate Compose 函数来实现,只须要为它提供不同的 target 的值,它就能帮你实现之间的变动,一旦动画创立,它就和一般的 Compose 函数是一样的。

留神一点 animate 创立的动画是不能被勾销的,要创立能够被勾销的动画能够应用animatedValue。还有其余两个类似动画函数:animatedFloat,animatedColor

啥?你说看起来有点相熟?那可不是嘛,ObjectAnimator,ValueAnimator, 你细品,更多对于动画的应用形式这里不开展了,有趣味的同学下来本人入手试试。

4. 与原生 View 的兼容

一门新的语言,一个新的框架,思考兼容是很有必要的,就像 Kotlin 那样,咱们应用 Kotlin 不用一下子重写整个我的项目,你能够增加一个新的类,一个新的模块中应用 Kotlin,因为它们与 Java 齐全互相调用。

Jetpack Compose 借鉴了教训,咱们要应用 Jetpack Compose, 也能够慢慢来,以前的代码不必动,在你的新模块中一点一点的增加,这就波及到与原来的 View 的兼容,在 Compose 中,能够应用 AndroidView 来兼容以前的 Views。

比方我的 Jetpack Compose 中要应用到 Webview,而它自身也没有提供,该如何是好?别放心,用原来的就行。

首先,创立一个 xml 文件webview.xml,外面增加 Webview 布局:

<?xml version="1.0" encoding="utf-8"?>
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</WebView>

而后写一个 compose 函数,应用 AndroidView 来加载:

@Composable
fun androidView(){AndroidView(R.layout.webview){ view ->
        val webView = view as WebView
        webView.settings.javaScriptEnabled =true
        webView.settings.allowFileAccess = true
        webView.settings.allowContentAccess = true
        webView.loadUrl("https://juejin.im/")
    }
}

加载了一个原生的 Webview,而后在 webview 中加载了掘金的网址,成果如下:

看一下 AndroidView 函数签名:

@Composable
// TODO(popam): support modifiers here
fun AndroidView(@LayoutRes resId: Int, postInflationCallback: (View) -> Unit = {_ ->}) {
    AndroidViewHolder(
        postInflationCallback = postInflationCallback,
        resId = resId
    )
}

承受一个布局文件资源 id, 和一个回调postInflationCallback, 当 View 被 inflate 进去后,会调用这个回调,而后你就能够在回调中应用它了。

然而,留神: 回调通常是在主线程被调用。

5. 总结

总的来说,这次 Developer PreView2 更新比拟多,并且很多 API 产生了变动,减少了一些要害的组件如 AdapterList,ConstraintLayout 动画组件 等, 应用形式也与 Preview1 有很多不同。能够来看一下 Google 对于 Jetpack Compose 上的时间表:

  • 2019.5 发表 Jetpack Compose
  • 2019.11 第一个 Developer Preview
  • 2020.6 第二个 Developer Preview
  • 2020 夏天将公布 Alpha 版本
  • 2021 将公布 release 1.0 版本

然而,要说的是,当初很多 API 还不是最终版本,能够看到,每一个打版本的变动还是蛮大的,当初依然不能用在商用我的项目上。然而就 jetpack Compose 自身来说,集体还是比拟期待的,从下面的时间表就能够看到,大略明年就能出第一个 release 版本,敬请期待吧!

对了,最新版本的 Jetpack Compose 须要 Android Studio 4.2 以上版本能力应用,想要体验的同学先安卓 Android Studio 4.2 Canary ​版本。​去官网下载!

小版本日志列表请看:https://developer.android.com…

youtobe 视频介绍请看:https://www.youtube.com/watch…

正文完
 0