文章首發於公眾號:「 技術最TOP 」
,每天都有乾貨文章持續更新,可以微信搜尋「 技術最TOP 」
第一時間閱讀,回覆【思維導圖】【面試】【簡歷】有我準備一些Android進階路線、面試指導和簡歷模板送給你
Jetpack Compose 是Google釋出的一個Android原生現代UI工具包,它完全採用Kotlin編寫,可以使用Kotlin語言的全部特性,可以幫助你輕鬆、快速的構建高質量的Android應用程式。如果你還不瞭解Jetpack Compose是什麼?建議你讀一下我前面的2篇文章:
Android Jetpack Compose 最全上手指南
去年的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...