واجهة مستخدم سريعة الاستجابة والتنقل
لتوفير أفضل تجربة تنقل ممكنة للمستخدمين، يجب عليك توفير واجهة مستخدم للتنقل مصممة خصيصًا للعرض والارتفاع وأصغر عرض لجهاز المستخدم. ننصحك باستخدام شريط تطبيق سفلي، أو درج تنقّل متاح دائمًا أو قابل للتصغير، أو سكة حديدية، أو ربما استخدام عنصر جديد تمامًا بناءً على مساحة ال��اشة المتاحة والنمط الفريد لتطبيقك.
يوفر دليل التصميم المتعدد الأبعاد سياقًا واعتبارات إضافية لإنشاء واجهة مستخدم سريعة الاستجابة، وهي واجهة مستخدم تتكيّف ديناميكيًا مع التغييرات البيئية. ومن أمثلة التغييرات البيئية التعديلات على العرض والارتفاع والاتجاه ولغة المستخدم المفضّلة. ويُشار إلى هذه الخصائص البيئية إجمالاً باسم إعدادات الجهاز.
عندما تتغير خاصية واحدة أو أكثر من هذه الخصائص في وقت التشغيل، يستجيب نظام التشغيل Android بإتلاف أنشطة التطبيق وأجزائه ثم إعادة إنشائها. لذلك، فإن أفضل ما يمكنك فعله لدعم واجهة المستخدم سريعة الاستجابة على Android هو التأكّد من استخدامك مؤهِّلات إعداد الموارد متى كان ذلك مناسبًا وتجنُّب استخدام أحجام التنسيقات ذات الترميز الثابت.
تنفيذ التنقل العام في واجهة مستخدم سريعة الاستجابة
يبدأ تنفيذ التنقل العام كجزء من واجهة المستخدم سريعة الاستجابة
بالنشاط الذي يستضيف الرسم البياني للتنقل. للحصول على مثال عملي، يمكنك الاطلاع
على الدرس التطبيقي حول ترميز التنقل.
يستخدم الدرس التطبيقي حول الترميز علامة NavigationView
لعرض قائمة التنقّل، كما هو موضّح في الشكل 2. عند استخدام جهاز يعمل بعرض شاشة 960 بكسل مستقل الكثافة على الأقل، يتم عرض علامة NavigationView
هذه على الشاشة دائمًا.
يتم تبديل أحجام الأجهزة والاتجاهات الأخرى ديناميكيًا بين
DrawerLayout
أو
BottomNavigationView
حسب الحاجة.
يمكنك تنفيذ هذا السلوك من خلال إنشاء ثلاثة تخطيطات مختلفة، حيث يحدد كل تخطيط عناصر التنقل المطلوبة وعرض التسلسل الهرمي بناءً على إعدادات الجهاز الحالية.
تُحدَّد الإعدادات التي ينطبق عليها كل تنسيق حسب بنية الدليل التي يتم وضع ملف التنسيق فيها. على سبيل المثال، تم العثور على ملف
التنسيق 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
(أو العرض المتاح >= 960dp) الأولوية على -h470dp
(أو المتاح
height >= 470). إذا لم تتطابق إعدادات الجهاز مع أي من هذين الشرطين، سيتم استخدام مورد التنسيق التلقائي
(res/layout/navigation_activity.xml
).
عند معالجة أحداث التنقل، ما عليك سوى توصيل الأحداث التي تتوافق مع التطبيقات المصغّرة الحالية، كما هو موضَّح في المثال التالي.
Kotlin
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) ... } ... }
Java
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. هذا يضع تركيزًا أكبر بكثير على كل شاشة في تطبيقك سريعة الاستجابة، بدلاً من تغيير هيكل التنقل بناءً على حجم الشاشة.
في حين أنه من الممكن تنفيذ واجهة تنسيق عرض مقسم باستخدام مكتبة التنقل، ينبغي أن تفكر في بدائل أخرى.
أسماء الوجهات
في حال توفير أسماء الوجهات في الرسم البياني باستخدام السمة 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" />
...
باستخدام قيم الموارد، سيتم تلقائيًا تطبيق الموارد الأكثر ملاءمة لوجهاتك كلما تغيّرت الإعدادات.