循序漸進的講解用Android Jetpack Compose 寫一個B站“一鍵三連”按鈕動畫, 願新的一年好運連連
1. 知識拆解
從本文你可以學習到以下知識點:
- 如何快速學會用Compose進行佈局
- 理解
Composable
函數中數據驅動UI的編程思想, 理解remember
函數的作用 - 理解函數副作用, 編寫動畫
1.1 佈局
“一鍵三連”由三個部分構成: 點贊及點贊數、投幣及投幣數和收藏及收藏數, 三個部分橫向排布, 因此可以用Row
來包裹
Row {
點贊組件
投幣組件
收藏組件
}
以點贊為例, 它是上下結構, 且數字對齊於圖標, 在傳統Android View
體系中, 我們很快會想到約束佈局然後使其左邊對齊左邊👈, 右邊對其右邊👉, 慶幸的是Compose中也有constraintlayout-compose
庫, 所以我們引入該庫
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"
該庫與我們以前使用約束佈局時的思想是一樣的:
- 用
ConstraintLayout
包裹子元素 - 給每個子元素生成id, 此處叫
createRefs
創建引用 - 將子元素對其
parent
, 子元素之間再按需對齊
ConstraintLayout {
// 給點贊圖標、數量文本創建引用, 此處使用瞭解構聲明語法
val (refThumb, refCounter) = createRefs()
// 將點贊圖標引用的左、上、右對齊`parent`的左、上、右邊
Icon(modifier = Modifier.constrainAs(refThumb) {
start.linkTo(parent.start)
top.linkTo(parent.top)
end.linkTo(parent.end)
})
// 將文本引用的左右兩邊對其`parent`,將頂部對齊圖標的底部
Text(modifier = Modifier.constrainAs(refCounter) {
start.linkTo(parent.start)
end.linkTo(parent.end)
top.linkTo(refThumb.bottom)
})
}
再如法炮製投幣和收藏組件,這樣"一鍵三連"就佈局好了
Row {
ConstraintLayout {
Icon()
Text()
}
ConstraintLayout {
Icon()
Text()
}
ConstraintLayout {
Icon()
Text()
}
}
1.2 用狀態驅動UI
在我們傳統的Android View
體系中,我們給TextView設置文本一般這麼做:
- 找到這個view:
textView = findViewId(id)
- 設置view的屬性:
textView.setText("67")
而在compose UI編程中,狀態指明瞭UI的當前屬性,狀態改變UI隨之改變。用一個表達式表示為:
$$UI = composable(state)$$
因此,
- 我們通過
remember
聲明一個狀態,它表示會變化的文本內容, 並且它可以跨越composable函數的重組階段而不被重新初始化(這是關鍵):
var thumbCount by remember { mutableStateOf(66) }
- 將狀態設置給Text:
Text(thumbCount.toString())
- 改變狀態,composable函數會進行重組,從而自動改變文本內容
Text(thumbCount.toString(), modifier = Modifier.clickable {
// 點擊時將數量+1
thumbCount = thumbCount + 1
})
1.3 設置長按事件,繪製動畫
“一鍵三連”時,長按時開始出現圓弧進度條,並且隨着時間圓弧掃過的角度從0-360°, 直到變成完整的圓圈⭕️,它有以下屬性
- 圓弧增長是連續的,或者説看似連續的, 每一小段時間就增長一點圓弧角度
圓弧角度
是一個狀態,因為他不能因為函數重組而被重新初始化- 動畫是可以被打斷的且被反轉的,鬆手後不再增加角度而是減小角度,因此
圓弧角度的變化
可以被放在一個協程中運行, 因為它可以很容易被取消並重新開始
```
// 三連進度 從0到-360度,逆時針
var hitProgress by remember { mutableStateOf(0) }
var hitJob by remember { mutableStateOf
...
// 畫一段圓弧,從-90度開始,掃過hitProgress的角度, 圓弧的弧長會自動跟隨hitProgress的變化而變化 drawArc( startAngle = -90f, sweepAngle = hitProgress.toFloat(), ) ```
2. 總結
通過以上的例子,我們可以發現,compose相比傳統view在佈局、動畫方面會更為快速便捷。但也與view體系有很大的不同,主要體現在:
- 無法拿到view對象,來改變自身屬性,而是通過
狀態依賴
來驅動自身屬性 - 動畫的實現通過
函數副作用
來實現,它又與協程聯繫緊密