مدیریت تغییرات پیکربندی

واسط کاربر و پیمایش واکنش‌گرا

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

نمونه‌هایی از ریل، کشوهای پیمایش، و نوار برنامه پایین
شکل ۱. نمونه‌هایی از ریل، کشوهای پیمایش، و نوار برنامه پایین.

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

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

پیاده‌سازی پیمایش سراسری در واسط کاربر واکنش‌گرا

پیاده‌سازی پیمایش سراسری به‌عنوان بخشی از میانای کاربر واکنش‌گرا با فعالیتی شروع می‌شود که میزبان نمودار پیمایش شما است. برای مشاهده نمونه عملی، آزمایشگاه کد پیمایش را بررسی کنید. این codelab از NavigationView برای نمایش منو پیمایش استفاده می‌کند، همان‌طور که در شکل ۲ نشان داده شده است. وقتی در دستگاهی اجرا می‌شود که با عرض حداقل ۹۶۰dp رندر می‌کند، این NavigationView همیشه روی صفحه است.

codelab ناوبری از نمای ناوبری استفاده می‌کند که همیشه نمایان است
            وقتی عرض دستگاه حداقل ۹۶۰dp باشد
شکل ۲. آموزش کدنویسی پیمایش از NavigationView برای نمایش منو پیمایش استفاده می‌کند.

اندازه‌ها و جهت‌های دیگر دستگاه به‌طور پویا بین DrawerLayout یا BottomNavigationView درصورت نیاز جابه‌جا می‌شوند.

یک bottomnavigationview و یک drawerlayout که برای منو پیمایش درصورت نیاز در چیدمان‌های دستگاه‌های کوچک‌تر استفاده می‌شود
شکل ۳.‏ آموزش «ناوبری» از BottomNavigationView و DrawerLayout برای نمایش منو ناوبری در دستگاه‌های کوچک‌تر استفاده می‌کند.

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

پیکربندی‌ای که هر چیدمان بر آن اعمال می‌شود توسط ساختار فهرست راهنمایی که فایل چیدمان در آن قرار دارد تعیین می‌شود. برای مثال، فایل چیدمان NavigationView در دایرکتوری res/layout-w960dp قرار دارد.

<!-- res/layout-w960dp/navigation_activity.xml -->
<RelativeLayout
   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="com.example.android.codelabs.navigation.MainActivity">

   <com.google.android.material.navigation.NavigationView
       android:id="@+id/nav_view"
       android:layout_width="wrap_content"
       android:layout_height="match_parent"
       android:layout_alignParentStart="true"
       app:elevation="0dp"
       app:headerLayout="@layout/nav_view_header"
       app:menu="@menu/nav_drawer_menu" />

   <View
       android:layout_width="1dp"
       android:layout_height="match_parent"
       android:layout_toEndOf="@id/nav_view"
       android:background="?android:attr/listDivider" />

   <androidx.appcompat.widget.Toolbar
       android:id="@+id/toolbar"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:layout_alignParentTop="true"
       android:layout_toEndOf="@id/nav_view"
       android:background="@color/colorPrimary"
       android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar" />

   <androidx.fragment.app.FragmentContainerView
       android:id="@+id/my_nav_host_fragment"
       android:name="androidx.navigation.fragment.NavHostFragment"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:layout_below="@id/toolbar"
       android:layout_toEndOf="@id/nav_view"
       app:defaultNavHost="true"
       app:navGraph="@navigation/mobile_navigation" />
</RelativeLayout>

نمای پیمایش پایین در فهرست راهنمای res/layout-h470dp قرار دارد:

<!-- res/layout-h470dp/navigation_activity.xml -->
<LinearLayout
   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"
   android:orientation="vertical"
   tools:context="com.example.android.codelabs.navigation.MainActivity">

   <androidx.appcompat.widget.Toolbar
       android:id="@+id/toolbar"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:background="@color/colorPrimary"
       android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar" />

   <androidx.fragment.app.FragmentContainerView
       android:id="@+id/my_nav_host_fragment"
       android:name="androidx.navigation.fragment.NavHostFragment"
       android:layout_width="match_parent"
       android:layout_height="0dp"
       android:layout_weight="1"
       app:defaultNavHost="true"
       app:navGraph="@navigation/mobile_navigation" />

   <com.google.android.material.bottomnavigation.BottomNavigationView
       android:id="@+id/bottom_nav_view"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       app:menu="@menu/bottom_nav_menu" />
</LinearLayout>

طرح‌بندی کشو در فهرست راهنمای res/layout قرار دارد. از این دایرکتوری برای طرح‌بندی‌های پیش‌فرض بدون واجدشرایط‌های خاص پیکربندی استفاده کنید:

<!-- res/layout/navigation_activity.xml -->
<androidx.drawerlayout.widget.DrawerLayout
   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/drawer_layout"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context="com.example.android.codelabs.navigation.MainActivity">

   <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:orientation="vertical">

       <androidx.appcompat.widget.Toolbar
           android:id="@+id/toolbar"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:background="@color/colorPrimary"
           android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar" />

       <androidx.fragment.app.FragmentContainerView
           android:id="@+id/my_nav_host_fragment"
           android:name="androidx.navigation.fragment.NavHostFragment"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           app:defaultNavHost="true"
           app:navGraph="@navigation/mobile_navigation" />
   </LinearLayout>

   <com.google.android.material.navigation.NavigationView
       android:id="@+id/nav_view"
       android:layout_width="wrap_content"
       android:layout_height="match_parent"
       android:layout_gravity="start"
       app:menu="@menu/nav_drawer_menu" />
</androidx.drawerlayout.widget.DrawerLayout>

Android هنگام تعیین اینکه کدام منابع را اعمال کند، از ترتیب تقدم پیروی می‌کند. به‌طور خاص در این مثال، -w960dp (یا عرض دردسترس >= ۹۶۰dp) بر -h470dp (یا ارتفاع دردسترس >= ۴۷۰) اولویت دارد. اگر پیکربندی دستگاه با هیچ‌کدام از این شرایط مطابقت نداشته باشد، از منبع چیدمان پیش‌فرض (res/layout/navigation_activity.xml) استفاده می‌شود.

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

کاتلین

class MainActivity : AppCompatActivity() {

   private lateinit var appBarConfiguration : AppBarConfiguration

   override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.navigation_activity)
      val drawerLayout : DrawerLayout? = findViewById(R.id.drawer_layout)
      appBarConfiguration = AppBarConfiguration(
                  setOf(R.id.home_dest, R.id.deeplink_dest),
                  drawerLayout)

      ...

      // Initialize the app bar with the navigation drawer if present.
      // If the drawerLayout is not null here, a Navigation button will be added
      // to the app bar whenever the user is on a top-level destination.
      setupActionBarWithNavController(navController, appBarConfig)

      // Initialize the NavigationView if it is present,
      // so that clicking an item takes
      // the user to the appropriate destination.
      val sideNavView = findViewById<NavigationView>(R.id.nav_view)
      sideNavView?.setupWithNavController(navController)

      // Initialize the BottomNavigationView if it is present,
      // so that clicking an item takes
      // the user to the appropriate destination.
      val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_nav_view)
      bottomNav?.setupWithNavController(navController)

      ...
    }

    ...
}

جاوا

public class MainActivity extends AppCompatActivity {

   private AppBarConfiguration appBarConfiguration;

   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.navigation_activity);
       NavHostFragment host = (NavHostFragment) getSupportFragmentManager()
               .findFragmentById(R.id.my_nav_host_fragment);
       NavController navController = host.getNavController();

       DrawerLayout drawerLayout = findViewById(R.id.drawer_layout);
       appBarConfiguration = new AppBarConfiguration.Builder(
               R.id.home_dest, R.id.deeplink_dest)
               .setDrawerLayout(drawerLayout)
               .build();

       // Initialize the app bar with the navigation drawer if present.
       // If the drawerLayout is not null here, a Navigation button will be added to
       // the app bar whenever the user is on a top-level destination.
       NavigationUI.setupActionBarWithNavController(
               this, navController, appBarConfiguration);


       // Initialize the NavigationView if it is present,
       // so that clicking an item takes
       // the user to the appropriate destination.
       NavigationView sideNavView = findViewById(R.id.nav_view);
       if(sideNavView != null) {
           NavigationUI.setupWithNavController(sideNavView, navController);
       }

       // Initialize the BottomNavigationView if it is present,
       // so that clicking an item takes
       // the user to the appropriate destination.
       BottomNavigationView bottomNav = findViewById(R.id.bottom_nav_view);
       if(bottomNav != null) {
           NavigationUI.setupWithNavController(bottomNav, navController);
       }

   }
}

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

جایگزین‌های چیدمان‌های نمای دونیمه را درنظر بگیرید

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

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

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

نام مقصدها

اگر نام‌های مقصد را در نمودارتان بااستفاده از مشخصه android:label ارائه می‌دهید، حتماً همیشه از مقادیر منبع استفاده کنید تا محتوایتان همچنان قابل بومی‌سازی باشد.

<navigation ...>
    <fragment
        android:id="@+id/my_dest"
        android:name="com.example.MyFragment"
        android:label="@string/my_dest_label"
        tools:layout="@layout/my_fragment" />
    ...

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