自定義Navigator切換fragment
使用場景:
使用Navigation完成fragment間的跳轉操作。
問題描述:
Navigation 實現 fragment 間的跳轉用的是replace()方法,此方法會移除原來的fragment,再新增新的fragment,所以回到上一個fragment時就需要重新走一遍生命週期流程,重新載入資料。
解決方案:
分析 NavController類 中的navigate 原始碼
private void navigate(@NonNull NavDestination node, @Nullable Bundle args,
@Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
...
Navigator<NavDestination> navigator = mNavigatorProvider.getNavigator(
node.getNavigatorName()); 根據節點名稱生成不同的navigator
Bundle finalArgs = node.addInDefaultArgs(args);
NavDestination newDest = navigator.navigate(node, finalArgs,
navOptions, navigatorExtras); 呼叫navigator 中的 navigate方法
...
}
getNavigator 原始碼如下
private final HashMap<String, Navigator<? extends NavDestination>> mNavigators =
new HashMap<>();
public <T extends Navigator<?>> T getNavigator(@NonNull String name) {
if (!validateName(name)) {
throw new IllegalArgumentException("navigator name cannot be an empty string");
}
Navigator<? extends NavDestination> navigator = mNavigators.get(name); // 根據傳入的node獲取不同的navigator
if (navigator == null) {
throw new IllegalStateException("Could not find Navigator with name \"" + name
+ "\". You must call NavController.addNavigator() for each navigation type.");
}
return (T) navigator;
}
因此,想要呼叫自定義navigate()方法就需要 自定義一個Navigator類,同時改變fragment節點名稱,將fragment節點名稱與Navigator類作為key,value 新增到 HashMap型別的mNavigators中。
自定義Navigator
@Navigator.Name("custom_fragment") // 節點名稱定義為custom_fragment,作為 mNavigatorProvider 變數的 key
class CustomNavigator( //作為 mNavigatorProvider 變數的 value
private val context: Context,
private val manager: FragmentManager,
private val containerId: Int
) : FragmentNavigator(context, manager, containerId) {
override fun navigate( // 重寫 navigate 方法
destination: Destination,
args: Bundle?,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
): NavDestination? {
val tag = destination.id.toString() // 跳轉目的地
val transaction = manager.beginTransaction() // 開啟fragment事務
val currentFragment = manager.primaryNavigationFragment // navigation 頂層fragment
if (currentFragment != null) {
transaction.hide(currentFragment) // 隱藏當前fragment
}
var fragment = manager.findFragmentByTag(tag) // 找到目的地fragment
if (fragment == null) { // fragment未被初始化
val className = destination.className
fragment = manager.fragmentFactory.instantiate(context.classLoader, className) // 例項化 fragment
transaction.add(containerId, fragment, tag) // 將碎片新增到容器中
} else { // fragment 已經初始化過了
transaction.show(fragment) // 顯示fragment
}
transaction.setPrimaryNavigationFragment(fragment) //將fragment 設定為頂層fragment
transaction.setReorderingAllowed(true)
transaction.commitNow() // 提交事務
return destination // 返回目的地,用於監聽
}
}
修改節點名稱
因為節點名稱定義為custom_fragment,所以修改為<custom_fragment>
<navigation 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/my_nav"
app:startDestination="@id/home_dest"
tools:ignore="UnusedNavigation">
<custom_fragment
android:id="@+id/home_dest"
android:name="com.cl.androidstudy.ui.home.HomeFragment"
android:label="fragment_home"
tools:layout="@layout/fragment_home" />
<custom_fragment
android:id="@+id/system_dest"
android:name="com.cl.navicationtest.SystemFragment"
android:label="fragment_system"
tools:layout="@layout/fragment_system" />
<custom_fragment
android:id="@+id/square_dest"
android:name="com.cl.navicationtest.SquareFragment"
android:label="fragment_square"
tools:layout="@layout/fragment_square" />
<custom_fragment
android:id="@+id/me_dest"
android:name="com.cl.androidstudy.ui.me.MeFragment"
android:label="fragment_me"
tools:layout="@layout/fragment_me" />
</navigation>
邏輯程式碼
val navController = Navigation.findNavController(this, R.id.fragment) // 建立navController
val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragment)!!
val navigator = CustomNavigator(
this,
navHostFragment.childFragmentManager,
R.id.fragment
)// 生成自定義Navigator物件
navController.navigatorProvider.addNavigator("custom_fragment", navigator) // 新增 key, value
navController.setGraph(R.navigation.my_nav) // 要在 CustomNavigator 類被載入之後新增graph,不然找不到 custom_fragment節點
相關文章
- win10怎麼自定義背景圖切換_win10自定義背景圖片隨機切換的步驟Win10隨機
- Flutter自定義實現神奇的卡片切換檢視Flutter
- 利用ViewPager和Fragment實現頁卡切換ViewpagerFragment
- 使用fragment載入自定義fragment出現error inflating class fragment錯誤解決辦法FragmentError
- CSS自定義屬性與前端頁面的主題切換CSS前端
- Android中Fragment巢狀Fragment,切換Fragment時不顯示檢視的原因及解決方法AndroidFragment巢狀
- 安卓開發:viewpager + fragment 實現滑動切換安卓ViewpagerFragment
- 如何在自定義 Tool Bar 和 Tab Bar 之間切換顯示
- Spring Boot中自定義註解+AOP實現主備庫切換Spring Boot
- app直播原始碼,自定義兩種Activity切換動畫實現APP原始碼動畫
- Android使用(TabLayout+ViewPager+fragment)與(FragmentTabHost+ViewPager+Fragment)實現底部狀態列切換AndroidTabLayoutViewpagerFragment
- 自定義值轉換器
- vue-video-player,通過自定義按鈕元件實現全屏切換效果VueIDE元件
- Android技術分享| 自定義ViewGroup實現直播間大小屏無縫切換AndroidView
- 直播商城系統原始碼,播放器aliPlayer自定義清晰度切換原始碼播放器
- 2. Jetpack原始碼解析---Navigation為什麼切換Fragment會重繪?Jetpack原始碼NavigationFragment
- 自定義註解完成資料庫切庫資料庫
- 自定義 Behavior,實現巢狀滑動、平滑切換周月檢視的日曆巢狀
- Bartender 4:圖示顯示切換大變樣,還能在選單欄自定義文字
- Django(6)自定義路由轉換器Django路由
- 【PageLayout】非常簡單的一鍵切換載入-空資料-錯誤頁,支援自定義
- MyBatis使用自定義TypeHandler轉換型別MyBatis型別
- Spring Boot之自定義JSON轉換器Spring BootJSON
- Android-Fragment 切換造成記憶體溢位,導致記憶體增長AndroidFragment記憶體溢位
- ViewPager、Fragment和TabLayout實現切頁效果ViewpagerFragmentTabLayout
- TabLayout+ViewPager+Fragment實現切頁展示TabLayoutViewpagerFragment
- 微信小程式自定義tabbar圖示切換點選兩次才選中解決方法微信小程式tabBar
- 直播系統程式碼,自定義軟鍵盤樣式:字母、數字、標點三種切換
- 程式切換(上下文切換)
- Flutter100行輕鬆實現自定義P站和油管的Logo及自由切換Logo功能FlutterGo
- 基於vue3+electron11實現QQ登入切換|自定義導航欄|托盤|打包Vue
- [譯]Workcation App – 第一部分 . 自定義 Fragment 轉場動畫APPFragment動畫
- springmvc 自定義訊息轉換器完整例子SpringMVC
- Mybatis使用小技巧-自定義型別轉換器MyBatis型別
- oracle sqldeveloper下 自定義Snippets檔案的替換OracleSQLDeveloper
- C#自定義控制元件—轉換開關C#控制元件
- 棧切換
- vue2.0-基於elementui換膚[自定義主題]VueUI