KMM Android 項(xiàng)目使用 Compose 構(gòu)建UI

iOS 的UI先不管,Compose 都還費(fèi)勁呢。

1. 預(yù)覽需要傳入?yún)?shù)

比如

  1. 列表 LazeColumn 需要接收數(shù)據(jù)
  2. 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 適配

  1. 通過ViewCompat.setOnApplyWindowInsetsListener可以拿到 WindowInsets
  2. 導(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那種透視效果的

高斯模糊預(yù)覽

2022-03-02結(jié)論,后續(xù)待更新
這里是Box內(nèi)嵌套LazeColumn和TopAppBar,與預(yù)期效果有兩點(diǎn)不同

    1. 對 TopAppBar設(shè)置的blur同時(shí)作用了TopAppBar內(nèi)部的Text
    1. 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è)置間距

  • 子元素間的間距使用horizontalArrangementverticalArrangement
  • 子元素與自身的間距使用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后,效果如下:

LazyVerticalGrid 效果

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影響
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容