Google I/O :Android Jetpack 最新變化(三)UI

語言: CN / TW / HK

theme: devui-blue highlight: atelier-sulphurpool-light


前言

本系列文章從 Architecture,UI,Performance 和 Compose 這四個方向帶大家瞭解本次 I/O 上 Jetpack 的最新內容。

本文是第三篇:UI 篇。

1. WindowManager

這並非系統 WMS 獲取的那個 WindowManager,它是 Jetpack 的新成員,當前剛剛邁入 1.1.0。 txt implementation "androidx.window:window:1.1.0-alpha02" 它可以幫助我們適配日益增多的可摺疊裝置,滿足多視窗環境下的開發需求。 可摺疊裝置通常分為兩類:單屏可摺疊裝置(一個整體的柔性螢幕)和雙屏可摺疊裝置(兩個螢幕由合頁相連)。

目前單屏可摺疊裝置正逐漸成為主流,但無論哪種裝置都可以通過 WindowManager 感知當前的螢幕顯示特性,例如當前摺疊的狀態和姿勢等。

獲取摺疊狀態

多屏裝置下,一個視窗可能會跨越物理螢幕顯示,這樣視窗中會出現鉸鏈等不連續部分,FoldingFeature (DisplayFeature 的子類)對鉸鏈這類的物理部件進行抽象,從中可以獲取鉸鏈在視窗中的準確位置,幫助我們避免將關鍵互動按鈕佈局在其中。另外 FoldingFeature 還提供了可以感知感知當前摺疊狀態的 API,我們可以根據這些狀態改變應用的佈局:

kotlin //鉸鏈處於半開狀態且位置水平,適合切換到平板模式 fun isTableTopMode(foldFeature: FoldingFeature) = foldFeature.isSeparating && foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL //鉸鏈處於半開狀態且位置垂直,適合切換到閱讀模式 fun isBookMode(foldFeature: FoldingFeature) = foldFeature.isSeparating && foldFeature.orientation == FoldingFeature.Orientation.VERTICAL

|isTableTopMode|isBookMode| |:--:|:--:| |||

WindowManager 允許我們通過 Flow 持續觀察顯示特性的變化。

kotlin lifecycleScope.launch(Dispatchers.Main) { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { WindowInfoTracker.getOrCreate([email protected]) .windowLayoutInfo([email protected]) .collect { newLayoutInfo -> // Use newLayoutInfo to update the layout. } } } 如上,當顯示特性變化時,我們能獲取 newLayoutInfo ,它是一個 WindowLayoutInfo 型別,內部持有了 FoldingFeature 資訊。

感知視窗大小變化

應用視窗可能跟隨裝置配置變化時(例如摺疊屏的展開、旋轉,或視窗在多視窗模式下調整大小)發生變化,我們可以通過 WIndowManger 的 WindowMetrics 獲取視窗大小,我們有兩種獲取當前 WindowMetrics 的方式,同步獲取和非同步監聽: ```kotlin //非同步監聽 lifecycleScope.launch(Dispatchers.Main) { windowInfoRepository().currentWindowMetrics.flowWithLifecycle(lifecycle) .collect { windowMetrics: WindowMetrics -> val currentBounds = windowMetrics.bounds val width = currentBounds.width() val height = currentBounds.height() } }

//同步獲取 val windowMetrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(activity) val currentBounds = windowMetrics.bounds val width = currentBounds.width() val height = currentBounds.height() ```

更多參考:https://medium.com/androiddevelopers/unbundling-the-windowmanager-fa060adb3ce9

2. DragAndDrop

Jetpack DragAndDrop 是專門處理拖放手勢的庫,它除了服務於普通手機裝置上的開發,更重要的意義是可以實現摺疊裝置跨螢幕的拖放 groovy implementation 'androidx.draganddrop:draganddrop:1.0.0-alpha02' DragStartHelper 和 DropHelper 是其最核心的 API,可以配置拖防過程中的資料傳遞、顯示效果等,還可以監聽手勢回撥。

拖動 DragStartHelper

DragStartHelper 負責監測拖動手勢的開始時機,包括長按拖動、單擊並用滑鼠拖動等。我們可以將需要拖動的檢視物件包裝進來並開啟監聽,當監聽到拖動手勢觸發時,完成一些簡單配置即可。

kotlin // 使用 DragStartHelper 包裝 draggableView 物件 DragStartHelper(draggableView) { view, _ -> // 將需要傳遞的資料封裝到 ClipData 中 val dragClipData = ClipData.newUri(contentResolver, "File", fileUri) // 建立目標拖動時的展示圖片,可自定義也可以根據 draggableView 建立預設樣式 val dragShadow = View.DragShadowBuilder(view) // 基於資料、拖動效果啟動拖動 view.startDragAndDrop( dragClipData, dragShadow, null, // Optional extra local state information // 新增 flag 啟動全域性拖動 DRAG_FLAG_GLOBAL or DRAG_FLAG_GLOBAL_URI_READ) ) }.attach() 如上,準備好需要拖動資料和樣式等,呼叫 View#startDragAndDrop 啟動拖動。例子中拖動的目標是 content: 這類 URI,因此我們可以通過設定 DRAG_FLAG_GLOBAL 實現跨程序的拖動。

放置 DropHelper

DropHelper 是另一個核心 API,關心拖動資料放下的時機和目標檢視。 kotlin //針對可拖放檢視呼叫 configureView DropHelper.configureView( this,// 當前Activity outerDropTarget, //接收拖放的物件,會根據情況高亮顯示 arrayOf(MIMETYPE_TEXT_PLAIN, "image/*"), // 支援的 MIME 型別 DropHelper.Options.Builder() //一些引數配置,例如放下時高亮的顏色,檢視範圍等 .addInnerEditTexts(innerEditText) .build() ) { _, payload -> // 監聽到目標的放下,可以從 ClipData 中取得資料, // 執行上傳、顯示等處理,當然還可以處理非法拖放時的警告或檢視提醒等 ... } 構建 DropHelper.Options 例項的時候,需要呼叫 addInnerEditTexts(),這樣可以確保巢狀的 EditText 控制元件不會搶奪檢視焦點。

更多參考:https://medium.com/androiddevelopers/simplifying-drag-and-drop-3713d6ef526e