درک و پیاده‌سازی اصول اولیه

پیمایش نحوه حرکت کاربران در برنامه شما را توصیف می‌کند. کاربران با عناصر رابط کاربری تعامل برقرار می‌کنند، معمولاً با ضربه زدن یا کلیک کردن روی آن‌ها، و برنامه با نمایش محتوای جدید پاسخ می‌دهد. اگر کاربر بخواهد به محتوای قبلی برگردد، از اشاره برگشتن استفاده می‌کند یا روی دکمه برگشتن تک‌ضرب می‌زند.

مدل‌سازی وضعیت پیمایش

روش مناسبی برای مدل‌سازی این رفتار، استفاده از پشته محتوا است. وقتی کاربر به محتوای جدید جلو می‌رود، محتوا به بالای پشته منتقل می‌شود. وقتی از آن محتوا برمی‌گردند، محتوا از پشته خارج می‌شود و محتوای قبلی نمایش داده می‌شود. در اصطلاحات پیمایش، معمولاً به این پشته پشته برگشت گفته می‌شود زیرا نشان‌دهنده محتوایی است که کاربر می‌تواند به آن برگردد.

دکمه کنش صفحه‌کلید نرم‌افزاری (نماد علامت تأیید) که با دایره قرمز مشخص شده است.
شکل ۱. نموداری که نشان می‌دهد پشته برگشت با رویدادهای پیمایش کاربر چگونه تغییر می‌کند.

ایجاد پشته برگشت

در «پیمایش ۳»، پشته برگشت درواقع محتوایی ندارد. درعوض، حاوی ارجاع به محتوا است که به‌عنوان کلید شناخته می‌شود. کلیدها می‌توانند از هر نوعی باشند اما معمولاً کلاس‌های داده ساده و سریال‌شدنی هستند. استفاده از مرجع‌ها به‌جای محتوا مزایای زیر را دارد:

  • پیمایش با فشار دادن کلیدها در پشته پشتی ساده است.
  • تا زمانی که کلیدها سریال‌شدنی باشند، پشته برگشت می‌تواند در فضای ذخیره‌سازی دائمی ذخیره شود و به آن امکان می‌دهد از تغییرات پیکربندی و توقف پردازش جان سالم به‌در ببرد. این کار مهم است زیرا کاربران انتظار دارند از برنامه شما خارج شوند، بعداً به آن برگردند، و از جایی که رها کرده‌اند ادامه دهند و همان محتوا نمایش داده شود. برای اطلاعات بیشتر، ذخیره کردن پشته برگشت را ببینید.

یکی از مفاهیم کلیدی در «میانای برنامه‌سازی کاربردی Navigation 3» این است که مالک پشته برگشت هستید. کتابخانه:

  • انتظار دارد پشته برگشت شما List<T> پشتیبان‌گیری‌شده با وضعیت لحظه‌ای باشد، که در آن T نوع پشته برگشت شما keys است. می‌توانید از Any استفاده کنید یا می‌توانید کلیدهای خودتان را با نوع قوی‌تر ارائه دهید. وقتی اصطلاحات «فشار دادن» یا «بیرون آوردن» را می‌بینید، پیاده‌سازی زیربنایی افزودن یا برداشتن موارد از انتهای فهرست است.
  • پشته برگشت شما را مشاهده می‌کند و وضعیت آن را بااستفاده از NavDisplay در واسط کاربر منعکس می‌کند.

مثال زیر نشان می‌دهد که چگونه کلیدها و پشته برگشت ایجاد کنید و پشته برگشت را در پاسخ به رویدادهای پیمایش کاربر اصلاح کنید:

// Define keys that will identify content
data object ProductList
data class ProductDetail(val id: String)

@Composable
fun MyApp() {

    // Create a back stack, specifying the key the app should start with
    val backStack = remember { mutableStateListOf<Any>(ProductList) }

    // Supply your back stack to a NavDisplay so it can reflect changes in the UI
    // ...more on this below...

    // Push a key onto the back stack (navigate forward), the navigation library will reflect the change in state
    backStack.add(ProductDetail(id = "ABC"))

    // Pop a key off the back stack (navigate back), the navigation library will reflect the change in state
    backStack.removeLastOrNull()
}

کلیدهای محتوا را حل کنید

محتوا در «ناوبری ۳» بااستفاده از NavEntry مدل‌سازی می‌شود، که یک کلاس حاوی یک تابع ترکیب‌شدنی است. این دکمه نشان‌دهنده مقصد است - یک محتوای واحد که کاربر می‌تواند به‌جلو و به‌عقب در آن پیمایش کند.

NavEntry همچنین می‌تواند حاوی فراداده - اطلاعاتی درباره محتوا - باشد. این فراداده می‌تواند توسط اشیای حاوی، مانند NavDisplay، خوانده شود تا به آن‌ها کمک کند تصمیم بگیرند چگونه محتوای NavEntry را نمایش دهند. برای مثال، از فراداده می‌توان برای ملغی کردن پویانمایی‌های پیش‌فرض برای NavEntry خاصی استفاده کرد. ‫NavEntry metadata نقشه‌ای از String کلید به Any مقدار است که ذخیره‌سازی داده‌های چندمنظوره را فراهم می‌کند.

برای تبدیل key به NavEntry، «ارائه‌دهنده ورودی» ایجاد کنید. این تابعی است که key را می‌پذیرد و NavEntry را برای آن key برمی‌گرداند. معمولاً هنگام ایجاد NavDisplay به‌عنوان پارامتر لامبدا تعریف می‌شود.

دو روش برای ایجاد «ارائه‌دهنده ورودی» وجود دارد، یا با ایجاد مستقیم تابع لامبدا یا بااستفاده از entryProvider DSL.

ایجاد مستقیم تابع «ارائه‌دهنده ورودی»

معمولاً بااستفاده از دستور when، تابع «ارائه‌دهنده ورودی» ایجاد می‌کنید و برای هریک از کلیدهایتان شاخه‌ای درنظر می‌گیرید.

entryProvider = { key ->
    when (key) {
        is ProductList -> NavEntry(key) { Text("Product List") }
        is ProductDetail -> NavEntry(
            key,
            metadata = mapOf("extraDataKey" to "extraDataValue")
        ) { Text("Product ${key.id} ") }

        else -> {
            NavEntry(Unit) { Text(text = "Invalid Key: $it") }
        }
    }
}

از entryProvider DSL استفاده کنید

entryProvider DSL می‌تواند با اجتناب از نیاز به آزمایش در برابر هریک از انواع کلید و ساختن NavEntry برای هریک از آن‌ها، تابع لامبدای شما را ساده کند. برای این کار از تابع سازنده entryProvider استفاده کنید. اگر کلید پیدا نشود، رفتار جایگزین پیش‌فرض (خطا دادن) را نیز شامل می‌شود.

entryProvider = entryProvider {
    entry<ProductList> { Text("Product List") }
    entry<ProductDetail>(
        metadata = mapOf("extraDataKey" to "extraDataValue")
    ) { key -> Text("Product ${key.id} ") }
}

موارد زیر را از تکه‌کد یادداشت کنید:

  • از entry برای تعریف NavEntry با نوع و محتوای ترکیب‌پذیر داده‌شده استفاده می‌شود
  • ‫entry پارامتر metadata را برای تنظیم NavEntry.metadata می‌پذیرد

نمایش پشته برگشت

پشته برگشت نشان‌دهنده وضعیت پیمایش برنامه شما است. هرگاه پشته برگشت تغییر می‌کند، واسط کاربر برنامه باید وضعیت پشته برگشت جدید را منعکس کند. در «پیمایش ۳»، NavDisplay پشته برگشت شما را مشاهده می‌کند و براین اساس واسط کاربر خود را به‌روز می‌کند. آن را با پارامترهای زیر بسازید:

  • پشته برگشت شما - این باید از نوع SnapshotStateList<T> باشد، که در آن T نوع کلیدهای پشته برگشت شما است. این یک List قابل‌مشاهده است بنابراین وقتی تغییر می‌کند، NavDisplay را بازترکیب می‌کند.
  • entryProvider برای تبدیل کلیدهای پشته برگشت به NavEntry اشیا.
  • درصورت تمایل، یک لامبدا به پارامتر onBack ارائه دهید. این تابع زمانی فراخوانده می‌شود که کاربر رویداد برگشت را راه‌اندازی کند.

مثال زیر نحوه ایجاد NavDisplay را نشان می‌دهد.

data object Home
data class Product(val id: String)

@Composable
fun NavExample() {

    val backStack = remember { mutableStateListOf<Any>(Home) }

    NavDisplay(
        backStack = backStack,
        onBack = { backStack.removeLastOrNull() },
        entryProvider = { key ->
            when (key) {
                is Home -> NavEntry(key) {
                    ContentGreen("Welcome to Nav3") {
                        Button(onClick = {
                            backStack.add(Product("123"))
                        }) {
                            Text("Click to navigate")
                        }
                    }
                }

                is Product -> NavEntry(key) {
                    ContentBlue("Product ${key.id} ")
                }

                else -> NavEntry(Unit) { Text("Unknown route") }
            }
        }
    )
}

به‌طور پیش‌فرض، NavDisplay بالاترین NavEntry را در پشته پشتی در طرح‌بندی یک قاب نشان می‌دهد. ضبط زیر نشان می‌دهد که این برنامه درحال اجرا است:

رفتار پیش‌فرض `NavDisplay` با دو مقصد.
شکل ۲. NavDisplay رفتار پیش‌فرض با دو مقصد.

چرخه حیات مقصد

‫NavDisplay از سفارشی‌سازی LifecycleOwners برای محدود کردن وضعیت چرخه حیات یک NavEntry براساس هر دو محدودیت سطح صحنه و محدودیت‌های سطح ورودی استفاده می‌کند.

برای اطلاعات بیشتر درباره چرخه‌های حیات در Compose، به چرخه حیات در Jetpack Compose مراجعه کنید.

محدودیت‌های چرخه حیات سطح صحنه

‫NavDisplay چرخه حیات Scene فعال را مدیریت می‌کند. سقف‌های سطح صحنه به این صورت تعیین می‌شوند:

برای صحنه‌های غیرپوششی:

  • RESUMED: فقط زمانی مجاز است که انتقال صحنه انجام شده باشد و هیچ صحنه رونهاد فعالی روی آن نمایش داده نشود.
  • STARTED: هنگام گذر صحنه، مانند زمانی که به جلو یا عقب پیمایش می‌کنید، یا زمانی که با رونهادی پوشیده می‌شود، در STARTED محدود می‌شود.

برای صحنه‌های رونهاد، مانند کادرهای گفتگو یا برگ‌های زیرین:

  • RESUMED: فقط برای صحنه رونهاد فعال فعلی در بالاترین سطح مجاز است.
  • STARTED: برای هر صحنه رونهاد زیرین که با رونهاد جدیدتری پوشش داده می‌شود، در STARTED محدود می‌شود.

وضعیت چرخه حیات سطح ورودی

کتابخانه حداکثر وضعیت چرخه حیات هر NavEntry را براساس حضور آن در پشته برگشت مدیریت می‌کند:

  • RESUMED: اگر ورودی در پشته برگشت فعلی وجود داشته باشد، چرخه حیات آن مجاز است تا RESUMED پیش برود (مشروط به سقف سطح صحنه).
  • CREATED: اگر ورودی دیگر در پشته برگشت نباشد، مثلاً وقتی که بیرون کشیده شده است اما همچنان درحال پرداز در صفحه‌نمایش است درحالی‌که درحال پویانمایی شدن است، کتابخانه چرخه عمر آن را دقیقاً در CREATED محدود می‌کند. این محدودیت تضمین می‌کند که ورودی‌های پس‌زمینه یا خروجی از اجرای کار فعال مانند جمع‌آوری جریان‌ها یا راه‌اندازی روتین‌های فرعی مرتبط با وضعیت‌های RESUMED یا STARTED درحین تکمیل انتقال‌های خروج خود متوقف شوند.

نحوه ترکیب آن‌ها

برای مثال، وضعیت چرخه زندگی نهایی NavEntry به‌صورت زیر حل‌وفصل می‌شود:

سناریو سقف سطح صحنه سقف سطح ورودی سقف مؤثر
ورودی فعال، صفحه ثابت (بدون گذار یا رونهاد) RESUMED RESUMED RESUMED
ورود فعال، درطول انتقال (پیمایش به یا از) STARTED RESUMED STARTED
ورودی فعال، پوشیده‌شده با رونهاد (برای مثال، «کادر گفتگو» باز است) STARTED RESUMED STARTED
ورودی بازشده، درحال پویانمایی برای بسته شدن STARTED یا RESUMED CREATED CREATED

درحال سرهم کردن

نمودار زیر نحوه جریان داده‌ها بین اشیای مختلف در Navigation 3 را نشان می‌دهد:

تصویرسازی از نحوه جریان داده‌ها بین اشیای مختلف در «پیمایش ۳».
شکل ۳. نموداری که نشان می‌دهد داده‌ها چگونه ازطریق اشیای مختلف در «پیمایش ۳» جریان می‌یابند.
  1. رویدادهای پیمایش تغییرات را آغاز می‌کنند. کلیدها در پاسخ به تعاملات کاربر به پشته پشتی اضافه یا از آن برداشته می‌شوند.

  2. تغییر در وضعیت پشته برگشت باعث بازیابی محتوا می‌شود. NavDisplay (عنصر ترکیبی که پشته برگشت را پرداز می‌کند) پشته برگشت را مشاهده می‌کند. در پیکربندی پیش‌فرض، این عنصر ورودی پشته برگشت بالای صفحه را در چیدمان تک‌پانه‌ای نمایش می‌دهد. وقتی کلید بالای پشته برگشت تغییر می‌کند، NavDisplay از این کلید برای درخواست محتوای مربوطه از ارائه‌دهنده ورودی استفاده می‌کند.

  3. ارائه‌دهنده ورودی محتوا را تأمین می‌کند. ارائه‌دهنده ورودی تابعی است که کلیدی را به NavEntry تبدیل می‌کند. پس‌از دریافت کلید از NavDisplay، ارائه‌دهنده ورودی NavEntry مرتبط را ارائه می‌دهد که هم کلید و هم محتوا را دربرمی‌گیرد.

  4. محتوا نمایش داده می‌شود. ‫NavDisplay NavEntry را دریافت می‌کند و محتوا را نمایش می‌دهد.