คอมโพเนนต์การนำทางให้วิธีต่างๆ ในการสร้างและโต้ตอบโดยใช้โปรแกรม กับองค์ประกอบการนำทางบางอย่าง
สร้าง NavHostFragment
คุณสามารถใช้
NavHostFragment.create()
เพื่อสร้าง NavHostFragment
แบบเป็นโปรแกรม ด้วยแหล่งข้อมูลกราฟที่เฉพาะเจาะจง
ดังที่แสดงในตัวอย่างด้านล่าง
Kotlin
val finalHost = NavHostFragment.create(R.navigation.example_graph) supportFragmentManager.beginTransaction() .replace(R.id.nav_host, finalHost) .setPrimaryNavigationFragment(finalHost) // equivalent to app:defaultNavHost="true" .commit()
Java
NavHostFragment finalHost = NavHostFragment.create(R.navigation.example_graph); getSupportFragmentManager().beginTransaction() .replace(R.id.nav_host, finalHost) .setPrimaryNavigationFragment(finalHost) // equivalent to app:defaultNavHost="true" .commit();
โปรดทราบว่า setPrimaryNavigationFragment(finalHost)
ช่วยให้NavHost
สกัดกั้นการกดปุ่มย้อนกลับของระบบ นอกจากนี้ คุณยังใช้ลักษณะการทำงานนี้ใน
XML ของ NavHost
โดยเพิ่ม app:defaultNavHost="true"
หากคุณกำลังใช้
ลักษณะการทำงานของปุ่มย้อนกลับที่กำหนดเอง
และไม่ต้องการให้ NavHost
สกัดกั้นการกดปุ่ม "ย้อนกลับ" คุณก็สามารถข้าม
null
ถึง setPrimaryNavigationFragment()
อ้างอิงปลายทางโดยใช้ NavBackStackEntry
เริ่มต้นด้วยการนำทาง 2.2.0 คุณสามารถรับการอ้างอิง
NavBackStackEntry
สำหรับปลายทางใดๆ ในสแต็กการนำทางโดยการเรียกใช้
NavController.getBackStackEntry()
,
ไปให้ถึงรหัสปลายทาง หากสแต็กด้านหลังมีอินสแตนซ์มากกว่า 1 รายการ
ของปลายทางที่ระบุ getBackStackEntry()
จะแสดงผลอินสแตนซ์ที่อยู่บนสุด
จากกองซ้อน
NavBackStackEntry
ที่แสดงผลระบุ
Lifecycle
ViewModelStore
และ
SavedStateRegistry
ที่ระดับปลายทาง ออบเจ็กต์เหล่านี้จะใช้ได้ตลอดอายุการใช้งานของปลายทางในกลุ่มแบ็ก เมื่อปลายทางที่เกี่ยวข้องถูกดึงออกจาก
Back Stack, Lifecycle
จะถูกทำลาย, ระบบจะไม่บันทึกสถานะอีกต่อไป และล้างออบเจ็กต์ ViewModel
พร็อพเพอร์ตี้เหล่านี้จะให้ Lifecycle
และร้านค้าสำหรับออบเจ็กต์ ViewModel
รายการและ
ชั้นเรียนที่ใช้ได้
สถานะที่บันทึกไว้ ไม่ว่าจะ
ประเภทปลายทางที่คุณใช้ ซึ่งจะเป็นประโยชน์อย่างยิ่งเมื่อทำงานร่วมกับ
ประเภทปลายทางที่ไม่มี Lifecycle
เชื่อมโยงโดยอัตโนมัติ
เช่น ปลายทางที่กำหนดเอง
ตัวอย่างเช่น คุณสามารถสังเกต Lifecycle
ของ NavBackStackEntry
ได้เช่นเดียวกับ
คุณจะเห็น Lifecycle
ของส่วนย่อยหรือกิจกรรม นอกจากนี้
NavBackStackEntry
เป็น LifecycleOwner
ซึ่งหมายความว่าคุณจะใช้บัญชีนี้ได้เมื่อ
สังเกต LiveData
หรือคอมโพเนนต์อื่นๆ ที่รับรู้ถึงวงจร ดังที่แสดงใน
ตัวอย่างต่อไปนี้
Kotlin
myViewModel.liveData.observe(backStackEntry, Observer { myData -> // react to live data update })
Java
myViewModel.getLiveData().observe(backStackEntry, myData -> { // react to live data update });
สถานะของวงจรจะอัปเดตโดยอัตโนมัติทุกครั้งที่คุณเรียกใช้ navigate()
สถานะของอายุการใช้งานสำหรับปลายทางที่ไม่ได้อยู่ที่ด้านบนสุดของแบ็กเอนด์
ย้ายจาก RESUMED
ไปยัง STARTED
หากปลายทางยังคงปรากฏภายใต้
ปลายทาง FloatingWindow
เช่น ปลายทางของกล่องโต้ตอบ หรือไปยัง STOPPED
หรือไม่เช่นนั้น
การแสดงผลลัพธ์ไปยังปลายทางก่อนหน้า
ในการนำทาง 2.3 ขึ้นไป NavBackStackEntry
จะให้สิทธิ์เข้าถึง
SavedStateHandle
SavedStateHandle
เป็นการแมปคีย์-ค่าที่ใช้จัดเก็บและเรียกข้อมูลได้
ค่าเหล่านี้จะยังคงอยู่แม้จะผ่านกระบวนการตายไปแล้ว ซึ่งรวมถึงการกำหนดค่า
และยังคงเข้าถึงได้ผ่านออบเจ็กต์เดียวกัน โดยการใช้ตัวระบุ
SavedStateHandle
คุณสามารถเข้าถึงและส่งข้อมูลระหว่างปลายทางได้
ซึ่งจะเป็นประโยชน์อย่างยิ่งในการเป็นกลไกในการดึงข้อมูลกลับจาก
ปลายทางหลังจากนำออกจากกอง
หากต้องการส่งข้อมูลกลับไปยังปลายทาง A จากปลายทาง B ก่อนอื่นให้
ตั้งค่าปลายทาง A เพื่อฟังผลลัพธ์ใน SavedStateHandle
ในการดำเนินการนี้ ให้เรียก NavBackStackEntry
โดยใช้
getCurrentBackStackEntry()
API แล้ว observe
ต่อ LiveData
โดย SavedStateHandle
Kotlin
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val navController = findNavController(); // We use a String here, but any type that can be put in a Bundle is supported navController.currentBackStackEntry?.savedStateHandle?.getLiveData<String>("key")?.observe( viewLifecycleOwner) { result -> // Do something with the result. } }
Java
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { NavController navController = NavHostFragment.findNavController(this); // We use a String here, but any type that can be put in a Bundle is supported MutableLiveData<String> liveData = navController.getCurrentBackStackEntry() .getSavedStateHandle() .getLiveData("key"); liveData.observe(getViewLifecycleOwner(), new Observer<String>() { @Override public void onChanged(String s) { // Do something with the result. } }); }
![ปลายทาง B สามารถใช้ getPreviousBackStackEntry() เพื่อดึงข้อมูล
NavBackStackEntry สำหรับปลายทาง A ก่อนหน้า](https://developer.android.google.cn/static/images/guide/navigation/nav-for-result.png?authuser=7&hl=th)
ในปลายทาง B คุณต้องset
ผลลัพธ์ใน SavedStateHandle
ของ
ปลายทาง A โดยใช้ getPreviousBackStackEntry()
API
Kotlin
navController.previousBackStackEntry?.savedStateHandle?.set("key", result)
Java
navController.getPreviousBackStackEntry().getSavedStateHandle().set("key", result);
หากต้องการจัดการผลการค้นหาเพียงครั้งเดียว คุณต้องโทร
remove()
ใน SavedStateHandle
เพื่อล้างผลลัพธ์ หากไม่นำ
ผลลัพธ์ LiveData
จะยังคงแสดงผลลัพธ์ล่าสุดไปยัง
อินสแตนซ์ Observer
รายการใหม่
ข้อควรพิจารณาเมื่อใช้ปลายทางของกล่องโต้ตอบ
เมื่อคุณnavigate
ไปยังจุดหมายที่ต้องการเห็นมุมมองทั้งหมดของ
NavHost
(เช่น ปลายทาง <fragment>
) จุดหมายก่อนหน้า
หยุดวงจรแล้ว ทำให้ไม่มีการเรียกกลับไปยัง LiveData
โดย SavedStateHandle
แต่เมื่อไปยัง
ปลายทางของกล่องโต้ตอบ
ปลายทางก่อนหน้ายังปรากฏบนหน้าจอ ดังนั้นจึง
STARTED
แม้ว่าจะไม่ใช่ปลายทางปัจจุบัน ซึ่งหมายความว่าการโทรหา
getCurrentBackStackEntry()
จากภายในวิธีตลอดอายุการใช้งาน เช่น
onViewCreated()
จะแสดงผล NavBackStackEntry
ของปลายทางกล่องโต้ตอบ
หลังการเปลี่ยนแปลงการกำหนดค่าหรือประมวลผลการเสียชีวิตและการสันทนาการ (ตั้งแต่กล่องโต้ตอบ
จะคืนค่าเหนือปลายทางอื่น) ดังนั้นคุณควรใช้
getBackStackEntry()
รหัสปลายทางของคุณเพื่อให้คุณ
ใช้ URL ที่ถูกต้อง
NavBackStackEntry
และยังหมายความว่า Observer
ใดก็ตามที่คุณตั้งค่าไว้ในผลลัพธ์ LiveData
จะ
ทริกเกอร์แม้ปลายทางของกล่องโต้ตอบจะยังอยู่บนหน้าจอ หากคุณ
ต้องการตรวจสอบผลลัพธ์เฉพาะเมื่อปลายทางของกล่องโต้ตอบปิดอยู่และ
ปลายทางที่สำคัญจะกลายเป็นปลายทางปัจจุบัน คุณสามารถสังเกต
Lifecycle
ที่เชื่อมโยงกับ NavBackStackEntry
และเรียกดูผลลัพธ์
เมื่อเปลี่ยนเป็น RESUMED
เท่านั้น
Kotlin
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val navController = findNavController(); // After a configuration change or process death, the currentBackStackEntry // points to the dialog destination, so you must use getBackStackEntry() // with the specific ID of your destination to ensure we always // get the right NavBackStackEntry val navBackStackEntry = navController.getBackStackEntry(R.id.your_fragment) // Create our observer and add it to the NavBackStackEntry's lifecycle val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME && navBackStackEntry.savedStateHandle.contains("key")) { val result = navBackStackEntry.savedStateHandle.get<String>("key"); // Do something with the result } } navBackStackEntry.lifecycle.addObserver(observer) // As addObserver() does not automatically remove the observer, we // call removeObserver() manually when the view lifecycle is destroyed viewLifecycleOwner.lifecycle.addObserver(LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_DESTROY) { navBackStackEntry.lifecycle.removeObserver(observer) } }) }
Java
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); NavController navController = NavHostFragment.findNavController(this); // After a configuration change or process death, the currentBackStackEntry // points to the dialog destination, so you must use getBackStackEntry() // with the specific ID of your destination to ensure we always // get the right NavBackStackEntry final NavBackStackEntry navBackStackEntry = navController.getBackStackEntry(R.id.your_fragment); // Create our observer and add it to the NavBackStackEntry's lifecycle final LifecycleEventObserver observer = new LifecycleEventObserver() { @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { if (event.equals(Lifecycle.Event.ON_RESUME) && navBackStackEntry.getSavedStateHandle().contains("key")) { String result = navBackStackEntry.getSavedStateHandle().get("key"); // Do something with the result } } }; navBackStackEntry.getLifecycle().addObserver(observer); // As addObserver() does not automatically remove the observer, we // call removeObserver() manually when the view lifecycle is destroyed getViewLifecycleOwner().getLifecycle().addObserver(new LifecycleEventObserver() { @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { if (event.equals(Lifecycle.Event.ON_DESTROY)) { navBackStackEntry.getLifecycle().removeObserver(observer) } } }); }
แชร์ข้อมูลที่เกี่ยวข้องกับ UI ระหว่างปลายทางด้วย ViewModel
สแต็กย้อนกลับของการนำทางจะจัดเก็บ
NavBackStackEntry
ไม่เพียงสำหรับปลายทางแต่ละแห่ง แต่สำหรับการนำทางระดับบนสุดแต่ละรายการ
ที่มีปลายทางแต่ละแห่ง ซึ่งจะช่วยให้คุณเรียกข้อมูล
NavBackStackEntry
ที่มีขอบเขตเป็นกราฟการนำทาง การนำทาง
NavBackStackEntry
ที่มีขอบเขตเป็นกราฟให้วิธีสร้าง ViewModel
ที่
ที่กำหนดขอบเขตเป็นกราฟการนำทาง เพื่อช่วยให้คุณแชร์ข้อมูลที่เกี่ยวข้องกับ UI ระหว่าง
ปลายทางของกราฟ ออบเจ็กต์ ViewModel
ใดๆ ที่สร้างขึ้นในลักษณะนี้จะทำงานจนถึงวันที่
NavHost
ที่เชื่อมโยงและViewModelStore
จะถูกล้าง หรือจนกว่า
กราฟการนำทางถูกดึงมาจากกลุ่มด้านหลัง
ตัวอย่างต่อไปนี้แสดงวิธีเรียกข้อมูล ViewModel
ที่กำหนดขอบเขตเป็น
กราฟการนำทาง:
Kotlin
val viewModel: MyViewModel by navGraphViewModels(R.id.my_graph)
Java
NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.my_graph); MyViewModel viewModel = new ViewModelProvider(backStackEntry).get(MyViewModel.class);
หากคุณใช้การนำทาง 2.2.0 หรือรุ่นก่อนหน้า คุณต้องระบุข้อมูล จากโรงงานที่จะใช้ สถานะที่บันทึกไว้ด้วย ViewModels, ดังที่ปรากฏในตัวอย่างต่อไปนี้
Kotlin
val viewModel: MyViewModel by navGraphViewModels(R.id.my_graph) { SavedStateViewModelFactory(requireActivity().application, requireParentFragment()) }
Java
NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.my_graph); ViewModelProvider viewModelProvider = new ViewModelProvider( backStackEntry.getViewModelStore(), new SavedStateViewModelFactory( requireActivity().getApplication(), requireParentFragment())); MyViewModel myViewModel = provider.get(myViewModel.getClass());
ดูข้อมูลเพิ่มเติมเกี่ยวกับ ViewModel
ได้ที่
ดูภาพรวมของโมเดล
การแก้ไขกราฟการนำทางที่พองขึ้น
คุณสามารถแก้ไขกราฟการนำทางที่สูงเกินจริงแบบไดนามิกในระหว่างรันไทม์
ตัวอย่างเช่น หากคุณมี
BottomNavigationView
ที่เชื่อมโยงกับ NavGraph
ซึ่งเป็นปลายทางเริ่มต้นของฟิลด์
NavGraph
จะบอกแท็บที่เลือกเมื่อเริ่มต้นแอป อย่างไรก็ตาม คุณอาจ
ลบล้างลักษณะการทำงานนี้ เช่น เมื่อค่ากำหนดของผู้ใช้ระบุ
แท็บที่ต้องการให้โหลดเมื่อเริ่มต้นแอป อีกวิธีหนึ่งคือ แอปอาจ
จำเป็นต้องเปลี่ยนแท็บเริ่มต้นตามพฤติกรรมของผู้ใช้ในอดีต คุณสามารถ
รองรับกรณีเหล่านี้ด้วยการระบุปลายทางเริ่มต้นของ
NavGraph
ลองพิจารณา NavGraph
นี้:
<?xml version="1.0" encoding="utf-8"?> <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_graph" app:startDestination="@id/home"> <fragment android:id="@+id/home" android:name="com.example.android.navigation.HomeFragment" android:label="fragment_home" tools:layout="@layout/fragment_home" /> <fragment android:id="@+id/location" android:name="com.example.android.navigation.LocationFragment" android:label="fragment_location" tools:layout="@layout/fragment_location" /> <fragment android:id="@+id/shop" android:name="com.example.android.navigation.ShopFragment" android:label="fragment_shop" tools:layout="@layout/fragment_shop" /> <fragment android:id="@+id/settings" android:name="com.example.android.navigation.SettingsFragment" android:label="fragment_settings" tools:layout="@layout/fragment_settings" /> </navigation>
เมื่อโหลดกราฟนี้ แอตทริบิวต์ app:startDestination
จะระบุ
HomeFragment
ก็จะแสดง วิธีลบล้างปลายทางเริ่มต้น
แบบไดนามิก ให้ทำดังต่อไปนี้
- ก่อนอื่น ให้เพิ่ม
NavGraph
ด้วยตนเอง - ลบล้างปลายทางเริ่มต้น
- สุดท้าย ให้แนบกราฟเข้ากับ
NavController
ด้วยตนเอง
Kotlin
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment val navController = navHostFragment.navController val navGraph = navController.navInflater.inflate(R.navigation.bottom_nav_graph) navGraph.startDestination = R.id.shop navController.graph = navGraph binding.bottomNavView.setupWithNavController(navController)
Java
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager() .findFragmentById(R.id.nav_host_fragment); NavController navController = navHostFragment.getNavController(); NavGraph navGraph = navController.getNavInflater().inflate(R.navigation.bottom_nav_graph); navGraph.setStartDestination(R.id.shop); navController.setGraph(navGraph); NavigationUI.setupWithNavController(binding.bottomNavView, navController);
ตอนนี้เมื่อแอปเริ่มทำงาน ShopFragment
จะแสดงแทน HomeFragment
เมื่อใช้ Deep Link NavController
จะสร้างแบ็กสแต็ก
โดยอัตโนมัติสำหรับปลายทางของ Deep Link หากผู้ใช้
นำทางไปที่ลิงก์ในรายละเอียด แล้วย้อนกลับมาหา
เริ่มต้นที่จุดใดจุดหนึ่ง การลบล้างปลายทางเริ่มต้นโดยใช้
ในตัวอย่างก่อนหน้านี้ จะช่วยให้การเริ่ม
ปลายทางจะถูกเพิ่มลงในสแต็กด้านหลังที่สร้างขึ้น
โปรดทราบว่าเทคนิคนี้ยังทำให้สามารถลบล้างองค์ประกอบ
NavGraph
ตามที่จำเป็น ต้องทำการแก้ไขกราฟทั้งหมด
ก่อนที่จะเรียกไปยัง setGraph()
เพื่อให้แน่ใจว่าโครงสร้างที่ถูกต้อง
ใช้เมื่อจัดการ Deep Link, คืนค่าสถานะ และย้ายไปยังจุดเริ่มต้น
ปลายทางของกราฟของคุณ