ประสานงาน UI ของคุณกับแถบสถานะของระบบ

จนถึง Wear OS 7 (API 37) แพลตฟอร์มไม่ได้แสดงแถบสถานะของระบบเหนือหน้าต่างแอปพลิเคชัน โดยแอปเป็นเจ้าของทั้งหน้าจอและสามารถวาดเนื้อหาใดก็ได้ที่ตำแหน่งด้านบน (12 นาฬิกา) (เช่น นาฬิกา TimeText ในแอป ส่วนหัวโค้งที่กำหนดเอง หรือตัวควบคุมด้านบน)

ตั้งแต่ Wear OS 8 เป็นต้นไป และพร้อมให้ทดสอบเวอร์ชันตัวอย่างทดลองใช้แล้วในโปรแกรมจำลอง Wear OS 7.1 เวอร์ชันตัวอย่างเบต้า (API 37.2-beta1) ระบบจะแสดงแถบสถานะเหนือหน้าต่างแอปพลิเคชันที่ตำแหน่ง 12 นาฬิกาเพื่อแสดงเวลาและสัญญาณบอกสถานะ เพื่อป้องกันไม่ให้ทับซ้อนกับแถบสถานะของระบบ แอปควรหยุดแสดงนาฬิกาของตนเอง (TimeText) และเคารพส่วนแทรกของหน้าต่างแถบสถานะด้านบน (จัดการโดยอัตโนมัติโดย AppScaffold และ ScreenScaffold จาก Wear Compose 1.8 เป็นต้นไป) หรือซ่อนแถบสถานะของระบบเมื่อต้องการประสบการณ์การใช้งานแบบเต็มหน้าจอที่สมจริง

แถบสถานะของระบบใน Wear OS 8
รูปที่ 1 แถบสถานะของระบบใน Wear OS 8

ประโยชน์หลักๆ มีดังนี้

  • ความสอดคล้องทางภาพ: ตัวอักษร การจัดวาง และเพลตด้านหลังของนาฬิกาที่สอดคล้องกันทั่วทั้งระบบและแอปทั้งหมด
  • สัญญาณบอกสถานะของระบบ: อนุญาตให้แพลตฟอร์มแสดงไอคอนสถานะ (เช่น สัญญาณบอกสถานะไมโครโฟนและตำแหน่งที่ใช้งานอยู่ สถานะการเชื่อมต่อ หรือข้อมูลแบตเตอรี่) และเวลาในแพลตฟอร์มต่างๆ ระบบจะแสดงไอคอนเพียงครั้งละ 1 รายการตามลำดับความสำคัญ
  • การเลื่อนและการประสานงานการเปลี่ยนอัตโนมัติ: เมื่อจับคู่กับ Jetpack Compose สำหรับ Wear OS 1.8 แถบสถานะของระบบจะซ่อนโดยอัตโนมัติเมื่อผู้ใช้เลื่อนดูเนื้อหา และจะปรากฏขึ้นอีกครั้งหลังจากไม่มีการใช้งานเป็นเวลาสั้นๆ (2 วินาที)

ตั้งค่าแอป

หากต้องการเริ่มต้นใช้งาน ให้ทำตามขั้นตอนที่จำเป็นต่อไปนี้

  1. ติดตั้ง Android Studio เวอร์ชันตัวอย่าง (Canary)
  2. ในเครื่องมือจัดการ SDK ให้ดาวน์โหลดอิมเมจระบบของโปรแกรมจำลอง Wear OS 7.1 เวอร์ชันตัวอย่าง (API 37.2-beta1) เวอร์ชันนี้ให้สิทธิ์ทดลองใช้ก่อนเปิดตัวแถบสถานะระบบ Wear OS 8 ก่อนเปิดตัว SDK ของ Wear OS 8
  3. ในโปรแกรมจัดการอุปกรณ์ ให้สร้างและเปิดใช้งานอุปกรณ์เสมือนจริง Wear OS โดยใช้อิมเมจ API 37.2-beta1

หากต้องการรองรับแถบสถานะของระบบในแอป ให้อัปเดตเวอร์ชัน targetSdk ของแอปเป็น 35 ขึ้นไป แพลตฟอร์มจะเปิดใช้งานแถบสถานะของระบบสำหรับแอปที่กำหนดเป้าหมายเป็น API 35 ขึ้นไปเท่านั้น เพื่อรักษาความเข้ากันได้แบบย้อนหลังสำหรับแอปเดิม นอกจากนี้ ให้อัปเดตทรัพยากร Dependency ของ Wear Compose เป็นเวอร์ชันอัลฟ่าล่าสุด ซึ่งปัจจุบันคือ 1.8.0-alpha02 ดังที่แสดงในข้อมูลโค้ดต่อไปนี้ หากแอปใช้ Wear Compose เวอร์ชันเก่า คุณสามารถใช้ทักษะการย้ายข้อมูล Wear Compose Material 3 เพื่ออำนวยความสะดวกในการย้ายข้อมูลได้

android {
    defaultConfig {
        targetSdk = 35
    }
}

dependencies {
    implementation("androidx.wear.compose:compose-foundation:1.8.0-alpha02")
    implementation("androidx.wear.compose:compose-material3:1.8.0-alpha02")
}

ใช้แถบสถานะของระบบ

ใน Wear Compose Material 3 1.8.0 เวอร์ชันอัลฟ่าล่าสุด (ปัจจุบันคือ 1.8.0-alpha02) ไลบรารีจะเลือกใช้แอปของคุณโดยอัตโนมัติด้วยการผสานพร็อพเพอร์ตี้ com.google.wear.ENABLE_GLOBAL_STATUS_BAR <meta-data> เข้ากับ AndroidManifest.xml ในระหว่างรันไทม์ AppScaffold และ ScreenScaffold จะตรวจสอบว่ามีการเปิดใช้แถบสถานะของระบบในอุปกรณ์หรือไม่ และมีการตั้งค่าพร็อพเพอร์ตี้การเลือกใช้ไฟล์ Manifest นี้เป็น true หรือไม่

  • ในโปรแกรมจำลองเวอร์ชันตัวอย่าง / Wear OS 8 ขึ้นไป (เมื่อเลือกใช้): AppScaffold จะระงับ TimeText ภายในเพื่อให้แถบสถานะของระบบแสดงผลอย่างชัดเจนที่ตำแหน่ง 12 นาฬิกาโดยไม่มีการซ้ำซ้อน ScreenScaffold จะคำนวณ contentPadding ใหม่เพื่อพิจารณาขอบเขตส่วนแทรกของแถบสถานะ และประสานงานการซ่อนและแสดงแถบสถานะเมื่อเลื่อนรายการ
  • ในอุปกรณ์ Wear OS 7 และเก่ากว่า (หรือเมื่อไม่ได้เลือกใช้): AppScaffold จะเปลี่ยนกลับไปแสดงผล TimeText ในแอปมาตรฐานโดยอัตโนมัติ ซึ่งจะรักษาความเข้ากันได้แบบย้อนหลังอย่างสมบูรณ์จากฐานของโค้ดเดียว
@Composable
fun GsbSampleApp() {
    // AppScaffold coordinates the system status bar across the app
    // and falls back to rendering TimeText on Wear OS 7 and earlier.
    AppScaffold {
        val columnState = rememberTransformingLazyColumnState()

        // ScreenScaffold includes status bar top inset clearance in
        // contentPadding and hides/shows the status bar during list scrolling.
        ScreenScaffold(scrollState = columnState) { contentPadding ->
            TransformingLazyColumn(
                state = columnState,
                contentPadding = contentPadding,
            ) {
                item {
                    ListHeader { Text("Hello Status Bar") }
                }
                items(count = 15) { index ->
                    Button(onClick = { /* Handle click */ }) {
                        Text("Item #${index + 1}")
                    }
                }
            }
        }
    }
}

สิ่งที่จะเกิดขึ้นเมื่อเรียกใช้ตัวอย่างมีดังนี้

  • เมื่อไม่ได้ใช้งาน (ด้านบนของรายการ): แถบสถานะของระบบจะแสดงที่ตำแหน่ง 12 นาฬิกา ส่วนหัว "Hello Status Bar" อยู่ใต้พื้นที่ส่วนแทรกของแถบสถานะโดยไม่มีการครอบตัดหรือทับซ้อนกัน
  • การเลื่อนลง: เมื่อคุณเลื่อนดูรายการต่างๆ แถบสถานะจะซ่อน และรายการจะเลื่อนแบบเต็มหน้าจอหลังขอบด้านบน
  • ไม่มีการใช้งาน: เมื่อคุณหยุดเลื่อนและปล่อยหน้าจอ แถบสถานะจะปรากฏขึ้นอีกครั้งโดยอัตโนมัติหลังจากผ่านไปประมาณ 2 วินาที

จัดการสถานการณ์และการปรับแต่งที่พบบ่อย

ส่วนต่อไปนี้อธิบายวิธีจัดการสถานการณ์ที่พบบ่อยซึ่งคุณอาจพบเมื่อทำงานกับแถบสถานะของระบบ

จัดการหน้าจอที่ไม่เลื่อน (คงที่)

ในหน้าจอที่ใช้เลย์เอาต์แบบคงที่ (เช่น Column หรือ Box) แทนรายการแบบเลื่อน ให้ใช้ contentPadding ที่ ScreenScaffold มีให้ในเลย์เอาต์รูทเพื่อให้องค์ประกอบ UI ด้านบนไม่ทับซ้อนกับแถบสถานะของระบบ

AppScaffold {
    ScreenScaffold { contentPadding ->
        Column(modifier = Modifier.padding(contentPadding)) {
            Text("Static Screen Content")
        }
    }
}

ทำงานกับคอมโพเนนต์แบบอินเทอร์แอกทีฟเต็มหน้าจอ

คอมโพเนนต์แบบอินเทอร์แอกทีฟเต็มหน้าจอใน Wear Compose Material 3 ซึ่งรวมถึง AlertDialog, Dialog, Stepper, TimePicker และ DatePicker จะซ่อนแถบสถานะของระบบโดยอัตโนมัติขณะอยู่บนหน้าจอ เพื่อให้ตัวควบคุมมีสิทธิ์เข้าถึงด้านบนของจอแสดงผลแบบวงกลมได้อย่างเต็มที่ เมื่อปิดแล้ว แถบสถานะของระบบจะได้รับการกู้คืนโดยอัตโนมัติโดยใช้ API ตัวควบคุมส่วนแทรกของหน้าต่าง

เปลี่ยนเป็นโหมดใหญ่พิเศษ

หากต้องการเปลี่ยนไปใช้โหมดใหญ่พิเศษซึ่งจะซ่อนทั้งแถบสถานะของระบบและ TimeText ในเครื่อง ให้เพิ่ม Composable StatusBarSuppression ลงในแอปหรือหน้าจอ สงวนโหมดใหญ่พิเศษไว้สำหรับหน้าจอที่ผู้ใช้เห็นเป็นเวลาสั้นๆ (การวางซ้อนการยืนยัน กล่องโต้ตอบ) หรือหน้าจอที่ใช้ทั้งจอแสดงผล (สัญญาณบอกสถานะความคืบหน้าแบบเต็มหน้าจอ)

พิจารณาว่าควรใช้โหมดใหญ่พิเศษระดับหน้าจอเมื่อใด

หากเฉพาะหน้าจอหนึ่งๆ ในแอปควรเป็นแบบเต็มหน้าจอ/สมจริง ให้เรียกใช้ StatusBarSuppression() ภายในเนื้อหาของ ScreenScaffold ScreenScaffold จะซ่อนแถบสถานะของระบบโดยอัตโนมัติ (และละเว้นระยะห่างจากขอบด้านในของแถบสถานะด้านบน) ขณะที่หน้าจอนั้นทำงานอยู่ จากนั้นจะคืนค่าแถบสถานะของระบบเมื่อกลับไปยังหน้าจออื่นๆ ดังนี้

AppScaffold {
    ScreenScaffold() {
        StatusBarSuppression()
        // Full-screen immersive content
    }
}

พิจารณาว่าควรใช้โหมดใหญ่พิเศษระดับแอปเมื่อใด

หากแอปพลิเคชันทั้งหมด (หรือ Activity) เป็นประสบการณ์การใช้งานแบบเต็มหน้าจอที่สมจริงในทุกหน้าจอ ให้ใช้ StatusBarSuppression ที่ระดับ AppScaffold และรวมเนื้อหาเพื่อระงับแถบสถานะของระบบในทุกหน้าจอ

// Use StatusBarSuppression at AppScaffold level and wrap your content
// to suppress status bar in the entire scaffold.
AppScaffold {
    StatusBarSuppression {
        SwipeDismissableNavHost(...) {
            // Nested screens inherit suppression and 0.dp padding
        }
    }
}

แสดงการลบล้างข้อความเวลาที่กำหนดเอง

หากต้องการแสดงส่วนหัวแบบโค้งที่กำหนดเองหรือเลือกไม่ใช้แถบสถานะของระบบชั่วคราวในหน้าจอที่เฉพาะเจาะจงหรือทั่วทั้งแอปในระหว่างการย้ายข้อมูลสำหรับ Wear OS 8 การส่ง timeText Composable ที่กำหนดเองจะระงับแถบสถานะของระบบ ดังนี้

// Screen-level custom TimeText override:
ScreenScaffold(
    timeText = { TimeText { timeTextCurvedText("Custom Status") } },
) {
    // Content
}

// App-level opt-out using in-app TimeText:
AppScaffold(
    timeText = { TimeText() },
) {
    // Screens
}

ติดตั้งใช้งานโดยใช้ Views (ไม่แนะนำ)

หากแอปใช้ Android Views แทน Jetpack Compose สำหรับ Wear OS ให้เลือกใช้ AndroidManifest.xml แอปที่อิงตามมุมมองต้องเลือกใช้โดยเพิ่มแท็ก ENABLE_GLOBAL_STATUS_BAR <meta-data> ภายใน <application> อย่างชัดเจน ซึ่งต่างจากเวอร์ชันอัลฟาล่าสุดของ Wear Compose Material 3 ที่เปิดใช้แถบสถานะของระบบโดยอัตโนมัติ

<application ...>
    <meta-data
        android:name="com.google.wear.ENABLE_GLOBAL_STATUS_BAR"
        android:value="true" />
</application>

ตรวจสอบส่วนที่เว้นไว้ในหน้าต่างด้านบน

ใช้ ViewCompat.setOnApplyWindowInsetsListener และตรวจสอบค่าของ windowInsets.getInsets(WindowInsetsCompat.Type.statusBars()) เมื่อ statusBarInsets.top > 0 ให้นำ UI แบบเดิมที่ตำแหน่ง 12 นาฬิกาออกและปรับระยะห่างจากขอบด้านบน มิฉะนั้น ให้กลับไปใช้ UI แบบเดิม

ViewCompat.setOnApplyWindowInsetsListener(rootView) { view, windowInsets ->
    val statusBarInsets = windowInsets.getInsets(
        WindowInsetsCompat.Type.statusBars()
    )

    if (statusBarInsets.top > 0) {
        // status bar is visible: remove legacy 12 o'clock UI and apply top inset padding
        view.updatePadding(top = statusBarInsets.top)
    } else {
        // status bar is not visible: fall back to legacy UI and padding
    }

    WindowInsetsCompat.CONSUMED
}

ซ่อนและแสดงแถบสถานะแบบเป็นโปรแกรม

แอปที่อิงตามมุมมองต้องซ่อนหรือแสดงแถบสถานะของระบบอย่างชัดเจน (เช่น เมื่อเข้าสู่มุมมองที่สมจริงแบบเต็มหน้าจอหรือประสานงานลักษณะการเลื่อนออก) โดยใช้ WindowInsetsControllerCompat ซึ่งต่างจาก ScreenScaffold ใน Compose

val insetsController = WindowCompat.getInsetsController(window, window.decorView)

// Hide the status bar
insetsController.hide(WindowInsetsCompat.Type.statusBars())

// Show the status bar
insetsController.show(WindowInsetsCompat.Type.statusBars())

ทดสอบแอป

เมื่อทดสอบแอปในโปรแกรมจำลอง Wear OS 7.1 เวอร์ชันตัวอย่างเบต้า (API 37.2-beta1) ให้ตรวจสอบสิ่งต่อไปนี้

  • นาฬิกาปรากฏเพียงครั้งเดียว: ตรวจสอบว่ามีเพียงเวลาในแถบสถานะของระบบที่ปรากฏที่ตำแหน่ง 12 นาฬิกา และไม่มี TimeText เดิมในแอปและเนื้อหาแอปซ้อนทับอยู่
  • ระยะห่างจากขอบด้านบน: ตรวจสอบว่าชื่อหน้าจอ ส่วนหัว และปุ่มด้านบนปรากฏใต้แถบสถานะ
  • การวางซ้อนและกล่องโต้ตอบแบบเต็มหน้าจอ: ตรวจสอบว่ากล่องโต้ตอบ เครื่องมือเลือก ตัวเลือก และกิจกรรมแบบเต็มหน้าจอที่กำหนดเองซ่อนแถบสถานะเมื่อเปิด และคืนค่าแถบสถานะเมื่อปิด