iOS 的UI先不管,Compose 都還費(fèi)勁呢。
1. 預(yù)覽需要傳入?yún)?shù)
比如
- 列表 LazeColumn 需要接收數(shù)據(jù)
- navigation導(dǎo)航需要提供
navController
“注意:我們強(qiáng)烈建議您不要向生產(chǎn)函數(shù)(即使其不帶參數(shù))添加 @Preview 注釋,而是編寫一個(gè)封裝函數(shù)并在其中添加 @Preview 注釋。“
—— 安卓開發(fā)者官網(wǎng)
前面那種情況,新創(chuàng)建一個(gè)方法就行
導(dǎo)航這種,官方推薦的是函數(shù)傳遞,也就是把方法作為參數(shù)傳遞進(jìn)來
@compose
fun HomeScreen(navigateToDetailScreen: (userId:String)->Unit){
}
2. 預(yù)覽失敗
提示
The following classes could not be found androidx.compose.ui.tooling.ComposeViewAdapter
發(fā)現(xiàn)依賴的ui-tool是preview版本,改成下面就可以了
implementation("androidx.compose.ui:ui-tooling:1.1.1")
3. 系統(tǒng) insets 適配
- 通過
ViewCompat.setOnApplyWindowInsetsListener可以拿到 WindowInsets - 導(dǎo)入谷歌的新庫
implementation("com.google.accompanist:accompanist-insets:0.23.1")
然后使用ProvideWindowInsets配合LocalWindowInsets.current.systemBars獲取系統(tǒng)Insets
ProvideWindowInsets(false) {
val inset = LocalWindowInsets.current.systemBars
Box{
}
}
4. Compose 的Padding 如何接受 pixel 而不是 DP
ContentPadding 為內(nèi)邊距
Padding 為外邊距(相當(dāng)于XML那一套的margin)
如果沒有特殊需求
導(dǎo)入accompanist-insets后可以直接使用rememberInsetsPaddingValues來給view設(shè)置padding,完成適配
否則,只能通過LocalDensity自己轉(zhuǎn)換
val density = LocalDensity.current
appBarHeight = with(density){size.height.toDp()}
5. 高斯模糊效果
調(diào)用Modifier.blur(30.dp)發(fā)現(xiàn)其只作用于當(dāng)前View及其子View,對兄弟View是不會(huì)有類似iOS那種透視效果的

2022-03-02結(jié)論,后續(xù)待更新
這里是Box內(nèi)嵌套LazeColumn和TopAppBar,與預(yù)期效果有兩點(diǎn)不同
- 對 TopAppBar設(shè)置的
blur同時(shí)作用了TopAppBar內(nèi)部的Text
- 對 TopAppBar設(shè)置的
- TopAppBar背景色設(shè)置為alpha = 0.5f,但是下層元素并沒有影響,也就是沒有真的像透過一層毛玻璃去看東西的效果。
6. 如何獲取Compose View/控件/元素的寬高
使用 Modifier.onSizeChanged
ProvideWindowInsets(false) {
val density = LocalDensity.current
var appBarHeight by remember { mutableStateOf(0.dp) }
Box {
TopAppBar(
modifier = Modifier.blur(4.dp).onSizeChanged { size ->
appBarHeight = with(density){size.height.toDp()}
},
...
),
...
}
7. List多列豎排使間距相同
單列有LazyColumn,多列有還在實(shí)驗(yàn)階段的
LazyVerticalGrid
設(shè)置列的數(shù)量
- 固定=>
GridCells.Fixed(spanCount) - 不固定=>
GridCells.Adaptive(minSize.dp)
設(shè)置間距
- 子元素間的間距使用
horizontalArrangement和verticalArrangement - 子元素與自身的間距使用
contentPadding
LazyVerticalGrid(
// 固定列數(shù)為2
cells = GridCells.Fixed(2),
contentPadding = rememberInsetsPaddingValues(
inset,
// 不應(yīng)用頂部Insets
applyTop = false,
// 頂部手動(dòng)添加額外 Padding
additionalTop = appBarHeight,
// 左邊 Padding
additionalStart = 12.dp,
// 右邊 Padding
additionalEnd = 12.dp,
),
// 元素水平間距
horizontalArrangement = Arrangement.spacedBy(12.dp),
// 元素垂直間距
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
items(roomInfoList) { roomInfo ->
RoomItem(roomInfo, nav2Rooms)
}
}
都設(shè)置為12.dp后,效果如下:

8. Compose 實(shí)現(xiàn)點(diǎn)擊旋轉(zhuǎn)360度的刷新效果
為什么動(dòng)畫結(jié)束需要手動(dòng)重置狀態(tài),因?yàn)槟J(rèn)的插值器是0增大到360,再從360降到0,動(dòng)畫會(huì)順時(shí)針、逆時(shí)針交替。
同時(shí)確保動(dòng)畫連貫,不會(huì)有邊界情況。
效果:
// 動(dòng)畫標(biāo)識(shí),是否已經(jīng)開始
var animateStart by remember { mutableStateOf(false) }
// 當(dāng)前 Rotate 角度
val animateRotateDegree by animateFloatAsState(
// 目標(biāo)角度
if (animateStart) 360f else 0f,
// 動(dòng)畫模式,時(shí)長設(shè)置
TweenSpec(if (animateStart) 500 else 0),
finishedListener = {
// 結(jié)束時(shí)重置標(biāo)志
if (animateStart) animateStart = false
}
)
IconButton(onClick = {
// 點(diǎn)擊后動(dòng)作
animateStart = true
}) {
Icon(
Icons.Default.Refresh,
...
modifier = Modifier.rotate(animateRotateDegree)
)
}
流程分析
- 點(diǎn)擊按鈕,animateStart = true
- Rotate 角度改變?yōu)?360,開始 500ms 的動(dòng)畫,Icon 開始旋轉(zhuǎn)
- 500ms后,動(dòng)畫結(jié)束,animateStart = false
- Rotate 角度改變?yōu)?0,開始 0ms 的動(dòng)畫,Icon 無需改變
- 邊界情況,開始后200ms再次點(diǎn)擊,此時(shí)animateStart并未改變,無任何UI影響