當前位置 博文首頁 > 萊布尼茨:【從零開始擼一個App】Fragment和導航中的使用

    萊布尼茨:【從零開始擼一個App】Fragment和導航中的使用

    作者:萊布尼茨 時間:2021-02-20 16:35

    Fragment簡介

    Fragment自從Android 3.0引入開始,它所承擔的角色就是顯而易見的。它之于Activity就如html片段之于頁面,好處無需贅述。

    Fragment實例由Activity的FragmentManager管理,其生命周期和Activity一樣,都不是由開發人員而是由系統維護的。自然而然的,每當它們被重建時,系統只會去調用它們的無參構造器,帶參構造器會被無視。那如果要在它們創建時傳入初始化數據咋辦呢?這也是為什么會有Bundle這個玩意兒的存在,就是用于開發人員存取相關數據,如下所示:

    /**
     * Use the [ThumbnailsFragment.newInstance] factory method to
     * create an instance of this fragment.
     */
    class ThumbnailsFragment() : Fragment() {
        private var albumTag: String? = null
    
        companion object {
            @JvmStatic
            fun newInstance(albumTag: String?) =
                ThumbnailsFragment().apply {
                    arguments = Bundle().apply {
                        putString("albumTag", albumTag)
                    }
                }
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            arguments?.let {
                albumTag = it.getString("albumTag")
            }
        }
        
        /*other code*/
    }
    

    arguments由FragmentManager維護(跨fragment生命周期),可參看Android解惑 - 為什么要用Fragment.setArguments(Bundle bundle)來傳遞參數

    底部導航欄切換Fragment

    效果如下

    BottomNavigationView

    底部是BottomNavigationView組件,各個菜單在另外xml中定義,然后通過app:menu="xxx"指定。此處菜單定義如下

    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:andro>
    
        <item
            android:
            android:icon="@drawable/ic_home_black_24dp"
            android:title="@string/title_home" />
    
        <item
            android:
            android:icon="@drawable/ic_dashboard_black_24dp"
            android:title="@string/title_dashboard" />
    
        <item
            android:
            android:icon="@drawable/ic_notifications_black_24dp"
            android:title="@string/title_notifications" />
    
    </menu>
    

    然后在代碼中設置BottomNavigationView.setOnNavigationItemSelectedListener,判斷當前選中的菜單項,手動切換Fragment,需要用到FragmentTransaction。如下示例

        override fun onClick(view: View?) {
            val trans = activity.supportFragmentManager.beginTransaction()
            val fragments = activity.supportFragmentManager.fragments
            fragments.forEach {
                if (it.isVisible) {
                    trans.hide(it) //隱藏當前顯示的fragment
                }
            }
            val tag = (view as TextView).text.toString()
            val thumbnailsFragment = ThumbnailsFragment.newInstance(tag)
            //fragment_main_container就是居中的那塊區域,用于顯示各個fragment
            trans.add(R.id.fragment_main_container, thumbnailsFragment, tag)
            trans.show(thumbnailsFragment)
            trans.addToBackStack(null) //將本次操作入棧
            trans.commitAllowingStateLoss() //提交
        }
    

    注意addToBackStack方法,該方法是為了實現回退時——用戶按返回按鈕或程序執行回退(配合popBackStack)——界面能返回到本次操作前的狀態。也可指定tag,在跨[多次]操作回退時有用。注意此處入棧的是操作信息,而非fragment。

    Navigation

    上述手動控制Fragment的切換太麻煩。2018 I/O大會上,Google隆重推出一個新的架構組件:Navigation。它提供了多Fragment之間的轉場、棧管理。在抽屜式/底部/頂部導航欄的需求中都可以使用這個組件。

    使用:在res目錄下新建navigation文件夾,然后新建一個navigation graph設為bottom_navigation:

    <navigation xmlns:andro
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:
        app:startDestination="@+id/navigation_home">
    
        <fragment
            android:
            android:name="com.eixout.presearchapplication.ui.home.HomeFragment"
            android:label="@string/title_home"
            tools:layout="@layout/fragment_home" />
    
        <fragment
            android:
            android:name="com.eixout.presearchapplication.ui.dashboard.DashboardFragment"
            android:label="@string/title_dashboard"
            tools:layout="@layout/fragment_dashboard" />
    
        <fragment
            android:
            android:name="com.eixout.presearchapplication.ui.notifications.NotificationsFragment"
            android:label="@string/title_notifications"
            tools:layout="@layout/fragment_notifications" />
    </navigation>
    

    注意每個fragment的id要和之前定義的menu的id保持一致?梢栽O置轉場動畫,還可以設置每個fragment跳轉的目標(destination),目標可以是 Activity或Fragment,也可以自定義。

    然后在Activity布局文件中添加一個Fragment,設置name屬性為android:name="androidx.navigation.fragment.NavHostFragment"。在傳統的單Activity多Fragment場景中,我們往往需要為Activity添加一個FrameLayout作為Fragment的容器。在Navigation中HavHostFragment就是Fragment的容器(HavHostFragment繼承了NavHost。The NavHost interface enables destinations to be swapped in and out.),其中設置app:navGraph="@navigation/bottom_navigation"使之與navigation graph建立聯系。

        <fragment
            android:
            android:name="androidx.navigation.fragment.NavHostFragment"        
            app:defaultNavHost="true"
            app:navGraph="@navigation/bottom_navigation"
            other_config="..." />
    

    app:defaultNavHost: If set to true, the navigation host will intercept the Back button.

    最后將導航欄與Navigation關聯

    val navController = findNavController(R.id.nav_host_fragment)
    bottomNavigationView.setupWithNavController(navController)
    

    如此便大功告成了。

    如果不依賴導航欄,而是手動跳轉,則可以使用NavController的相關方法,比如在Activity里navController.navigate(actionId)。

    問題

    Navigation和FragmentTransaction方式最好不要同時使用,它倆的返回堆棧似乎不是同一個,回退時會有問題。不能同時使用還使得下面兩個問題不好解決。

    1. 使用Navigation,Fragment可以相互跳轉沒問題,但狀態丟失了。比如A下滑一定距離后跳轉到B,B回退到A,A的下滑狀態丟失,仍是從頭部開始顯示。

    2. 每次點擊BottomNavigationView的菜單項,對應的Fragment會recreate,這其實不是我們想要的,我們預期的應該是Fragment第一次創建后就一直復用,既保留了當前狀態也不會對后端造成不必要的調用。

    如果使用FragmentTransaction很好處理,只要緩存一個Fragment集合即可(若要保留Fragment的狀態,比如滑動位置,可以使用supportFragmentManager.saveFragmentInstanceState(fragment)fragment.setInitialSavedState(savedState)加載,也可以使用hide/show(fragment)的方式),但用了Navigation后就沒辦法了?梢钥纯碞avigation, Saving fragment state評論區的吐槽,里面也有臨時的一些解決方案(不實用)。

    FragmentTransaction本身也有對狀態信息的處理考量,參看commit(), commitNow()和commitAllowingStateLoss()

    參考資料

    嵌套Fragment的使用及常見錯誤
    Fragment 生命周期和使用
    BottonNavigationView+Fragment切換toolbar標題欄
    手把手教你使用Android官方組件Navigation
    Playing with Navigation Architecture Components
    The Navigation Architecture Component Tutorial: Getting Started
    Handle Complex Navigation Flow with Single-Activity Architecture and Android Jetpack’s Navigation component
    導航到目的地-popUpTo 和 popUpToInclusive
    Difference between add(), replace(), and addToBackStack()

    bk
推土機2019:ImageCombiner - Java服務端圖片合成工具,好用! 萊布尼茨:【從零開始擼一個App】Fragment和導航中的使用 等不到的口琴:億級流量架構之資源隔離思路與方法 TOP生物信息:一文學會常規轉錄組分析 Twittytop:我的2020之路 我愛睡蓮:keepalived-1.3.5+MHA部署mysql集群 程序員養成日記:mysql一張表到底能存多少數據? 等你歸去來:java線程池趣味事:這不是線程池 Linyiwei:C++算法代碼――Tuna Linux中執行shell腳本的4種方法總結 歐洲云計算巨頭機房著火:數據中心被毀,部分客戶數據丟失無法恢復 php中文文字水印亂碼怎么辦 介紹Linux環境下PHP7.0安裝 如何解決php7中文亂碼問題 PHP中十六個魔術方法你都了解嗎 做網賺大師:要了解網絡賺錢的來龍去脈 axure8怎么制作一個敬告彈窗效果? 對于企業來說在選購云服務器時應該從哪些方面進行考慮 curl,使用curl方式獲取頁面所有內容 exit,在exit里輸入文本來顯示信息 javascript 正則表達式,正則表達式的應用領域包括字符串語義分析 css布局,css布局從此進入了grid時代 jiaocheng,ghost是一個用于系統備份和恢復的工具 R語言修改下載安裝包install.package的默認存儲路徑的操作方法 R語言中R-squared與Adjust R-squared參數的解釋 python numpy中mat和matrix的區別 如何在Asp.Net Core中集成ABP Dapper R語言如何實現多元線性回歸 formatnumber,使用fmt:formatNumber標簽格式化el表達式的值 什么是xml,一看就懂的xml介紹
国产av欧美av亚韩av_欧美 日产 国产 首页_婷婷五月色中文字幕的