2023年9月25日 星期一

【Android】 雙窗格版面配置 SlidingPaneLayout


基礎

為什麼要使用? 💬在大螢幕上,使用多窗格或許更為方便。

SlidingPaneLayout 會根據兩個窗格的寬度,判斷是否要顯示窗格。像是如果螢幕寬度大於「清單窗格 + 詳細窗格」的最小寬度,SlidingPaneLayout就會自動並排顯示這兩個窗格。

添加依附元件

dependencies {
    implementation "androidx.slidingpanelayout:slidingpanelayout:1.2.0"
}


中斷點(Brackpoints)推薦

大小類別中斷點裝置佔比
精簡寬度< 600dp99.96% 直向模式的手機
中等寬度600dp+93.73% 直向模式的平板電腦、

直向模式的展開大型內部螢幕

展開寬度840dp+97.22% 橫向模式的平板電腦、

橫向模式的展開大型內部螢幕

精簡高度< 480dp99.78% 橫向模式的手機
中等高度480dp+96.56% 橫向模式的平板電腦、

97.59% 直向模式的手機

展開高度900dp+94.25% 直向模式的平板電腦



實例

layout

<androidx.slidingpanelayout.widget.SlidingPaneLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/sliding_pane_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".SportsListFragment">
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="550dp"
        android:layout_height="match_parent"
        android:clipToPadding="false"
        android:padding="8dp"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/detail_container"
        android:name="com.example.android.sports.NewsDetailsFragment"
        android:layout_height="match_parent"
        android:layout_width="300dp"
        android:layout_weight="1"/>
</androidx.slidingpanelayout.widget.SlidingPaneLayout>

  • 將 root 轉成 SlidingPaneLayout
  • 添加 FragmentContainerView , 設定 android:name 指定類別。
  • 設定 width 為 dp (要在平板電腦上顯示,加總寬度要大於 840dp)
  • 將 FragmentContainerView 設 android:layout_weight="1" ,讓 FragmentContainerView  能填滿多餘的螢幕寬度。

將文章列表的導航改成在第二個雙格顯示詳細內容 (若寬度足以顯示雙視窗)

若沒有改掉導航,點選文章列表中的選項後,會整個螢幕都跳去閱讀詳細文章的頁面。

將 Item onClick 後會導航 ( this.findNavController().navigate(action) )的部分,改成以下程式碼 :
binding.slidingPaneLayout.openPane()

新增自訂的返回瀏覽功能

需要改善的問題 :
  • 問題一 : 在可以顯示雙窗格的設備上,點選返回鍵,會跳出應用程式,而非顯示上一個瀏覽的文章。 
  • 問題二 : 在小型設備上,在詳細資料窗格點選返回鍵,沒有返回清單窗格,而是直接退出應用程式。                             

解決小型設備的返回問題

添加返回的程式

將此 class 添加在清單所在 class 之下。
class SportsListOnBackPressedCallback(
    private val slidingPaneLayout: SlidingPaneLayout
): OnBackPressedCallback(slidingPaneLayout.isSlideable && slidingPaneLayout.isOpen) ,SlidingPaneLayout.PanelSlideListener {
    init {
        slidingPaneLayout.addPanelSlideListener(this) //將 SportsListOnBackPressedCallback 事件監聽器類別新增至事件監聽器清單中
    }

    override fun handleOnBackPressed() {
        slidingPaneLayout.closePane() //關閉內容窗格並返回清單窗格
    }

    // 在詳細窗格滑動時呼叫
    override fun onPanelSlide(panel: View, slideOffset: Float) {
    }

    // 在詳細窗格開啟(可見)時呼叫
    override fun onPanelOpened(panel: View) {
        isEnabled = true //啟用 onBackPressedCallback
    }

    //在詳細窗格關閉時呼叫
    override fun onPanelClosed(panel: View) {
        isEnabled = false //停用 onBackPressedCallback
    }
}
  • isSlideable() : 檢查此佈局中的清單窗格和詳細資訊檢視窗格是否可以完全並排放置。
  • isOpen : 是否開啟詳細資訊檢視

註冊 callback

FragmentActivity 可透過 OnBackPressedDispatcher 控制返回。
OnBackPressedDispatcher 可控管系統如何將「返回按鈕事件分派到一個或多個 OnBackPressedCallback 物件。

使用 addCallback() 方法新增回呼(需使用LifecycleOwner)。只有在 LifecycleOwner 為 Lifecycle.State.STARTED 時,才會新增 OnBackPressedCallback 。

當相關 LifecycleOwner 遭到刪除時,活動或片段也會移除已註冊的回呼,藉此防止記憶體流失,並讓回呼適合用於片段或其他生命週期擁有者 (生命週期較短時)。

實作
onViewCreated 添加以下程式碼:
val slidingPaneLayout = binding.slidingPaneLayout // Connect the SlidingPaneLayout to the system back button.
requireActivity().onBackPressedDispatcher.addCallback(
    viewLifecycleOwner, //讓這個回呼的有效期間僅限於片段的生命週期
    SportsListOnBackPressedCallback(slidingPaneLayout) //將回呼類別做為第二個參數的例項
)

鎖定模式 (禁止以手勢返回清單)

未設定時,可以用手勢返回清單,如下:

若要鎖定(禁用手勢滑動),請在 onViewCreated 中新增以下程式碼:
slidingPaneLayout.lockMode = SlidingPaneLayout.LOCK_MODE_LOCKED

資料來源

0 comments:

張貼留言