ناوبری برای رابط های کاربری پاسخگو

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

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

مؤلفه Jetpack Navigation اصول ناوبری را اجرا می کند و می تواند برای تسهیل توسعه برنامه ها با رابط های کاربری پاسخگو استفاده شود.

شکل 1. نمایشگرهای بزرگ، متوسط ​​و فشرده با کشوی ناوبری، ریل و نوار پایین.

ناوبری UI پاسخگو

اندازه پنجره نمایش اشغال شده توسط یک برنامه بر ارگونومی و قابلیت استفاده تأثیر می گذارد. کلاس‌های اندازه پنجره به شما امکان می‌دهند عناصر ناوبری مناسب (مانند میله‌های ناوبری، ریل‌ها یا کشوها) را تعیین کنید و آنها را در جایی قرار دهید که بیشتر در دسترس کاربر هستند. در دستورالعمل‌های طرح‌بندی Material Design، عناصر ناوبری فضای ثابتی را در لبه جلویی نمایشگر اشغال می‌کنند و زمانی که عرض برنامه فشرده است می‌توانند به لبه پایین حرکت کنند. انتخاب عناصر ناوبری شما تا حد زیادی به اندازه پنجره برنامه و تعداد مواردی که عنصر باید نگه دارد بستگی دارد.

کلاس اندازه پنجره چند مورد بسیاری از موارد
عرض فشرده نوار ناوبری پایین کشوی ناوبری (لبه جلو یا پایین)
عرض متوسط ریل ناوبری کشوی ناوبری (لبه پیشرو)
عرض منبسط شده ریل ناوبری کشوی ناوبری مداوم (لبه پیشرو)

در طرح‌بندی‌های مبتنی بر نما، فایل‌های منبع طرح‌بندی را می‌توان با نقاط شکست کلاس اندازه پنجره برای استفاده از عناصر ناوبری مختلف برای ابعاد مختلف نمایش واجد شرایط دانست. Jetpack Compose می‌تواند از نقاط شکست ارائه‌شده توسط API کلاس اندازه پنجره استفاده کند تا از نظر برنامه‌ریزی عنصر ناوبری را که برای پنجره برنامه مناسب‌تر است، تعیین کند.

بازدیدها

<!-- res/layout/main_activity.xml -->

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        ... />

    <!-- Content view(s) -->
</androidx.constraintlayout.widget.ConstraintLayout>


<!-- res/layout-w600dp/main_activity.xml -->

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.navigationrail.NavigationRailView
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        ... />

    <!-- Content view(s) -->
</androidx.constraintlayout.widget.ConstraintLayout>


<!-- res/layout-w1240dp/main_activity.xml -->

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.navigation.NavigationView
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        ... />

    <!-- Content view(s) -->
</androidx.constraintlayout.widget.ConstraintLayout>

نوشتن

// This method should be run inside a Composable function.
val widthSizeClass = calculateWindowSizeClass(this).widthSizeClass
// You can get the height of the current window by invoking heightSizeClass instead.

@Composable
fun MyApp(widthSizeClass: WindowWidthSizeClass) {
    // Select a navigation element based on window size.
    when (widthSizeClass) {
        WindowWidthSizeClass.Compact -> { CompactScreen() }
        WindowWidthSizeClass.Medium -> { MediumScreen() }
        WindowWidthSizeClass.Expanded -> { ExpandedScreen() }
    }
}

@Composable
fun CompactScreen() {
    Scaffold(bottomBar = {
                NavigationBar {
                    icons.forEach { item ->
                        NavigationBarItem(
                            selected = isSelected,
                            onClick = { ... },
                            icon = { ... })
                    }
                }
            }
        ) {
        // Other content
    }
}

@Composable
fun MediumScreen() {
    Row(modifier = Modifier.fillMaxSize()) {
        NavigationRail {
            icons.forEach { item ->
                NavigationRailItem(
                    selected = isSelected,
                    onClick = { ... },
                    icon = { ... })
            }
        }
        // Other content
    }
}

@Composable
fun ExpandedScreen() {
    PermanentNavigationDrawer(
        drawerContent = {
            icons.forEach { item ->
                NavigationDrawerItem(
                    icon = { ... },
                    label = { ... },
                    selected = isSelected,
                    onClick = { ... }
                )
            }
        },
        content = {
            // Other content
        }
    )
}

مقاصد محتوای واکنشگرا

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

هنگامی که هر مقصد جداگانه رویدادهای تغییر اندازه را مدیریت می کند، تغییرات در رابط کاربری جدا می شوند. بقیه وضعیت برنامه، از جمله ناوبری، تحت تأثیر قرار نگرفته است.

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

پیمایش به عنوان یک اثر جانبی تغییر اندازه پنجره دارای مشکلات زیر است:

  • مقصد قدیمی (برای اندازه پنجره قبلی) ممکن است برای لحظه ای قبل از حرکت به مقصد جدید قابل مشاهده باشد
  • برای حفظ برگشت پذیری (به عنوان مثال، هنگامی که یک دستگاه تا شده و باز می شود)، ناوبری برای هر اندازه پنجره مورد نیاز است.
  • حفظ وضعیت برنامه بین مقصدها می تواند دشوار باشد، زیرا ناوبری می تواند وضعیت را پس از باز شدن پشت پشتی از بین ببرد

همچنین، برنامه شما ممکن است حتی در زمانی که تغییر اندازه پنجره اتفاق می افتد، در پیش زمینه نباشد. طرح‌بندی برنامه شما ممکن است به فضای بیشتری نسبت به برنامه پیش‌زمینه نیاز داشته باشد، و وقتی کاربر به برنامه شما برمی‌گردد، جهت و اندازه پنجره ممکن است تغییر کرده باشند.

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

مقاصد محتوا با طرح‌بندی‌های جایگزین

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

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

بازدیدها

SlidingPaneLayout به شما امکان می‌دهد یک مقصد ناوبری واحد ایجاد کنید که دو صفحه محتوا را در کنار هم در صفحه‌های بزرگ نمایش می‌دهد، اما در دستگاه‌های با صفحه‌نمایش کوچک مانند تلفن‌ها، هر بار فقط یک صفحه نمایش می‌دهد.

<!-- Single destination for list and detail. -->

<navigation ...>

    <!-- Fragment that implements SlidingPaneLayout. -->
    <fragment
        android:id="@+id/article_two_pane"
        android:name="com.example.app.ListDetailTwoPaneFragment" />

    <!-- Other destinations... -->
</navigation>

برای جزئیات در مورد اجرای طرح‌بندی جزئیات فهرست با استفاده از SlidingPaneLayout به ایجاد طرح‌بندی دو پنجره‌ای مراجعه کنید.

نوشتن

در Compose، یک نمای لیست جزئیات را می توان با ترکیب ترکیب‌پذیرهای جایگزین در یک مسیر واحد که از کلاس‌های اندازه پنجره استفاده می‌کند تا ترکیب‌بندی مناسب برای هر کلاس اندازه را منتشر کند، پیاده‌سازی شود.

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

نمای فهرست جزئیات از سه ترکیب پذیر تشکیل شده است، به عنوان مثال:

/* Displays a list of items. */
@Composable
fun ListOfItems(
    onItemSelected: (String) -> Unit,
) { /*...*/ }

/* Displays the detail for an item. */
@Composable
fun ItemDetail(
    selectedItemId: String? = null,
) { /*...*/ }

/* Displays a list and the detail for an item side by side. */
@Composable
fun ListAndDetail(
    selectedItemId: String? = null,
    onItemSelected: (String) -> Unit,
) {
  Row {
    ListOfItems(onItemSelected = onItemSelected)
    ItemDetail(selectedItemId = selectedItemId)
  }
}

یک مسیر ناوبری واحد دسترسی به نمای فهرست جزئیات را فراهم می کند:

@Composable
fun ListDetailRoute(
    // Indicates that the display size is represented by the expanded window size class.
    isExpandedWindowSize: Boolean = false,
    // Identifies the item selected from the list. If null, a item has not been selected.
    selectedItemId: String?,
) {
  if (isExpandedWindowSize) {
    ListAndDetail(
      selectedItemId = selectedItemId,
      /*...*/
    )
  } else {
    // If the display size cannot accommodate both the list and the item detail,
    // show one of them based on the user's focus.
    if (selectedItemId != null) {
      ItemDetail(
        selectedItemId = selectedItemId,
        /*...*/
      )
    } else {
      ListOfItems(/*...*/)
    }
  }
}

ListDetailRoute (مقصد ناوبری) تعیین می کند که کدام یک از سه ترکیب پذیر را منتشر کند: ListAndDetail برای اندازه پنجره بزرگ. ListOfItems یا ItemDetail برای فشرده، بسته به اینکه یک آیتم فهرست انتخاب شده باشد.

مسیر در NavHost گنجانده شده است، به عنوان مثال:

NavHost(navController = navController, startDestination = "listDetailRoute") {
  composable("listDetailRoute") {
    ListDetailRoute(isExpandedWindowSize = isExpandedWindowSize,
                    selectedItemId = selectedItemId)
  }
  /*...*/
}

می‌توانید آرگومان isExpandedWindowSize با بررسی WindowMetrics برنامه‌تان ارائه کنید.

آرگومان selectedItemId می تواند توسط ViewModel ارائه شود که حالت را در تمام اندازه های پنجره حفظ می کند. هنگامی که کاربر یک مورد را از لیست انتخاب می کند، متغیر حالت selectedItemId به روز می شود:

class ListDetailViewModel : ViewModel() {

  data class ListDetailUiState(
      val selectedItemId: String? = null,
  )

  private val viewModelState = MutableStateFlow(ListDetailUiState())

  fun onItemSelected(itemId: String) {
    viewModelState.update {
      it.copy(selectedItemId = itemId)
    }
  }
}

val listDetailViewModel = ListDetailViewModel()

@Composable
fun ListDetailRoute(
    isExpandedWindowSize: Boolean = false,
    selectedItemId: String?,
    onItemSelected: (String) -> Unit = { listDetailViewModel.onItemSelected(it) },
) {
  if (isExpandedWindowSize) {
    ListAndDetail(
      selectedItemId = selectedItemId,
      onItemSelected = onItemSelected,
      /*...*/
    )
  } else {
    if (selectedItemId != null) {
      ItemDetail(
        selectedItemId = selectedItemId,
        /*...*/
      )
    } else {
      ListOfItems(
        onItemSelected = onItemSelected,
        /*...*/
      )
    }
  }
}

این مسیر همچنین شامل یک BackHandler سفارشی می‌شود، زمانی که جزئیات آیتم قابل ترکیب کل پنجره برنامه را اشغال می‌کند:

class ListDetailViewModel : ViewModel() {

  data class ListDetailUiState(
      val selectedItemId: String? = null,
  )

  private val viewModelState = MutableStateFlow(ListDetailUiState())

  fun onItemSelected(itemId: String) {
    viewModelState.update {
      it.copy(selectedItemId = itemId)
    }
  }

  fun onItemBackPress() {
    viewModelState.update {
      it.copy(selectedItemId = null)
    }
  }
}

val listDetailViewModel = ListDetailViewModel()

@Composable
fun ListDetailRoute(
    isExpandedWindowSize: Boolean = false,
    selectedItemId: String?,
    onItemSelected: (String) -> Unit = { listDetailViewModel.onItemSelected(it) },
    onItemBackPress: () -> Unit = { listDetailViewModel.onItemBackPress() },
) {
  if (isExpandedWindowSize) {
    ListAndDetail(
      selectedItemId = selectedItemId,
      onItemSelected = onItemSelected,
      /*...*/
    )
  } else {
    if (selectedItemId != null) {
      ItemDetail(
        selectedItemId = selectedItemId,
        /*...*/
      )
      BackHandler {
        onItemBackPress()
      }
    } else {
      ListOfItems(
        onItemSelected = onItemSelected,
        /*...*/
      )
    }
  }
}

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

برای اجرای کامل نمای لیست جزئیات در Compose، به نمونه JetNews در GitHub مراجعه کنید.

یک نمودار ناوبری

برای ارائه یک تجربه کاربری ثابت در هر دستگاه یا اندازه پنجره، از یک نمودار ناوبری استفاده کنید که در آن چیدمان هر مقصد محتوا پاسخگو باشد.

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

میزبان ناوبری تو در تو

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

برای پیاده سازی این نوع پیمایش فرعی، پنجره جزئیات می تواند یک میزبان ناوبری تودرتو با نمودار ناوبری خاص خود باشد که مقاصدی را که از پنجره جزئیات به آنها دسترسی دارند را مشخص می کند:

بازدیدها

<!-- layout/two_pane_fragment.xml -->

<androidx.slidingpanelayout.widget.SlidingPaneLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/sliding_pane_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/list_pane"
        android:layout_width="280dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"/>

    <!-- Detail pane is a nested navigation host. Its graph is not connected
         to the main graph that contains the two_pane_fragment destination. -->
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/detail_pane"
        android:layout_width="300dp"
        android:layout_weight="1"
        android:layout_height="match_parent"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:navGraph="@navigation/detail_pane_nav_graph" />
</androidx.slidingpanelayout.widget.SlidingPaneLayout>

نوشتن

@Composable
fun ItemDetail(selectedItemId: String? = null) {
    val navController = rememberNavController()
    NavHost(navController, "itemSubdetail1") {
        composable("itemSubdetail1") { ItemSubdetail1(...) }
        composable("itemSubdetail2") { ItemSubdetail2(...) }
        composable("itemSubdetail3") { ItemSubdetail3(...) }
    }
}

این با یک نمودار ناوبری تو در تو متفاوت است زیرا نمودار ناوبری NavHost تودرتو به گراف ناوبری اصلی متصل نیست. یعنی نمی‌توانید مستقیماً از مقصدهای یک نمودار به مقصد در گراف دیگر پیمایش کنید.

برای اطلاعات بیشتر، نمودارهای پیمایش تودرتو و پیمایش با نوشتن را ببینید.

حالت حفظ شده

برای ارائه مقاصد محتوای پاسخگو، برنامه شما باید حالت خود را هنگامی که دستگاه چرخانده یا تا می شود یا اندازه پنجره برنامه تغییر می کند، حفظ کند. به‌طور پیش‌فرض، تغییرات پیکربندی مانند این‌ها، فعالیت‌ها، قطعات، سلسله‌مراتب مشاهده و ترکیب‌پذیری‌های برنامه را دوباره ایجاد می‌کند. روش توصیه شده برای ذخیره حالت رابط کاربری با ViewModel یا rememberSaveable است که در تمام تغییرات پیکربندی زنده می مانند. ( به ذخیره حالت‌های رابط کاربری و State و Jetpack Compose مراجعه کنید.)

تغییرات اندازه باید برگشت پذیر باشد - به عنوان مثال، زمانی که کاربر دستگاه را می چرخاند و سپس آن را به عقب می چرخاند.

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

دامنه های ViewModel

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

یک ViewModel همیشه در محدوده یک چرخه حیات قرار می گیرد، و هنگامی که این چرخه حیات به طور دائم تمام می شود، ViewModel پاک می شود و می توان آن را دور انداخت. چرخه حیاتی که ViewModel به آن اختصاص دارد - و بنابراین به طور گسترده ViewModel می توان به اشتراک گذاشت - به این بستگی دارد که کدام ویژگی برای بدست آوردن ViewModel استفاده می شود.

در ساده ترین حالت، هر مقصد ناوبری یک قطعه واحد با حالت رابط کاربری کاملاً ایزوله است. و بنابراین، هر قطعه می‌تواند از خصوصیت viewModels() برای بدست آوردن ViewModel با محدوده آن قطعه استفاده کند.

برای به اشتراک گذاشتن حالت رابط کاربری بین فرگمنت‌ها، ViewModel با فراخوانی activityViewModels() در فرگمنت‌ها در محدوده فعالیت قرار دهید (معادل فعالیت فقط viewModels() است). این به فعالیت و هر قطعه ای که به آن متصل می شود اجازه می دهد تا نمونه ViewModel را به اشتراک بگذارد. با این حال، در معماری تک فعالیتی، این محدوده ViewModel تا زمانی که برنامه دوام می آورد، به طور موثر ادامه می یابد، بنابراین ViewModel در حافظه باقی می ماند حتی اگر هیچ قطعه ای از آن استفاده نکند.

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

در عوض، ViewModel به یک نمودار ناوبری در NavController فعلی محدود کنید. یک نمودار ناوبری تودرتو ایجاد کنید تا مقاصدی را که بخشی از جریان پرداخت هستند، محصور کنید. سپس در هر یک از آن مقاصد قطعه، از نماینده ویژگی navGraphViewModels() استفاده کنید و شناسه گراف پیمایش را برای بدست آوردن ViewModel مشترک ارسال کنید. این تضمین می‌کند که وقتی کاربر از جریان پرداخت خارج می‌شود و نمودار پیمایش تودرتو خارج از محدوده است، نمونه مربوطه ViewModel کنار گذاشته می‌شود و برای پرداخت بعدی استفاده نخواهد شد.

دامنه نماینده اموال می تواند ViewModel را با
قطعه Fragment.viewModels() فقط قطعه فعلی
فعالیت Activity.viewModels()

Fragment.activityViewModels()

فعالیت و تمام قطعات متصل به آن
نمودار ناوبری Fragment.navGraphViewModels() همه قطعات در یک نمودار ناوبری یکسان

توجه داشته باشید که اگر از یک میزبان ناوبری تودرتو استفاده می‌کنید (به بالا مراجعه کنید)، مقصدهای موجود در آن میزبان نمی‌توانند هنگام استفاده از navGraphViewModels() ViewModel را با مقصدهای خارج از میزبان به اشتراک بگذارند، زیرا نمودارها متصل نیستند. در این مورد، می توانید به جای آن از محدوده فعالیت استفاده کنید.

حالت مرتفع

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

در بخش نوشتن مقاصد محتوا با طرح‌بندی‌های جایگزین بالا، وضعیت اجزای فهرست-جزئیات را به ListDetailRoute ارتقا دادیم تا بدون توجه به اینکه کدام composable نمایش داده می‌شود، این حالت حفظ می‌شود:

@Composable
fun ListDetailRoute(
    // Indicates that the display size is represented by the expanded window size class.
    isExpandedWindowSize: Boolean = false,
    // Identifies the item selected from the list. If null, a item has not been selected.
    selectedItemId: String?,
) { /*...*/ }

منابع اضافی

{% کلمه به کلمه %} {% آخر کلمه %} {% کلمه به کلمه %} {% آخر کلمه %}