จำกัดการวางแนวของแอปในโทรศัพท์ แต่ไม่ได้จำกัดในอุปกรณ์ที่มีหน้าจอขนาดใหญ่

แอปของคุณทำงานได้ดีในโทรศัพท์ในแนวตั้ง คุณจึงจำกัดแอปให้แสดงในแนวตั้งเท่านั้น แต่คุณเห็นโอกาสที่จะทําสิ่งต่างๆ ได้มากขึ้นบนหน้าจอขนาดใหญ่ในแนวนอน

คุณจะทำอย่างไรได้บ้างหากต้องการจำกัดแอปให้แสดงในแนวตั้งบนหน้าจอขนาดเล็ก แต่เปิดใช้แนวนอนบนหน้าจอขนาดใหญ่

คู่มือนี้เป็นมาตรการชั่วคราวจนกว่าคุณจะปรับปรุงแอปให้รองรับการกำหนดค่าอุปกรณ์ทั้งหมดได้

จัดการการวางแนวของแอป

หากต้องการเปิดใช้การวางแนวแนวนอนบนหน้าจอขนาดใหญ่ ให้ตั้งค่าไฟล์ Manifest ของแอปให้จัดการการเปลี่ยนแปลงการวางแนวโดยค่าเริ่มต้น กำหนดขนาดหน้าต่างแอปขณะรันไทม์ หากหน้าต่างแอปมีขนาดเล็ก ให้จำกัดการวางแนวของแอปโดยการลบล้างการตั้งค่าการวางแนวไฟล์ Manifest

1. ระบุการตั้งค่าการวางแนวในไฟล์ Manifest ของแอป

คุณสามารถหลีกเลี่ยงการประกาศองค์ประกอบ screenOrientation ของไฟล์ Manifest ของแอป (ซึ่งในกรณีนี้การวางแนวจะเป็น unspecified โดยค่าเริ่มต้น) หรือตั้งค่าการวางแนวหน้าจอเป็น fullUser หากผู้ใช้ไม่ได้ล็อกการหมุนตามเซ็นเซอร์ไว้ แอปจะรองรับการวางแนวอุปกรณ์ทุกรูปแบบ

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

ความแตกต่างระหว่าง unspecified กับ fullUser นั้นเล็กน้อยแต่สำคัญ หากคุณไม่ประกาศค่า screenOrientation ระบบจะเลือกการวางแนว และนโยบายที่ระบบใช้เพื่อกำหนดการวางแนวอาจแตกต่างกันไปในแต่ละอุปกรณ์ ในทางกลับกัน การระบุ fullUser จะตรงกับลักษณะการทำงานที่ผู้ใช้กำหนดไว้สำหรับอุปกรณ์มากกว่า หากผู้ใช้ล็อกการหมุนตามเซ็นเซอร์ไว้ แอปจะเป็นไปตามค่ากำหนดของผู้ใช้ มิเช่นนั้น ระบบจะอนุญาตให้ใช้การวางแนวหน้าจอได้ 4 แบบ (แนวตั้ง แนวนอน แนวตั้งกลับด้าน หรือแนวนอนกลับด้าน) โปรดดู screenOrientation

2. กำหนดขนาดหน้าจอ

เมื่อตั้งค่าไฟล์ Manifest ให้รองรับการวางแนวที่ผู้ใช้อนุญาตทั้งหมด คุณจะระบุการวางแนวของแอปแบบเป็นโปรแกรมตามขนาดหน้าจอได้

เพิ่มไลบรารี 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 เมตริกกรอบเวลาสามารถเปรียบเทียบกับคลาสขนาดหน้าต่างเพื่อตัดสินใจว่าจะจำกัดการวางแนวเมื่อใด

Window Size Classes จะระบุจุดแบ่งระหว่างหน้าจอขนาดเล็กและขนาดใหญ่

ใช้จุดตัด 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. ลบล้างการตั้งค่าไฟล์ Manifest ของแอป

เมื่อคุณพิจารณาแล้วว่าอุปกรณ์มีขนาดหน้าจอกะทัดรัด คุณสามารถเรียกใช้ Activity#setRequestedOrientation() เพื่อลบล้างการตั้งค่า screenOrientation ของไฟล์ Manifest ดังนี้

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: การตั้งค่าไฟล์ Manifest ของแอปที่ช่วยให้คุณระบุวิธีตอบสนองของแอปต่อการเปลี่ยนแปลงการวางแนวของอุปกรณ์ได้
  • Jetpack WindowManager: ชุดไลบรารีที่ช่วยให้คุณกำหนดขนาดและสัดส่วนภาพของหน้าต่างแอปได้ ใช้งานร่วมกับ API ระดับ 14 ย้อนหลังได้
  • Activity#setRequestedOrientation(): วิธีการเปลี่ยนการวางแนวของแอปขณะรันไทม์

ผลลัพธ์

ตอนนี้แอปของคุณควรอยู่ในแนวตั้งบนหน้าจอขนาดเล็ก ไม่ว่าจะหมุนอุปกรณ์อย่างไรก็ตาม บนหน้าจอขนาดใหญ่ แอปควรรองรับการวางแนวแนวนอนและแนวตั้ง

คอลเล็กชันที่มีคู่มือนี้

คู่มือนี้เป็นส่วนหนึ่งของคอลเล็กชันคู่มือฉบับย่อที่มีการดูแลจัดการซึ่งครอบคลุมเป้าหมายการพัฒนา Android ที่กว้างขึ้น ดังนี้

เปิดใช้แอปเพื่อรองรับประสบการณ์การใช้งานที่เพิ่มประสิทธิภาพในแท็บเล็ต อุปกรณ์แบบพับได้ และอุปกรณ์ ChromeOS

หากมีคำถามหรือความคิดเห็น

ไปที่หน้าคำถามที่พบบ่อยเพื่อดูคู่มือฉบับย่อหรือติดต่อเราเพื่อบอกความคิดเห็นของคุณ