Navigation是指允許用戶在應用程序中瀏覽、進入和退出不同內容的Fragment 。Navigation能夠實現從簡單的按鈕單擊到更復雜的模式,如應用程序欄和導航抽屜。導航組件還通過遵循一套既定的原則來確保一致和可預測的用戶體驗。
Navigation的原則:
- 固定的起始位置:除去登陸等一次性界面,用戶在啟動App最先看到的界面需要為一個固定的起始界面。
- Navigation的表現形式為堆棧形式:Navigation類似於棧,棧頂為用戶所看到的界面,界面的切換總是在棧頂進行,導航到目標后,目標位於棧頂。
- 標題欄的回退應該和返回鍵的功能一樣,但是標題欄不能退出應用:這個也很容易理解,因為兩個的定義就存在本質的區別。
- Deep Link:比如從瀏覽器跳轉到其他App時,用戶首先看到的應該還是瀏覽器的那個頁面,沒有其他界面的跳轉過程。
Navigation有三個主要的部分:
- Navigation graph:就是Navigatation的xml文件,包含所有的需要跳轉的目標
- NavHost:一個容器,用於顯示
- NavController:控制跳轉流程
具體實現(簡單例子)
首先新建新建兩個Fragment,HomeFragment和DetailFragment
在其對應生成的xml文件中添加一些控件
frament_home.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout 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" tools:context=".HomeFragment"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" app:layout_constraintGuide_begin="335dp" app:layout_constraintGuide_percent="0.5" /> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" android:text="Home" android:textSize="36sp" app:layout_constraintBottom_toTopOf="@+id/guideline" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.8" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" android:text="Button" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/guideline" app:layout_constraintVertical_bias="0.2" /> </androidx.constraintlayout.widget.ConstraintLayout> </FrameLayout>
fragment_detail.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout 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" tools:context=".DetailFragment"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" app:layout_constraintGuide_begin="348dp" app:layout_constraintGuide_percent="0.5" /> <TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" android:text="Detail" android:textSize="36sp" app:layout_constraintBottom_toTopOf="@+id/guideline2" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.8" /> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" android:text="Button" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/guideline2" app:layout_constraintVertical_bias="0.2" /> </androidx.constraintlayout.widget.ConstraintLayout> </FrameLayout>
接着在activity_main中為Fragment添加NavHost容器
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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" tools:context=".MainActivity"> <fragment android:id="@+id/fragment" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="409dp" android:layout_height="729dp" app:defaultNavHost="true" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:navGraph="@navigation/my_nav_graph" /> </androidx.constraintlayout.widget.ConstraintLayout>
再新建navigation文件夾新建my_nav_graph,添加Fragment到其中
建立Fragment之間的跳轉關系,當然也可以直接在xml文件里寫,不過這樣會有點麻煩。
HomeFragment.java
package com.example.navigationdemo; import android.os.Bundle; import androidx.fragment.app.Fragment; import androidx.navigation.NavController; import androidx.navigation.Navigation; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import com.google.android.material.internal.NavigationMenuItemView; /** * A simple {@link Fragment} subclass. */ public class HomeFragment extends Fragment { public HomeFragment() { // Required empty public constructor } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_home, container, false); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); Button button; button = getView().findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { NavController controller = Navigation.findNavController(v); controller.navigate(R.id.action_homeFragment_to_detailFragment); } }); } }
DetaiFragment.java
package com.example.navigationdemo; import android.os.Bundle; import androidx.fragment.app.Fragment; import androidx.navigation.Navigation; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; /** * A simple {@link Fragment} subclass. */ public class DetailFragment extends Fragment { public DetailFragment() { // Required empty public constructor } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_detail, container, false); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); getView().findViewById(R.id.button2).setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_detailFragment_to_homeFragment)); } }
這樣就能實現兩個界面之間的跳轉。
如果想要實現手機屏幕左上角 的返回功能,則需要在MainActivity中添加代碼
package com.example.navigationdemo; import androidx.appcompat.app.AppCompatActivity; import androidx.navigation.NavController; import androidx.navigation.Navigation; import androidx.navigation.ui.NavigationUI; import android.os.Bundle; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); NavController controller = Navigation.findNavController(this, R.id.fragment); NavigationUI.setupActionBarWithNavController(this, controller); } @Override public boolean onSupportNavigateUp() { NavController controller = Navigation.findNavController(this, R.id.fragment); return controller.navigateUp(); //return super.onSupportNavigateUp(); } }
Fragement間數據傳遞
這里也沒有很大的變化,也是通過Bundle來傳遞的,然后接收方通過getArguments()來獲取到對應的Bundle從而取出數據。
Fragment轉換動畫
是可以直接在Navigation graph中直接設置轉換動畫,極大減少了工作量。當然如果對自帶動畫效果不滿意,也可以創建Animation資源自己編寫xml文件來創建動畫。
但要切記,在實際工程中不要把轉換動畫設置的太過花哨,以免影響用戶的操作體驗。