Android Jetpack 架構元件之 Navigation
一、關鍵欄位的解釋
Kotlin語言環境下在Fragment裡面操作控制元件的注意事項:
在onCreateView()方法和onStart()方法裡面直接通過id操作控制元件的話,會要求必須加findViewById()獲取控制元件id,否則就會報"must not be null"這個錯誤。
但是在onViewCreated()方法裡面就不用這樣。另外自定義的方法裡面也是必須獲取控制元件id。
可以這麼理解:
在Fragment裡面,除了在onViewCreated()方法裡面直接操作控制元件或者呼叫操作控制元件的方法外,其他任何地方操作控制元件或者呼叫操作控制元件的方法都是需要通過findViewById()獲取控制元件id,否者就會報上面的錯誤。
Navigation各欄位含義:
1、Activity對應的xml裡面,fragment標籤示例以及幾個關鍵字的含義:
<fragment
android:id="@+id/nav_fragment"
android:layout_width="match_parent"
android:layout_height=" match_parent "
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/nav_host"
app:defaultNavHost="true"/>
android:id:在Activity裡面用於通過supportFragmentManager獲取NavHostFragment物件。
android:name:是NavHostFragment,它實現了NavHost,這是一個用於放置管理destination的空檢視。指向NavHost的實現類NavHostFragment。
app:navGraph:指向Navigation graph配置檔案,用於將NavHostFragment和nav_graph.xml關聯起來。
app:defaultNavHost:預設值為false,當該值為false的時候,當前Activity中的Fragment使用了Navigation,且使用Navigation跳轉到下一個Fragment,在下一個Fragment頁面中點選了Back鍵會退出當前Activity。為true的時候是回到上一個Fragment中,如果上一個Fragment為null才會退出當前Activity。類似於我們處理WebView的back事件。
2、navigation資料夾下nav_host.xml檔案示例及裡面關鍵欄位的含義:
<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/nav_host"
app:startDestination="@id/homeFragment"
tools:ignore="UnusedNavigation">
<fragment
android:id="@+id/homeFragment"
android:name="com.androidjetpack.navigation.HomeFragment"
android:label="home_dest"
tools:layout="@layout/home_dest" >
<action
android:id="@+id/action_homeFragment_to_oneFragment"
app:destination="@id/oneFragment" />
</fragment>
<fragment
android:id="@+id/oneFragment"
android:name="com.androidjetpack.navigation.OneFragment"
android:label="OneFragment"
tools:layout="@layout/flow_step_one_dest">
<!-- action標籤不是必須的,比如後面的fragment標籤就沒有action標籤,尤其是最後一個fragment -->
<!-- action裡面id的規則:action_本fragment的id_to_下一個fragment的id -->
<action
android:id="@+id/action_oneFragment_to_deepLinkFragment"
app:destination="@+id/deepLinkFragment"/>
</fragment>
<fragment
android:id="@+id/deepLinkFragment"
android:name="com.androidjetpack.navigation.DeepLinkFragment"
android:label="DeepLinkFragment"
tools:layout="@layout/deep_link_dest">
<!-- argument標籤不是必須的 -->
<argument
android:name="flowStepNumber"
app:argType="integer"
android:defaultValue="1"/>
</fragment>
</navigation>
name:指定Fragment的路徑。
tools:layout:指定佈局檔案,如果不配置該屬性在Design皮膚會看不見預覽。
action:字面理解就是動作,作用是fragment之間進行切換。
app:startDestination:指定這個Fragment是start-destination。
app:destination:目的地,指定要跳轉到Fragment的id。
app:id:定義這個action的id,程式碼裡執行跳轉時要用到。
enterAnim、exitAnim、popEnterAnim、popExitAnim:是頁面切換和彈窗動畫
如果某個Fragment是最後一個Fragment,那麼我們就可以直接省略他的action。
二、Navigation的使用步驟
1、app的build.gradle匯入依賴:
def nav_version = "2.3.0"
implementation "androidx.navigation:navigation-fragment:$nav_version"
implementation "androidx.navigation:navigation-ui:$nav_version"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
implementation "androidx.navigation:navigation-dynamic-features-fragment:$nav_version"
androidTestImplementation "androidx.navigation:navigation-testing:$nav_version"
並在這個build.gradle裡面新增:
apply plugin: "androidx.navigation.safeargs"
2、在專案工程的build.gradle新增如下依賴:
classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0"
3、新建幾個Fragment並關聯佈局:
ZeroFragment及對應的xml佈局檔案:
xml檔案佈局
<TextView xmlns:android=http://schemas.android.com/apk/res/android
xmlns:tools=http://schemas.android.com/tools
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:text="@string/deep"
android:textSize="32sp"
tools:ignore="HardcodedText" />
kotlin程式碼實現:
class ZeroFragment :Fragment(){
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_zero, container, false)
}
}
OneFragment及對應的xml佈局檔案:
xml檔案佈局:
<LinearLayout xmlns:android=http://schemas.android.com/apk/res/android
xmlns:tools=http://schemas.android.com/tools
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:ignore="HardcodedText">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="50dp"
android:text="@string/one"
android:textSize="32sp" />
</LinearLayout>
kotlin程式碼實現:
class OneFragment :Fragment(){
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_one, container, false)
}
}
HomeFragment及對應的xml佈局檔案:
xml檔案佈局:
<LinearLayout xmlns:android=http://schemas.android.com/apk/res/android
xmlns:tools=http://schemas.android.com/tools
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context="com.androidjetpack.navigation.HomeFragment"
tools:ignore="HardcodedText">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="50dp"
android:text="@string/home"
android:textSize="32sp" />
<TextView
android:id="@+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="@color/black"
android:padding="12dp"/>
</LinearLayout>
kotlin程式碼實現:
class HomeFragment :Fragment(){
var mActivity:NavigationPrimaryActivity? = null
override fun onAttach(context: Context) {
super.onAttach(context)
mActivity = (context as NavigationPrimaryActivity)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_home, container, false)
}
//在onCreateView()方法和onStart()方法裡面直接通過id操作控制元件的話,會要求必須加findViewById()獲取控制元件id,
//否則就會報"must not be null"這個錯誤。
//但是在onViewCreated()方法裡面就不用這樣。另外自定義的方法裡面也是必須獲取控制元件id。
//可以這麼理解:在Fragment裡面,除了在onViewCreated()方法裡面直接操作控制元件或者呼叫操作控制元件的方法外,
//其他任何地方操作控制元件或者呼叫操作控制元件的方法都是需要通過findViewById()獲取控制元件id,否者就會報上面的錯誤。
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
operationView()
}
fun operationView() {
open1.setOnClickListener {
mActivity?.printLoginfo("不能滑動,沒什麼鳥用")
}
tvTitle.text = "皇者號天令"
}
}
4、新建Navigation graph資原始檔
1)、在工程的res資料夾下建立新的資料夾:navigation;
2)、單擊navigation資料夾,右鍵選單選擇New->Navigation Resource File,在File name後面輸入檔名:nav_host,選擇Resource type為Navigation,點選ok即可建立Navigation graph資原始檔:nav_host.xml。
3)、把新建的Fragment引入到該資原始檔內:
按照上面截圖的順序,在上面第二步建立的nav_host.xml檔案裡面依次操作。在標記有3的搜尋框處搜尋要新增的Fragment,然後從Design模式切換到Split模式,在裡面編輯功能:跳轉的順序、action行為活動細節等。最終程式碼如下:
<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/nav_host"
app:startDestination="@id/homeFragment"
tools:ignore="UnusedNavigation">
<fragment
android:id="@+id/homeFragment"
android:name="com.androidjetpack.navigation.HomeFragment"
android:label="home_dest"
tools:layout="@layout/fragment_home" >
<action
android:id="@+id/action_homeFragment_to_oneFragment"
app:destination="@id/oneFragment" />
</fragment>
<fragment
android:id="@+id/oneFragment"
android:name="com.androidjetpack.navigation.OneFragment"
android:label="OneFragment"
tools:layout="@layout/fragment_one">
<!--action標籤不是必須的,比如後面的fragment標籤就沒有action標籤,尤其是最後一個fragment-->
<!--action裡面id的規則:action_本fragment的id_to_下一個fragment的id-->
<action
android:id="@+id/action_oneFragment_to_deepLinkFragment"
app:destination="@+id/deepLinkFragment"/>
</fragment>
<fragment
android:id="@+id/deepLinkFragment"
android:name="com.androidjetpack.navigation.ZeroFragment"
android:label="DeepLinkFragment"
tools:layout="@layout/fragment_zero">
<!-- 下面的argument標籤不是必須的 -->
<!--argument:類似於Activity的跳轉傳參,只不過傳參取參更加方便簡單,
如果某個Fragment要接收從別的Fragment傳過來的引數,那麼可以通過argument標籤處理,
如果我們在kotlin程式碼裡面沒有傳參,那麼我們獲取到的就是通過argument標籤設定的預設值,具體見下面。-->
<!--下面defaultValue的型別必須與欄位argType必須保持一致,比如下面argType="integer",defaultValue="1"-->
<argument
android:name="flowStepNumber"
app:argType="integer"
android:defaultValue="1"/>
</fragment>
</navigation>
argument:
類似於Activity的跳轉傳參,只不過傳參取參更加方便簡單,如果某個Fragment要接收從別的Fragment傳過來的引數,那麼可以通過argument標籤處理,如果我們在kotlin程式碼裡面沒有傳參,那麼我們獲取到的就是通過argument標籤設定的預設值,具體如下:
//傳值且跳轉
val flowStepNumberArg=1
val action = HomeFragmentDirections.nextAction(flowStepNumberArg)
findNavController().navigate(action)
//取值
val safeArgs: FlowStepFragmentArgs by navArgs()
val flowStepNumber = safeArgs.flowStepNumber
5、建立menu選單:bottom_nav_menu.xml
<menu xmlns:android=http://schemas.android.com/apk/res/android
xmlns:app="http://schemas.android.com/apk/res-auto">
<!--這裡的id對應著nav_host.xml裡面的fragment標籤的id-->
<!--這裡的item的順序對應著nav_host.xml裡面的fragment的順序-->
<!--showsAction屬性解釋:
1、alaways:這個值會使選單項一直顯示在ActionBar上。
2、ifRoom:如果有足夠的空間,這個值會使選單顯示在ActionBar上。
3、never:這個值選單永遠不會出現在ActionBar是。
4、withText:這個值使選單和它的圖示,選單文字一起顯示。
5、collapseActionView 宣告瞭這個操作視窗應該被摺疊到一個按鈕中,
當使用者選擇這個按鈕時,這個操作視窗展開。否則,這個操作視窗在預設的情況下是可見的,
並且即便在用於不適用的時候,也要佔據操作欄的有效空間。-->
<item
android:id="@+id/homeFragment"
android:icon="@mipmap/home"
android:title="home"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/oneFragment"
android:icon="@mipmap/focus"
android:title="one" />
<item
android:id="@+id/deepLinkFragment"
android:icon="@mipmap/fire"
android:title="deep" />
</menu>
注意:
這裡的item.id和Navigation graph資原始檔中的fragment.id必須保持一致,否則,恭喜入坑。
6、最後是Fragment的寄主NavigationPrimaryActivity和activity_navigation_primary.xml的處理:
<LinearLayout
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:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/container"
android:orientation="vertical"
tools:context=".navigation.NavigationPrimaryActivity">
<!--android:name:是NavHostFragment,它實現了NavHost,這是一個用於放置管理destination的空檢視。指向NavHost的實現類NavHostFragment-->
<!--app:navGraph:指向Navigation graph配置檔案,用於將NavHostFragment和nav_graph.xml關聯起來。-->
<!--app:defaultNavHost:預設值為false,當該值為false的時候,當前Activity中的Fragment使用了Navigation,
且使用Navigation跳轉到下一個Fragment,在下一個Fragment頁面中點選了Back鍵會退出當前Activity。
為true的時候是回到上一個Fragment中,如果上一個Fragment為null才會退出當前Activity。類似於我們處理WebView的back事件。-->
<fragment
android:id="@+id/nav_fragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/nav_host"
app:defaultNavHost="true"/>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav_view"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@color/write"
app:menu="@menu/bottom_nav_menu"/>
</LinearLayout>
上面fragment標籤的id在Activity裡面將會用到,這一點要特別注意,其餘注意事項以及說明,程式碼裡面解釋的很清楚了。
接下來就是Activity裡面的邏輯處理,一共分四步:
class NavigationPrimaryActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_navigation_primary)
//第一步獲取到NavHostFragment,這裡的nav_fragment就是本Activity對應的xml裡面Fragment標籤的id。
val host: NavHostFragment = supportFragmentManager.findFragmentById(R.id.nav_fragment) as NavHostFragment? ?: return
//第二步獲取到NavController,這一步的內容幾乎都是死的。
val navController = host.navController
//第三步配置BottomNavigationView,這裡的bottom_nav_view就是Activity對應的xml裡面BottomNavigationView控制元件的id。
val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_nav_view)
//這裡,將導航控制元件BottomNavigationView與navController關聯起來。
bottomNav?.setupWithNavController(navController)
//第四步新增路由監聽,這一步的內容幾乎都是死的。
navController.addOnDestinationChangedListener { _, destination, _ ->
val dest: String = try {
resources.getResourceName(destination.id)
} catch (e: Resources.NotFoundException) {
destination.id.toString()
}
toast("Navigated to $dest")
}
}
}
要注意一個細節:
第三部其實分兩個細節,一是初始化BottomNavigationView的id,二是將導航控制元件BottomNavigationView與navController關聯起來。執行效果如下:
7、跳轉傳參
雖然Fragment之間傳遞引數的機率比較低,但是多少還是會用到。
1)、無參跳轉
在HomeFragment中的onViewCreated(view: View, savedInstanceState: Bundle?)函式中新增以下程式碼:
//其中,id值action_homeFragment_to_oneFragment是我們在nav_host.xml資源內新增的action屬性。
//這裡是定義了一個無參跳轉:homeFragment_to_oneFragment
open1.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_homeFragment_to_oneFragment))
上面定義了一個從homeFragment跳轉到oneFragment的動作。
其中R.id. action_homeFragment_to_oneFragment是我們在nav_host資源內新增的action屬性的id
R.id. action_homeFragment_to_oneFragment中:
action:意指活動;
homeFragment_to_oneFragment:從homeFragment到oneFragment
2)、帶參跳轉
這個目前尚未研究成功,無法編譯生成帶參跳轉的過載方法,待後面成功了在完成這部分內容。
最後來說一個關於帶參跳轉的問題的解決方案,問題如下:
Cannot inline bytecode built with JVM target 1.8 into bytecode that is being built with JVM target 1.6. Please specify proper '-jvm-target' option.
android {
......
compileOptions {
sourceCompatibility = 1.8
targetCompatibility = 1.8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
新增上面加粗標紅的程式碼。然後按下圖操作即可完成:
相關文章
- Android Jetpack架構元件(七)之WorkManagerAndroidJetpack架構元件
- Android Jetpack 之Navigation Architecture Component使用AndroidJetpackNavigation
- Android Jetpack Navigation基本使用AndroidJetpackNavigation
- Jetpack 之 Navigation 初探JetpackNavigation
- Android官方架構元件Navigation:大巧不工的Fragment管理框架Android架構元件NavigationFragment框架
- Jetpack路由元件學習:深入理解功能強大的Navigation架構之接管系統的返回操作Jetpack路由元件Navigation架構
- Android Jetpack Navigation 深入體驗報告AndroidJetpackNavigation
- 剖析 Android 架構元件之 ViewModelAndroid架構元件View
- Android 官方架構元件之 LifecycleAndroid架構元件
- Android Jetpack元件之Lifecycles庫詳解AndroidJetpack元件
- Android Jetpack - 使用 Navigation 管理頁面跳轉AndroidJetpackNavigation
- 初學 Android 架構元件之 LifecycleAndroid架構元件
- 初學 Android 架構元件之 ViewModelAndroid架構元件View
- Android_Jetpack:Paging元件之BoundaryCallback的使用AndroidJetpack元件
- Jetpack架構元件學習(1)——LifeCycle的使用Jetpack架構元件
- Android 官方元件 Navigation 初使用Android元件Navigation
- Android JetPack 簡介及 Work Manager 和 Navigation 元件詳述 | Google 開發者大會AndroidJetpackNavigation元件Go
- Jetpack架構元件學習(3)——Activity Results API使用Jetpack架構元件API
- Jetpack架構元件學習(2)——ViewModel和Livedata使用Jetpack架構元件ViewLiveData
- Android Jetpack 之 LiveDataAndroidJetpackLiveData
- Android Jetpack之ViewModelAndroidJetpackView
- Android Jetpack 之 LifecycleAndroidJetpack
- Android Jetpack 之 ViewModelAndroidJetpackView
- Android Jetpack元件之資料庫Room詳解(一)AndroidJetpack元件資料庫OOM
- Android 元件化架構概要Android元件化架構
- Android新元件架構——LifecylceAndroid元件架構
- Jetpack Navigation----原始碼解析JetpackNavigation原始碼
- Android MVVM元件化架構方案AndroidMVVM元件化架構
- Android 官方架構元件(一)——LifecycleAndroid架構元件
- Android 官方架構元件(二)——LiveDataAndroid架構元件LiveData
- Android 官方架構元件(三)——ViewModelAndroid架構元件View
- Android架構元件-DataBinding的使用Android架構元件
- Android架構元件WorkManager詳解Android架構元件
- 《Android元件化架構》上市了Android元件化架構
- Android 架構元件 - Lifycycle, LiveData, ViewModelAndroid架構元件LiveDataView
- Android官方架構元件之LiveData + ViewModel + Room 原始碼分析一Android架構元件LiveDataViewOOM原始碼
- Android Jetpack Architecture原理之ViewModelAndroidJetpackView
- Jetpack 之 LifeCycle 元件使用詳解Jetpack元件