Accompanist組件庫中文指南 - Insets篇

這是我參與更文挑戰的第5天,活動詳情查看: 更文挑戰
本文翻譯自 Accompanist 官方文檔 - Insetshtml

目前有一個正在進行的 Jetpack Compose中文手冊 項目,旨在幫助開發者更好的理解和掌握Compose框架,目前仍還在開荒中,歡迎你們進行關注與加入! 這篇文章由本人翻譯撰寫,目前已經發布到該手冊中,歡迎進行查閱。java

概述

Jetpack Compose 的 Insets 採用了 View 系統中 Insetter 組件庫的設計理念,使其能夠在 composables 中被使用。android

用法

爲了能在你的 composables 中使用 Insets , 你須要使用 ProvideWindowInsets 方法並將你的視圖內容聲明在尾部lambda中。這步操做一般要在你的composable層級的頂部附近進行。git

setContent {
  MaterialTheme {
    ProvideWindowInsets {
      // your content
    }
  }
}
複製代碼

⚠️ 爲了使你的 view 層級可以獲取到 Insets, 你須要確保在你Activity中使用 WindowCompat.setDecorFitsSystemWindows(window, false)。若是你還想爲你的系統狀態欄設置顏色,可使用Accompanist組件庫提供的系統UI控制器組件來完成。github

經過使用 ProvideWindowInsets 方法將容許本組件在 content 中設置一個 OnApplyWindowInsetsListener,這個Listener將會被用來更新 LocalWindowInsets 這個 CompositionLocal。api

LocalWindowInsets 持有了一個 WindowInsets 實例,其中包含了各類 WindowInsets types 數值信息,例如狀態欄、導航欄、輸入法等。你一般能夠像這樣使用這些數值信息。微信

Composable
fun ImeAvoidingBox() {
    val insets = LocalWindowInsets.current
		// 切記,這些信息都是px單位,使用時要根據需求轉換單位
    val imeBottom = with(LocalDensity.current) { insets.ime.bottom.toDp() }
    Box(Modifier.padding(bottom = imeBottom))
}
複製代碼

可是本組件一樣也提供了一些易於使用的Modifier。markdown

Modifiers

本組件提供了兩種 Modifier 類型用於輕鬆適配特定 insets 的 padding 與 size.app

Padding Modifier

使用 Padding Modifier 將容許爲你的 composable 施加 padding 來適配一些特定的 insets,當前提供了以下幾種擴展方法。框架

這些方法一般會被用來將 composable 移出系統狀態欄或導航欄等,FloatingActionButton 就是一個典型的例子,一般咱們都但願將這個懸浮按鈕移動至系統導航欄上方, 不但願被系統導航欄遮蓋。

FloatingActionButton(
    onClick = { /* TODO */ },
    modifier = Modifier
        .align(Alignment.BottomEnd)
        .padding(16.dp) // normal 16dp of padding for FABs
        .navigationBarsPadding() // Move it out from under the nav bar
) {
    Icon(imageVector = Icons.Default.Add, contentDescription = null)
}
複製代碼

Size Modifier

經過 Size Modifier 將容許爲你的 composable 施加 size 來適配一些特定的 Insets,當前提供了以下幾種擴展方法。

我門一般可讓 composable 爲系統欄提供背景,相似以下。

Spacer(
    Modifier
        .background(Color.Black.copy(alpha = 0.7f))
        .statusBarsHeight() // Match the height of the status bar
        .fillMaxWidth()
)
複製代碼

PaddingValues

Compose 提供了 PaddingValues 的理念,該數據類包含着全部要被施加的 padding 信息(相似於一個 Rect)。一般會被用於一些容器類型 composables,例如爲 LazyColumn 設置內容 padding。

你可能須要使用某個具體 Inset 信息做爲內容 padding,因此本組件提供了 rememberInsetsPaddingValues() 擴展方法用於將 Inset 轉化爲 PaddingValues,下面的例子中就獲取了系統欄Inset信息。

LazyColumn(
    contentPadding = rememberInsetsPaddingValues(
        insets = LocalWindowInsets.current.systemBars,
        applyTop = true,
        applyBottom = true,
    )
) {
    // content
}
複製代碼

對於更復雜的場景,能夠查閱例子 EdgeToEdgeLazyColumn

可感知 Inset 的 Layouts (insets-ui)

不幸的是,目前大多數 Compose 所提供的 Material 風格的 Layout 還不支持使用內容 padding,這意味着下面的代碼可能不會產生與你的預期相同的結果。

// 😥 This likely doesn't do what you want
TopAppBar(
    // content
    modifier = Modifier.statusBarsPadding()
)
複製代碼

爲了應對這種狀況,咱們提供了 insets-ui 這個兄弟組件庫,其中包含了經常使用佈局,並增長了一個名爲 contentPadding 的參數。下面的例子就是爲 TopAppBar 提供狀態欄的Inset信息做爲內容的 padding。

import com.google.accompanist.insets.ui.TopAppBar

TopAppBar(
    contentPadding = rememberInsetsPaddingValues(
        insets = LocalWindowInsets.current.statusBars,
        applyStart = true,
        applyTop = true,
        applyEnd = true,
    )
) {
    // content
}
複製代碼

這個兄弟組件庫還提供了Scaffold的修改版,經過在content中繪製頂部和底部欄,更好地支持邊靠邊的佈局。

Scaffold(
    topBar = {
        // We use TopAppBar from accompanist-insets-ui which allows us to provide
        // content padding matching the system bars insets.
        TopAppBar(
            title = { Text(stringResource(R.string.insets_title_list)) },
            backgroundColor = MaterialTheme.colors.surface.copy(alpha = 0.9f),
            contentPadding = rememberInsetsPaddingValues(
                LocalWindowInsets.current.statusBars,
                applyBottom = false,
            ),
        )
    },
    bottomBar = {
        // We add a spacer as a bottom bar, which is the same height as
        // the navigation bar
        Spacer(Modifier.navigationBarsHeight().fillMaxWidth())
    },
) { contentPadding ->
    // We apply the contentPadding passed to us from the Scaffold
    Box(Modifier.padding(contentPadding)) {
        // content
    }
}
複製代碼

有關庫中提供的其餘佈局的列表,請參見 API 文檔

🚧試驗性功能

接下來的功能還在試驗中,須要開發者選擇性使用。

Insets動畫支持

功能介紹

本組件庫當前試驗性支持 WindowInsetsAnimations, 這將容許你的UI內容能夠根據Insets動畫發生改變,例如當軟鍵盤彈出或關閉時, imePadding()navigationBarsWithImePadding() 在這種場景下就能夠被使用了。 在 API >= 21 的設備上,不管 WindowInsetsAnimationCompat 是否工做,在任意時刻都進行使用。

爲了可以使用Insets動畫,你須要一個使用 ProvideWindowInsets 的重載方法,而且設置 windowInsetsAnimationsEnabled = true

使用方法

ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
    // content
}
複製代碼

你可以像這樣使用 navigationBarsWithImePadding()

OutlinedTextField(
    // other params,
    modifier = Modifier.navigationBarsWithImePadding()
)
複製代碼

能夠查閱例子 ImeAnimationSample

軟鍵盤動畫

功能介紹

若是你但願使用 Insets 動畫支持軟鍵盤動畫,你須要確保在 AndroidManifest 清單中配置當前 Activity 的 windowSoftInputMode 屬性爲 adjustResize

<activity
      android:name=".MyActivity"
      android:windowSoftInputMode="adjustResize">
</activity>
複製代碼

windowSoftInputMode 默認值應該也有效,可是Compose當前沒有設置必要的標識 (詳情看 這裏)

本組件庫已經支持經過手勢操做來控制軟鍵盤,這將容許你的可滾動的組件將軟鍵盤拉進或拉出屏幕,對於這種嵌套手勢滑動可使用內置的 NestedScrollConnection 接口進行實現,本組件提供了 rememberImeNestedScrollConnection() 方法直接獲取這種軟鍵盤動畫場景的嵌套手勢滑動實現類。

⚠️ 此功能僅在 API >= 30 的設備上才能正常運行。

使用方法

// Here we're using ScrollableColumn, but it also works with LazyColumn, etc.
ScrollableColumn(
    // We use the nestedScroll modifier, passing in the 
    // the connection from rememberImeNestedScrollConnection()
    modifier = Modifier.nestedScroll(
        connection = rememberImeNestedScrollConnection()
    )
) {
    // list content
}
複製代碼

能夠查閱例子 ImeAnimationSample

配置Gradle依賴

repositories {
    mavenCentral()
}

dependencies {
    implementation "com.google.accompanist:accompanist-insets:<version>"
    // If using insets-ui
    implementation "com.google.accompanist:accompanist-insets-ui:<version>"
}
複製代碼

每一個版本能夠在 快照倉庫 中被找到,每次提交時都會更新。

可能出現的問題

若是你發現運行時出現了一些問題,這裏有一個錯誤清單能夠查閱。

  • 確保你在Activity中執行了 WindowCompat.setDecorFitsSystemWindows(window, false) 。除非你這麼作了,不然 DecorView 將消費這些insets,他們的信息不回被分配到 content 中。
  • 若是有什麼跟軟鍵盤相關的操做,確保 AndroidManifest 清單中當前 Activity 的 windowSoftInputMode 屬性被設置爲 adjustResize。不然 IME 的可見性變化將不會做爲Insets 變化而發送。
  • 類似的,若是你設置 android:windowFullscreen 屬性爲 true (或使用了 .Fullscreen 主題) 。當發現 adjustResize 沒有正常工做,請 查閱文檔 以瞭解替代方案。
  • 若是你在視圖系統的多個層級中(同時在Activity與其中的Fragment中) 使用了 ProvideWindowInsets (或 ViewWindowInsetObserver) ,你須要關閉 Insets 的消費。當執行 ProvideWindowInsets (或 ViewWindowInsetObserver) 時會徹底消費全部通過的 Insets。在Activity與其中的Fragment同時使用 ProvideWindowInsets (或 ViewWindowInsetObserver) 時意味着Activity將獲取到 Insets,可是Fragment將不回,爲了禁用消費須要設置 ProvideWindowInsets 方法參數 consumeWindowInsets = false 或者使用 ViewWindowInsetObserver.start()

本文同步已發表於微信公衆號,搜索 Jetpack Compose 博物館 便可關注

相關文章
相關標籤/搜索