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

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

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

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

תוצאות

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

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

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

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'

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

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

משתמשים בנקודות עצירה (breakpoints) 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() כי אפשר להפעיל את האפליקציה במצב ריבוי חלונות, שמתעלם מההגדרה של כיוון המסך. אין טעם לקבוע את גודל חלון האפליקציה ולבטל את הגדרת הכיוון, אלא אם חלון האפליקציה הוא המסך כולו של המכשיר.

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

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: קבוצה של ספריות שמאפשרות לקבוע את הגודל ויחס הגובה-רוחב של חלון האפליקציה. יש תאימות לאחור עד לרמת API‏ 14
  • Activity#setRequestedOrientation(): שיטה שבעזרתה אפשר לשנות את כיוון האפליקציה בזמן הריצה

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

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

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

יש לך שאלות או משוב?

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