文章首发于公众号:「 技术最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组件),展现一张图片

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

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

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

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

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

@Composablefun 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,代码如下:

@Composablefun 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个:

@Composablefun 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中减少如下代码:

@Composablefun 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中的列表应用则是非常简单,简略到令人发指。来看一下咱们如何展现一个列表:

@Composablefun 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 形容它们的地位关系?

代码:

@Composablefun 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        }

残缺代码如下:

@Composablefun 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动画。

代码如下:

@Composablefun 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 来加载:

@Composablefun 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 herefun 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...