ממשק משתמש רספונסיבי וניווט
כדי לספק למשתמשים את חוויית הניווט הטובה ביותר, מומלץ לספק ממשק משתמש לניווט שמותאם לרוחב, לגובה ול הרוחב הקטן ביותר של המכשיר של המשתמש. כדאי להשתמש סרגל האפליקציות התחתון, מוצג תמיד או מתקפל חלונית הזזה לניווט, rail, או משהו חדש לגמרי, בהתאם לשטח המסך הזמין את הסגנון הייחודי של האפליקציה.
עיצוב החומר מדריך לארכיטקטורת מוצרים מספקת הקשר ושיקולים נוספים לפיתוח ממשק משתמש רספונסיבי – ממשק משתמש שמתאים באופן דינמי לשינויים סביבתיים. כמה דוגמאות ל שינויים סביבתיים כוללים התאמות של רוחב, גובה, כיוון של המשתמש. הנכסים הסביבתיים האלה מקובצים יחד נקראת הגדרות אישיות המכשיר.
כשאחד או יותר מהמאפיינים האלה משתנים בזמן הריצה, מערכת Android OS מגיבה לפי להשמיד את הפעילויות והמקטעים של האפליקציה וליצור אותם מחדש. לכן, הדבר הטוב ביותר שאפשר לעשות כדי לתמוך בממשק משתמש רספונסיבי ב-Android הוא חשוב לוודא שמשתמשים מגדירים הגדרות של משאבים במקרים הרלוונטיים, להימנע משימוש בגדלי פריסה המוגדרים בתוך הקוד.
הטמעת ניווט גלובלי בממשק משתמש רספונסיבי
ההטמעה של ניווט גלובלי כחלק מממשק משתמש רספונסיבי מתחילה ב-
פעילות שמארחת את תרשים הניווט שלך. כדי לקבל דוגמה מעשית, אפשר:
את
Codelab ניווט.
ב-Codelab נעשה שימוש ב-NavigationView
כדי להציג את תפריט הניווט, כפי שמוצג באיור 2. בזמן ההפעלה במכשיר
בתצוגה ברוחב של 960dp לפחות, ה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
(או ערך זמין)
גובה >= 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" />
...
עם ערכי משאבים, ליעדים שלכם יש באופן אוטומטי את הערך המתאים ביותר משאבים שמיושמים בכל פעם שההגדרה משתנה.