להגביל את הכיוון של האפליקציה בטלפונים, אבל לא במכשירים עם מסך גדול

האפליקציה שלכם פועלת מצוין בטלפונים בכיוון לאורך, ולכן הגבלתם את האפליקציה לכיוון לאורך בלבד. אבל יש לכם הזדמנות לעשות יותר במסכים גדולים בפריסה לרוחב.

איך אפשר להגביל את האפליקציה לכיוון לאורך במסכים קטנים, אבל לאפשר תצוגה לרוחב במסכים גדולים?

המדריך הזה הוא אמצעי זמני עד שתוכלו לשפר את האפליקציה כדי לספק תמיכה מלאה בכל הגדרות המכשיר.

ניהול הכיוון של האפליקציה

כדי להפעיל את הכיוון לרוחב במסכים גדולים, צריך להגדיר במניפסט של האפליקציה טיפול בשינויי כיוון כברירת מחדל. במהלך זמן הריצה, קובעים את גודל חלון האפליקציה. אם חלון האפליקציה קטן, אפשר להגביל את כיוון האפליקציה על ידי שינוי ההגדרה של כיוון המניפסט.

1. ציון הגדרת הכיוון במניפסט של האפליקציה

אפשר להימנע מהצהרה על הרכיב screenOrientation במניפסט של האפליקציה (במקרה כזה, כיוון המסך יהיה unspecified כברירת מחדל) או להגדיר את כיוון המסך כ-fullUser. אם המשתמש לא נעל את הסיבוב מבוסס-החיישן, האפליקציה תתמוך בכל כיווני המכשיר.

<activity
    android:name=".MyActivity"
    android:screenOrientation="fullUser">

ההבדל בין unspecified לבין fullUser הוא קלוש אבל חשוב. אם לא מגדירים ערך ל-screenOrientation, המערכת בוחרת את הכיוון, והמדיניות שבה המערכת משתמשת כדי להגדיר את הכיוון עשויה להשתנות ממכשיר למכשיר. לעומת זאת, ציון הערך fullUser תואם יותר להתנהגות שהמשתמש הגדיר למכשיר: אם המשתמש נעל את הסיבוב על סמך חיישן, האפליקציה תתאים את עצמה להעדפות המשתמש. אחרת, המערכת תאפשר כל אחת מארבע כיווני המסך האפשריים (לאורך, לרוחב, הפוך לרוחב או הפוך לאורך). מידע נוסף זמין במאמר screenOrientation.

2. קביעת גודל המסך

כשהמניפסט מוגדר לתמוך בכל הכיוונים המותרים למשתמשים, אפשר לציין את כיוון האפליקציה באופן פרוגרמטי על סמך גודל המסך.

מוסיפים את הספריות של Jetpack WindowManager לקובץ build.gradle או build.gradle.kts של המודול:

Kotlin

implementation("androidx.window:window:version")
implementation("androidx.window:window-core:version")

Groovy

implementation 'androidx.window:window:version'
implementation 'androidx.window:window-core:version'

משתמשים ב-method‏ WindowMetricsCalculator#computeMaximumWindowMetrics() של Jetpack WindowManager כדי לקבל את גודל המסך של המכשיר כאובייקט WindowMetrics. אפשר להשוות בין מדדי החלון לבין כיתות של גודל חלון כדי להחליט מתי להגביל את הכיוון.

סיווגים של גודל חלונות מספקים את נקודות העצירה בין מסכים קטנים למסכים גדולים.

כדי לקבוע את גודל המסך, משתמשים בנקודות הציון WindowWidthSizeClass#COMPACT ו-WindowHeightSizeClass#COMPACT:

Kotlin

/** Determines whether the device has a compact screen. **/
fun compactScreen() : Boolean {
    val metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this)
    val width = metrics.bounds.width()
    val height = metrics.bounds.height()
    val density = resources.displayMetrics.density
    val windowSizeClass = WindowSizeClass.compute(width/density, height/density)

    return windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT ||
        windowSizeClass.windowHeightSizeClass == WindowHeightSizeClass.COMPACT
}

Java

/** Determines whether the device has a compact screen. **/
private boolean compactScreen() {
    WindowMetrics metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this);
    int width = metrics.getBounds().width();
    int height = metrics.getBounds().height();
    float density = getResources().getDisplayMetrics().density;
    WindowSizeClass windowSizeClass = WindowSizeClass.compute(width/density, height/density);
    return windowSizeClass.getWindowWidthSizeClass() == WindowWidthSizeClass.COMPACT ||
                windowSizeClass.getWindowHeightSizeClass() == WindowHeightSizeClass.COMPACT;
}
    הערה:
  • הדוגמאות מיושמות כשימוש בשיטות של פעילות, ולכן מתבצע ביטול ההפניה לפעילות כ-this בארגומנט של computeMaximumWindowMetrics().
  • השיטה computeMaximumWindowMetrics() משמשת במקום computeCurrentWindowMetrics() כי אפשר להפעיל את האפליקציה במצב ריבוי חלונות, שבו ההגדרה של כיוון המסך מתעלמת. אין טעם לקבוע את גודל חלון האפליקציה ולעקוף את הגדרת הכיוון, אלא אם חלון האפליקציה הוא כל מסך המכשיר.

במאמר WindowManager מפורטות הוראות להצהרה על יחסי תלות כדי שהשיטה computeMaximumWindowMetrics() תהיה זמינה באפליקציה.

3. שינוי ההגדרה של קובץ המניפסט של האפליקציה

אחרי שמחליטים שהמסך של המכשיר קומפקטי, אפשר להפעיל את Activity#setRequestedOrientation() כדי לשנות את ההגדרה screenOrientation במניפסט:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    requestedOrientation = if (compactScreen())
        ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else
        ActivityInfo.SCREEN_ORIENTATION_FULL_USER
    ...
    // Replace with a known container that you can safely add a
    // view to where the view won't affect the layout and the view
    // won't be replaced.
    val container: ViewGroup = binding.container

    // Add a utility view to the container to hook into
    // View.onConfigurationChanged. This is required for all
    // activities, even those that don't handle configuration
    // changes. You can't use Activity.onConfigurationChanged,
    // since there are situations where that won't be called when
    // the configuration changes. View.onConfigurationChanged is
    // called in those scenarios.
    container.addView(object : View(this) {
        override fun onConfigurationChanged(newConfig: Configuration?) {
            super.onConfigurationChanged(newConfig)
            requestedOrientation = if (compactScreen())
                ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else
                ActivityInfo.SCREEN_ORIENTATION_FULL_USER
        }
    })
}

Java

@Override
protected void onCreate(Bundle savedInstance) {
    super.onCreate(savedInstanceState);
    if (compactScreen()) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    } else {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
    }
    ...
    // Replace with a known container that you can safely add a
    // view to where the view won't affect the layout and the view
    // won't be replaced.
    ViewGroup container = binding.container;

    // Add a utility view to the container to hook into
    // View.onConfigurationChanged. This is required for all
    // activities, even those that don't handle configuration
    // changes. You can't use Activity.onConfigurationChanged,
    // since there are situations where that won't be called when
    // the configuration changes. View.onConfigurationChanged is
    // called in those scenarios.
    container.addView(new View(this) {
        @Override
        protected void onConfigurationChanged(Configuration newConfig) {
            super.onConfigurationChanged(newConfig);
            if (compactScreen()) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            } else {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
            }
        }
    });
}

הוספת הלוגיקה לשיטות onCreate() ו-View.onConfigurationChanged() מאפשרת לקבל את מדדי החלון המקסימליים ולעקוף את הגדרת הכיוון בכל פעם שמגדילים או מעבירים את הפעילות בין מסכים, למשל אחרי סיבוב המכשיר או כשמכשיר מתקפל פתוח או סגור. למידע נוסף על המקרים שבהם מתרחשים שינויים בהגדרות ועל המקרים שבהם הם גורמים ליצירה מחדש של פעילויות, קראו את המאמר טיפול בשינויים בהגדרות.

נקודות עיקריות

  • screenOrientation: הגדרה במניפסט של האפליקציה שמאפשרת לציין איך האפליקציה תגיב לשינויים בכיוון המכשיר
  • Jetpack WindowManager: קבוצה של ספריות שמאפשרות לקבוע את הגודל ואת יחס הגובה-רוחב של חלון האפליקציה. התאימות לאחור היא עד לרמה 14 של API.
  • Activity#setRequestedOrientation(): שיטה שמאפשרת לשנות את כיוון האפליקציה במהלך זמן הריצה

תוצאות

עכשיו האפליקציה אמורה להישאר בכיוון לאורך במסכים קטנים, ללא קשר לסיבוב המכשיר. במסכים גדולים, האפליקציה צריכה לתמוך בכיוון לרוחב ובכיוון לאורך.

אוספים שמכילים את המדריך הזה

המדריך הזה הוא חלק מהאוספים של המדריכים המהירים שנבחרו במיוחד, שמכסים יעדים רחבים יותר לפיתוח Android:

מאפשרים לאפליקציה לתמוך בחוויית משתמש אופטימלית בטאבלטים, במכשירים מתקפלים ובמכשירי ChromeOS.

יש לכם שאלות או משוב

אתם יכולים להיכנס לדף השאלות הנפוצות שלנו ולקרוא מדריכים מהירים, או ליצור איתנו קשר ולספר לנו מה דעתכם.