Jetpack Compose 是Google發佈的一個Android原生現代UI工具包,它徹底採用Kotlin編寫,可使用Kotlin語言的所有特性,能夠幫助你輕鬆、快速的構建高質量的Android應用程序。若是你還不瞭解Jetpack Compose是什麼?建議你讀一下我前面的2篇文章:java
Android Jetpack Compose 最全上手指南android
去年的Google IO 大會上,Google宣佈了Jetpack Compose的面世,可是在去年11月份,它才發佈第一個預覽版-Developer Preview1,此後,基本保持每兩週發佈一個小版本,到如今,半年的時間過去了,中間發佈了十多個小版本,今天,終於迎來了重大更新,Developer Preview2 發佈了。面試
Jetpack Compose Developer Preview1發佈後,開發者最關心的幾個問題是,沒有Compose版本的RecyclerView、Constriantlayout、動畫等一系列問題。這些問題在Preview2都解決了。bash
固然,從Preview1 到如今發佈的Preview2,變化很是大,甚至不少API都已經變了,有的屬性或者類的增長或者刪除。具體的變換化太多,就不在這裏一一講解,感興趣的能夠看看官方的每一個小版本的更新日誌。今天就帶你們一塊兒看看PreView2增長的一些重磅功能。微信
好戲開場了!markdown
首先,說一下Modifier(修改器),在Preview1版本,就已經有了modifier,不過使用的地方很少,而且對於它的定位比較模糊,使人困惑,由於modifier能幹的事兒,經過組合函數也能作到。可是咱們發現了一件事,例如,要在Compose函數中增長padding的時候,會產生大量的嵌套,由於要給嵌套一個容器才能設置padding,所以,如今將不少功能都移動到了Modifier,它如今使用很是普遍,能夠修飾一個元素、一個佈局或者一些其餘行爲。如何使用Modifier?先來看一個例子:app
首先,咱們寫一個Compose函數(即Compose組件),展現一張圖片框架
@Composable fun Greeting() { val (shape,setShape) = state<Shape> { CircleShape } Image(asset = imageResource(R.drawable.androidstudio), contentScale = ContentScale.Crop ) } 複製代碼
圖片顯示的是原來的尺寸,而後給圖片指定一個大小,好比:256dp,此時就須要使用Modifier了。ide
@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
,效果就是點擊圖片,形狀在這兩種效果之間來回切換。
效果以下:
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
,看一下效果:
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 它的位置可能有兩種狀況:
該怎麼寫代碼?先定一個一個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
相同,有興趣的能夠去試試。
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
, 你細品,更多關於動畫的使用方式這裏不展開了,有興趣的同窗下來本身動手試試。
一門新的語言,一個新的框架,考慮兼容是頗有必要的,就像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.cn") } } 複製代碼
加載了一個原生的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出來後,會調用這個回調,而後你就能夠在回調中使用它了。
可是,注意: 回調一般是在主線程被調用。
總的來講,此次Developer PreView2 更新比較多,而且不少API發生了變化,增長了一些關鍵的組件如AdapterList
,ConstraintLayout
,動畫組件
等,使用方式也與Preview1有不少不一樣。能夠來看一下Google關於Jetpack Compose 上的時間表:
可是,要說的是,如今不少API還不是最終版本,能夠看到,每個打版本的變化仍是蠻大的,如今仍然不能用在商用項目上。可是就jetpack Compose 自己來講,我的仍是比較期待的,從上面的時間表就能夠看到,大概明年就能出第一個release版本,敬請期待吧!
對了,最新版本的Jetpack Compose 須要Android Studio 4.2以上版本才能使用,想要體驗的同窗先安卓Android Studio 4.2 Canary 版本。去官網下載!
小版本日誌列表請看:developer.android.com/jetpack/and…
youtobe視頻介紹請看:www.youtube.com/watch?v=U5B…
文章首發於公衆號:
「 技術最TOP 」
,天天都有乾貨文章持續更新,能夠微信搜索「 技術最TOP 」
第一時間閱讀,回覆【思惟導圖】【面試】【簡歷】有我準備一些Android進階路線、面試指導和簡歷模板送給你