การนำทางและกลุ่มย้อนกลับ

NavController มี "Back Stack" ซึ่งมีปลายทางที่ผู้ใช้เคยเข้าชม เมื่อผู้ใช้ไปยังหน้าจอต่างๆ ในแอป NavController จะเพิ่มและนำปลายทางออกจาก Back Stack

Back Stack เป็นโครงสร้างข้อมูลแบบ "เข้าทีหลังออกก่อน" เนื่องจากเป็นสแต็ก ดังนั้น NavController จะพุชรายการไปยังด้านบนของสแต็กและป๊อปรายการออกจากด้านบนของสแต็ก

ลักษณะการทำงานพื้นฐาน

ต่อไปนี้คือข้อเท็จจริงหลักๆ ที่คุณควรพิจารณาเกี่ยวกับลักษณะการทำงานของสแต็กย้อนกลับ

  • ปลายทางแรก: เมื่อผู้ใช้เปิดแอป NavController จะพุชปลายทางแรกไปยังด้านบนของ Back Stack
  • การพุชไปยังสแต็ก: การเรียก NavController.navigate() แต่ละครั้งจะพุช ปลายทางที่ระบุไปยังด้านบนของสแต็ก
  • การป๊อปปลายทางด้านบน: การแตะ ขึ้น หรือ กลับ จะเรียก NavController.navigateUp() และ NavController.popBackStack() ตามลำดับ ซึ่งจะป๊อปปลายทางด้านบนออกจากสแต็ก ดูข้อมูลเพิ่มเติมเกี่ยวกับความแตกต่าง ระหว่าง ขึ้น กับ กลับ ได้ที่หน้า หลักการนำทาง

ป๊อปย้อนกลับ

เมธอด NavController.popBackStack() จะพยายามป๊อปปลายทางปัจจุบันออกจาก Back Stack และไปยังปลายทางก่อนหน้า ซึ่งจะย้ายผู้ใช้กลับไป 1 ขั้นตอนในประวัติการนำทาง เมธอดนี้จะแสดงผลเป็นบูลีนที่ระบุว่าป๊อปย้อนกลับไปยังปลายทางสำเร็จหรือไม่

ป๊อปย้อนกลับไปยังปลายทางที่เฉพาะเจาะจง

นอกจากนี้ คุณยังใช้ popBackStack() เพื่อไปยังปลายทางที่เฉพาะเจาะจงได้ด้วย โดยใช้การโอเวอร์โหลดรายการใดรายการหนึ่ง ซึ่งมีหลายรายการที่อนุญาตให้คุณส่ง ตัวระบุ เช่น จำนวนเต็ม id หรือสตริง route การโอเวอร์โหลดเหล่านี้จะนำผู้ใช้ไปยังปลายทางที่เชื่อมโยงกับตัวระบุที่ระบุ ที่สำคัญคือการโอเวอร์โหลดเหล่านี้จะป๊อปทุกอย่างในสแต็กที่อยู่เหนือปลายทางนั้น

การโอเวอร์โหลดเหล่านี้ยังใช้บูลีน inclusive ด้วย ซึ่งจะกำหนดว่า NavController ควรป๊อปปลายทางที่ระบุออกจาก Back Stack ด้วยหรือไม่หลังจากไปยังปลายทางนั้นแล้ว

ดูตัวอย่างข้อมูลโค้ดสั้นๆ ต่อไปนี้

navController.popBackStack(R.id.destinationId, true)

ในตัวอย่างนี้ NavController จะป๊อปย้อนกลับไปยังปลายทางที่มีรหัส destinationId เป็นจำนวนเต็ม เนื่องจากค่าของอาร์กิวเมนต์ inclusive เป็น true NavController จึงป๊อปปลายทางที่ระบุออกจาก Back Stack ด้วย

จัดการการป๊อปย้อนกลับที่ไม่สำเร็จ

เมื่อ popBackStack() แสดงผลเป็น false การเรียก NavController.getCurrentDestination() ในภายหลังจะแสดงผลเป็น null ซึ่งหมายความว่าแอปได้ป๊อปปลายทางสุดท้ายออกจาก Back Stack แล้ว ในกรณีนี้ ผู้ใช้จะเห็นเฉพาะหน้าจอว่างเปล่า

เหตุการณ์นี้อาจเกิดขึ้นในกรณีต่อไปนี้

  • popBackStack() ไม่ได้ป๊อปสิ่งใดออกจากสแต็ก
  • popBackStack() ป๊อปปลายทางออกจาก Back Stack และตอนนี้สแต็กว่างเปล่า

หากต้องการแก้ไขปัญหานี้ คุณต้องไปยังปลายทางใหม่หรือเรียก finish() ในกิจกรรมเพื่อสิ้นสุดกิจกรรม ข้อมูลโค้ดต่อไปนี้แสดงให้เห็นถึงการดำเนินการนี้

kotlin

...

if (!navController.popBackStack()) {
    // Call finish() on your Activity
    finish()
}

java

...

if (!navController.popBackStack()) {
    // Call finish() on your Activity
    finish();
}

ป๊อปขึ้นไปยังปลายทาง

หากต้องการนำปลายทางออกจาก Back Stack เมื่อไปยังปลายทางหนึ่งไปยังอีกปลายทางหนึ่ง ให้เพิ่มอาร์กิวเมนต์ popUpTo() ลงในการเรียกใช้ฟังก์ชัน navigate() ที่เกี่ยวข้อง popUpTo() จะสั่งให้ไลบรารีการนำทางนำปลายทางบางรายการออกจาก Back Stack ซึ่งเป็นส่วนหนึ่งของการเรียกใช้ navigate() ค่าพารามิเตอร์คือตัวระบุของปลายทางใน Back Stack ตัวระบุอาจเป็น จำนวนเต็ม id หรือสตริง route

คุณสามารถใส่อาร์กิวเมนต์สำหรับพารามิเตอร์ inclusive ที่มีค่าเป็น true เพื่อระบุว่าปลายทางที่คุณระบุใน popUpTo() ควรป๊อปออกจาก Back Stack ด้วย

หากต้องการใช้ฟีเจอร์นี้แบบเป็นโปรแกรม ให้ส่ง popUpTo() ไปยัง navigate() ซึ่งเป็นส่วนหนึ่งของ NavOptions โดยตั้งค่า inclusive เป็น true ซึ่งจะใช้ได้ทั้งใน Compose และ Views

บันทึกสถานะเมื่อป๊อปขึ้น

เมื่อใช้ popUpTo เพื่อไปยังปลายทาง คุณสามารถเลือกบันทึก Back Stack และสถานะของปลายทางทั้งหมดที่ป๊อปออกจาก Back Stack ได้ จากนั้นคุณจะกู้คืน Back Stack และปลายทางได้เมื่อไปยังปลายทางนั้นในภายหลัง ซึ่งจะช่วยให้คุณเก็บรักษาสถานะของปลายทางที่กำหนดและมี สแต็กย้อนกลับหลายรายการได้

หากต้องการดำเนินการนี้แบบเป็นโปรแกรม ให้ระบุ saveState = true เมื่อเพิ่ม popUpTo ลงในตัวเลือกการนำทาง

นอกจากนี้ คุณยังระบุ restoreState = true ในตัวเลือกการนำทางเพื่อกู้คืน Back Stack และสถานะที่เชื่อมโยงกับปลายทางโดยอัตโนมัติได้ด้วย

เช่น

navController.navigate(
    route = route,
    navOptions =  navOptions {
        popUpTo<A>{ saveState = true }
        restoreState = true
    }
)

หากต้องการเปิดใช้การบันทึกและกู้คืนสถานะใน XML ให้กำหนด popUpToSaveState เป็น true และ restoreState เป็น true ตามลำดับใน action ที่เกี่ยวข้อง

ตัวอย่าง XML

ต่อไปนี้คือตัวอย่าง popUpTo ใน XML โดยใช้การดำเนินการ

<action
  android:id="@+id/action_a_to_b"
  app:destination="@id/b"
  app:popUpTo="@+id/a"
  app:popUpToInclusive="true"
  app:restoreState=”true”
  app:popUpToSaveState="true"/>

ตัวอย่าง Compose

ต่อไปนี้คือตัวอย่างที่สมบูรณ์ของโค้ดเดียวกันใน Compose

@Composable
fun MyAppNavHost(
    modifier: Modifier = Modifier,
    navController: NavHostController = rememberNavController(),
    startDestination: Any = A
) {
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = startDestination
    ) {
        composable<A> {
            DestinationA(
                onNavigateToB = {
                // Pop everything up to, and including, the A destination off
                // the back stack, saving the back stack and the state of its
                // destinations.
                // Then restore any previous back stack state associated with
                // the B destination.
                // Finally navigate to the B destination.
                    navController.navigate(route = B) {
                        popUpTo<A> {
                            inclusive = true
                            saveState = true
                        }
                        restoreState = true
                    }
                },
            )
        }
        composable<B> { DestinationB(/* ... */) }
    }
}

@Composable
fun DestinationA(onNavigateToB: () -> Unit) {
    Button(onClick = onNavigateToB) {
        Text("Go to A")
    }
}

คุณสามารถเปลี่ยนวิธีเรียก NavController.navigate() ได้ละเอียดยิ่งขึ้นด้วยวิธีต่อไปนี้

// Pop everything up to the destination_a destination off the back stack before
// navigating to the "destination_b" destination
navController.navigate("destination_b") {
    popUpTo("destination_a")
}

// Pop everything up to and including the "destination_a" destination off
// the back stack before navigating to the "destination_b" destination
navController.navigate("destination_b") {
    popUpTo("destination_a") { inclusive = true }
}

// Navigate to the "search” destination only if we’re not already on
// the "search" destination, avoiding multiple copies on the top of the
// back stack
navController.navigate("search") {
    launchSingleTop = true
}

ดูข้อมูลทั่วไปเกี่ยวกับการส่งตัวเลือกไปยัง NavController.navigate() ได้ที่ คู่มือการนำทางพร้อมตัวเลือก

ป๊อปโดยใช้การดำเนินการ

เมื่อนำทางโดยใช้การดำเนินการ คุณสามารถเลือกป๊อปปลายทางเพิ่มเติมออกจาก Back Stack ได้ ตัวอย่างเช่น หากแอปมีขั้นตอนการเข้าสู่ระบบครั้งแรก เมื่อผู้ใช้เข้าสู่ระบบแล้ว คุณควรป๊อปปลายทางทั้งหมดที่เกี่ยวข้องกับการเข้าสู่ระบบออกจาก Back Stack เพื่อไม่ให้ปุ่มย้อนกลับนำผู้ใช้กลับไปที่ขั้นตอนการเข้าสู่ระบบ

อ่านเพิ่มเติม

อ่านข้อมูลเพิ่มเติมได้ในหน้าต่อไปนี้

  • การนำทางแบบวนซ้ำ: ดูวิธีหลีกเลี่ยงสแต็กย้อนกลับ ที่บรรจุข้อมูลมากเกินไปในกรณีที่ขั้นตอนการนำทางเป็นแบบวนซ้ำ
  • ปลายทางของกล่องโต้ตอบ: อ่านเกี่ยวกับวิธีที่ปลายทางของกล่องโต้ตอบทำให้เกิดข้อควรพิจารณาที่ไม่เหมือนใครในการจัดการ Back Stack