做者 / Chris Banes, Android 開發者關係團隊工程師android
本文是手勢導航連載的第四篇文章,若是您但願瞭解其餘手勢導航的話題,請查看本系列的其餘文章:git
本文咱們將爲你們介紹的是手勢交互和衝突在全屏應用 (系統欄也被隱藏) 下的狀況和注意事項。讓咱們緊接上一篇文章,給你們講講流程圖右側的兩種狀況。github
右側的兩個解決方案都是 Android 平臺爲應用提供的沉浸模式 (immersive mode)。那問題來了: 什麼是沉浸模式?沉浸模式是一種讓內容全屏呈現的方式,用來隱藏系統欄,從而確保應用擁有最大的屏幕空間。此外,它還提供了防誤操做的功能 (好比意外使用手勢離開應用),特別適合在遊戲中採用。bash
沉浸模式分爲兩種:app
非粘性沉浸模式: 用戶能夠經過在系統欄上滑動來退出沉浸模式。ide
粘性沉浸模式: 用戶能夠經過在系統欄上滑動來暫時退出沉浸模式。在通過一小段時間後 (只有幾秒) 會從新自動回到沉浸模式。ui
這兩種模式都有兩種狀態:this
系統欄隱藏: 在此狀態下,返回主屏幕手勢和後退手勢均被禁用。用戶必須首先從邊緣向內側滑動才能讓系統欄顯示。google
系統欄顯示: 在此狀態下,返回主屏幕手勢和後退手勢能夠正常工做。spa
如今,咱們已經瞭解了沉浸模式的基礎知識,下面介紹這兩種不一樣模式的細節。
你們在上面的流程圖中可能已經看到,非粘性 (non-sticky) 沉浸模式很是適合須要全屏顯示但不須要在屏幕邊緣附近使用精確滑動手勢的 UI。常見的例子包括全屏視頻播放和照片瀏覽等。
就手勢導航而言,非粘性沉浸模式與其在早期版本的 Android 上的工做方式一致。在 Android 10 或以上系統中運行時,應用可使用咱們在上一篇文章中介紹的手勢區域排除 API 。在此模式下,不管系統欄是否可見,每一個邊緣能排除的區域高度仍舊限制爲 200dp。
若是您的應用正在使用非粘性沉浸模式,咱們建議您回顧一下第三篇文章,避免在屏幕邊緣出現的視圖與系統手勢出現衝突。
粘性 (sticky) 沉浸模式適合那些強烈須要使用整個屏幕,並要求在整個屏幕區域內進行觸摸輸入的 UI。常見的例子是繪圖應用,以及使用滑動操做的遊戲。
咱們來看一下運行在 Android 10 上,且使用手勢導航的 Markers 繪圖應用:
如上圖所示,一旦用戶開始在屏幕邊緣附近滑動 (繪製),就會觸發後退手勢,這會打斷用戶當前的操做。使用粘性沉浸模式的應用會有很強的交互性,所以手勢區域排除 API 的限制會被移除,但僅限於系統欄隱藏的時候。這意味着應用能夠根據須要徹底佔用屏幕左 / 右邊緣。
可是,在系統欄可見時,系統則會忽略全部排除的手勢區域,讓用戶能夠返回,而不會受到來自應用的干擾。在粘性沉浸模式下,系統欄僅在短期內可見,所以不會影響應用的正常交互。
屏幕底部的主屏手勢區域依舊會正常存在,是沒法排除的 "強制" 手勢區域。處於粘性沉浸模式的應用可能會佔用兩個垂直邊緣的整個長度,所以屏幕底部的主手勢區域多是用戶呼出系統欄並退出應用的惟一方法。
接下來咱們來看一下繪圖應用的改進版本,整個垂直邊緣都被應用佔用:
能夠看到,用戶如今能夠在屏幕邊緣附近自由繪製,後退手勢不會再幹擾他們。若是用戶想要退出應用,則能夠從屏幕底部向上滑動呼出系統欄,進行後退或返回主屏的操做。在實現方面,此處使用的代碼大致沿用自第三篇文章中的 "使用手勢區域排除 API" 部分,不一樣之處在於,咱們但願視圖可以知道它自身是否處於沉浸模式之中:
private val exclusionRects = ArrayList<Rect>()
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
if (changed && Build.VERSION.SDK_INT >= 29) {
updateGestureExclusionRects()
}
}
override fun onWindowSystemUiVisibilityChanged(visibility: Int) {
super.onWindowSystemUiVisibilityChanged(visibility)
// Update our gesture exclusions rects if we’re
// running on Android 10+
if (Build.VERSION.SDK_INT >= 29) {
updateGestureExclusionRects()
}
}
private fun updateGestureExclusionRects() {
// If the navigation bar is hidden, let's exclude any vertical edges so // that the user can draw freely if ((windowSystemUiVisibility and SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0) { // Root window insets are null, which happens if this is called // before we're attached and laid out. Ignore the call for now.
val rootWindowInsets = rootWindowInsets ?: return
val gestureInsets = rootWindowInsets.systemGestureInsets
exclusionRects.clear()
// Add an exclusion rect for the left gesture edge
exclusionRects += Rect(0, 0, gestureInsets.left, height)
// Add an exclusion rect for the right gesture edge
exclusionRects += Rect(width - gestureInsets.right, 0, width, height)
systemGestureExclusionRects = exclusionRects
} else {
// If the navigation bar is showing, we don't want to exclude any edges. systemGestureExclusionRects = emptyList() } } 複製代碼
您能夠在 GitHub 上閱讀 Makers 應用更新的完整代碼。
呼,一口氣看到這裏可能有點記不住。這裏我爲你們了提供一張表格,它總結出了非粘性和粘性沉浸模式之間的差別。
如何處理手勢交互中的衝突就講到這裏。我也但願您已經對手勢交互有了更深的理解,並將這些理解完美落實到應用的開發與更新中去。
本系列的第五篇文章 (也是最後一篇文章) 將介紹您可能會在應用中採用的一些常見 UI 模式,以及如何在手勢導航中支持它們。
點擊這裏進一步瞭解 Android 手勢導航