ถ้าพูดถึงการทำให้แอปฯแสดงหน้าจอแบบ Fullscreen หรือ Immersive ก็คงจะร้องอ๋อกัน ซึ่งในบทความนี้ก็จะขอพูดถึงการควบคุมการทำงานของ System UI ภายในแอปฯกันครับ
เนื้อหาในบทความนี้จะไม่ได้พูดถึง WindowInsets API ตัวใหม่ของ Android 11
ซึ่ง System UI ที่ว่านี้ก็หมายถึง Status Bar, Navigation Bar และ Action Bar (หรือ Toolbar) ที่คุ้นเคยกันดีนั่นเอง ซึ่งระบบแอนดรอยด์ก็เปิดให้แอปฯสามารถเปลี่ยนรูปแบบการแสดงผลของ System UI ให้เหมาะกับการใช้งานแอปฯในแต่ละรูปแบบได้
![](https://akexorcist.dev/content/images/2020/12/android_app_system_ui_management-001.jpg)
ซึ่งควบคุมยังไงได้บ้างนั้นก็จะขึ้นอยู่กับเวอร์ชันของแอนดรอยด์ด้วย เพราะเวอร์ชันเก่าๆก็อาจจะทำงานบางอย่างไม่ได้ แต่จะมีอะไรบ้างนั้นมาดูกันต่อเลย
Dimming หรือ Low Profile
เป็นความสามารถที่ถูกเพิ่มเข้ามาตั้งแต่สมัย Android 4.0 Ice Cream Sandwich (API Level 14) โดยจะลดการแสดงผลของ Status Bar และ Navigation Bar ให้รบกวนผู้ใช้น้อยลง ซึ่งเหมาะกับแอปที่มีเนื้อหาที่ผู้ใช้ต้องดูเป็นระยะเวลานาน และไม่อยากให้การทำงานส่วนอื่นๆมารบกวนสายตาของผู้ใช้
![](https://akexorcist.dev/content/images/2020/12/android_app_system_ui_management-002-2.png)
เมื่อเปิดใช้งาน Dimming หรือ Low Profile ผู้ใช้จะเห็นแค่ระดับแบตเตอรีที่ Status Bar เท่านั้น ส่วนไอคอนอื่นๆรวมไปถึง Notification จากแอปต่างๆจะถูกซ่อนออกไป ในขณะเดียวกันปุ่มที่อยู่บน Navigation Bar ก็จะปรับสีเพื่อลด Contrast ลงไปด้วย
การแสดงผลแบบ Dimming หรือ Low Profile จะต้องใช้คำสั่งแบบนี้
val window: Window = /* ... */
val decorView: View = window.decorView
decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LOW_PROFILE
จะเรียกใช้งานเมื่อไรก็ได้ โดยที่ Dimming จะไม่ถูกยกเลิกจนกว่าผู้ใช้จะกดที่ Status Bar/Navigation Bar หรือใช้คำสั่งเพื่อยกเลิกแบบนี้
val window: Window = /* ... */
val decorView = window.decorView
decorView.systemUiVisibility = 0
การแสดง Status Bar
เวลาที่ต้องการซ่อน Status Bar จะต้องกำหนด Flag ให้กับ setSystemUiVisibility(...)
แบบนี้
val window: Window = /* ... */
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
และถ้าอยากให้ Status Bar กลับมาแสดงอีกครั้งก็ให้เคลียร์ค่าเป็น 0 ซะ
val window: Window = /* ... */
window.decorView.systemUiVisibility = 0
![](https://akexorcist.dev/content/images/2020/12/android_app_system_ui_management-003.gif)
และเมื่อ Status Bar มีการซ่อนหรือแสดงจะทำให้ UI ของแอปมีการปรับความสูงเพื่อให้พอดีกับขนาดหน้าจอด้วย
การแสดง Navigation Bar
สำหรับการควบคุม Navigation Bar นั้นจะทำได้ตั้งแต่ Android 4.1 Jelly Bean (API Level 16) ขึ้นไป โดยมีคำสั่งคล้ายกันกับของ Status Bar เลย แต่ว่าจะใช้ Flag ที่แตกต่างกันเล็กน้อย
// คำสั่งซ่อน Navigation Bar
val window: Window = /* ... */
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
// คำสั่งแสดง Navigation Bar
val window: Window = /* ... */
window.decorView.systemUiVisibility = 0
![](https://akexorcist.dev/content/images/2020/12/android_app_system_ui_management-004-1.gif)
แน่นอนว่าเมื่อ Navigation Bar มีการซ่อนหรือแสดงก็จะทำให้ UI ของแอปปรับความสูงให้พอดีกับหน้าจอด้วยเช่นกัน
การแสดง Navigation และ Status Bar พร้อมๆกัน
เนื่องจากคำสั่ง setSystemUiVisibility
เป็นการกำหนดค่าแบบ Flag ดังนั้นจึงกำหนดค่าหลายๆตัวพร้อมๆกันได้ จึงทำให้สามารถซ่อน/แสดง Navigation Bar และ Status Bar ไปพร้อมๆกันได้เลย
// คำสั่งซ่อน Navigation Bar และ Status Bar
val window: Window = /* ... */
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN
// คำสั่งแสดง Navigation Bar และ Status Bar
val window: Window = /* ... */
window.decorView.systemUiVisibility = 0
Immersive Fullscreen
จากที่ผ่านมาจะเห็นว่าถ้าอยากให้แอปฯสามารถแสดงผลแบบ Fullscreen ได้ ก็จะต้องเขียนโค้ดเพิ่มเข้าไปเอง ซึ่งยังไม่รวมไปถึงการออกจากโหมด Fullscreen และการทำปุ่มเพื่อกดออกจาก Fullscreen ก็อาจจะดูไม่สะดวกเสมอไป ดังนั้นบน Android 4.4 KitKat (API 19) จึงได้เพิ่มความสามารถในการแสดงผลแบบ Fullscreen ให้สะดวกและง่ายมากขึ้นโดยมีชื่อเรียกว่า Immersive Fullscreen
ประโยชน์หลักๆของ Immersive Fullscreen คือนักพัฒนาไม่ต้องจัดการอะไรเกี่ยวกับการแสดงผลมากนัก นอกจากสั่งแค่ให้มันแสดงผลเป็นแบบ Immersive Fullscreen เท่านั้น
โดยคำสั่งของ Immersive Fullscreen จะใช้คำสั่งแบบนี้
val window: Window = /* ... */
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_FULLSCREEN or
View.SYSTEM_UI_FLAG_IMMERSIVE
เมื่อคำสั่งทำงาน แอปฯก็จะเข้าสู่ Immersive Fullscreen ทันที และในกรณีที่แอปฯนั้นๆเพิ่งจะแสดงเป็น Immersive Fullscreen ครั้งแรก จะมีหน้าต่างแสดงให้ผู้ใช้รับรู้ด้วย
![](https://akexorcist.dev/content/images/2020/12/android_app_system_ui_management-005.png)
สำหรับการออกจาก Immersive Fullscreen สามารถทำได้ด้วยปัดนิ้วที่ขอบด้านบนหรือด้านล่างของหน้าจอ ก็จะออกจาก Immersive Fullscreen ทันที
โดยการทำงานของ Immersive Fullscreen นั้นจะมีสองแบบด้วยกัน คือแบบ Non-Sticky Immersion และ Sticky Immersion
Non-Sticky Immersion
เป็นการเข้าสู่ Immersive Fullscreen เพียงแค่ครั้งเดียว จนกว่าผู้ใช้จะกดออก ซึ่งใช้คำสั่งเดียวกันกับที่เจ้าของบล็อกยกตัวอย่างในตอนแรกนั่นแหละ
val window: Window = /* ... */
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_FULLSCREEN or
View.SYSTEM_UI_FLAG_IMMERSIVE
เหมาะสำหรับแอปฯที่ต้องการแสดงผลแบบ Immersive Fullscreen ในการใช้งานบางอย่างเท่านั้น
Sticky Immersion
แต่ถ้าเกิดอยากจะให้แอปแสดงผลแบบ Immersive Fullscreen ตลอดเวลาเลย ก็ให้ใช้คำสั่งแบบนี้
val window: Window = /* ... */
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_FULLSCREEN or
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
โดยคำสั่งจะคล้ายๆกับก่อนหน้านี้เลย ต่างกันแค่ตรง View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
เท่านั้นเอง
ซึ่งผลลัพธ์จะออกมาเป็นลักษณะแบบนี้
![](https://akexorcist.dev/content/images/2020/12/android_app_system_ui_management-006-1.gif)
จะเห็นว่าผู้ใช้จะอยู่ในโหมด Fullscreen เสมอ เมื่อมีการลากนิ้วที่ขอบข้างบน/ล่างของหน้าจอ ก็จะเป็นการแสดง Status Bar กับ Navigation Bar และเมื่อผ่านไปซักพักก็จะซ่อนเองโดยอัตโนมัติ
การกรณีที่ใช้ Immersive Fullscreen แบบ Sticky เจ้าของบล็อกแนะนำว่าให้ใส่โค้ดลงใน onWindowFocusChanged
ได้เลย เพื่อที่ว่าจะได้เข้า Immersive Fullscreen ทันทีที่เข้าสู่หน้า Activity นั้นๆ
class MainActivity : Activity() {
/* ... */
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (hasFocus) {
val decorView = window.decorView
decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_FULLSCREEN
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
}
}
}
การซ่อน Title/Action Bar และ Status Bar ผ่าน Theme
นอกเหนือจากการสั่งงานผ่านโค้ด Java แล้ว ยังสามารถกำหนดให้แสดงผลแบบ Fullscreen ได้ด้วยการกำหนด Theme ให้กับ Activity ที่ต้องการได้เลย
สามารถกำหนด Theme ให้มี Parent เป็น Theme ที่เป็น Fullscreen ได้เลย
![](https://akexorcist.dev/content/images/2020/12/android_app_system_ui_management-011.jpg)
จากภาพข้างบนก็จะเห็นว่าการสั่งให้ Fullscreen เพื่อซ่อน Status Bar นั้นจะมาพร้อมกับการซ่อน Title/Action Bar เสมอ ซึ่งก็ไม่เข้าใจเหมือนกันว่าทำไม
แต่ถ้าอยากจะซ่อนเฉพาะ Status Bar จริงๆก็กำหนดเพิ่มเข้าไปใน Theme ที่ใช้อยู่ได้เลย
<resources>
<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
<item name="android:windowFullscreen">true</item>
<!-- ... -->
</style>
<!-- ... -->
</resources>
เพียงเท่านี้ Theme ของผู้ที่หลงเข้ามาอ่านก็จะกลายเป็น Theme ที่ซ่อน Status Bar และผู้ใช้ก็ยังคงสามารถปัดนิ้วที่ขอบบนของจอเพื่อแสดง Status Bar ชั่วคราวได้
แต่วิธีแบบนี้จะไม่สามารถซ่อน Navigation Bar ผ่าน Theme ได้นะ
สรุป
การควบคุม System UI นั้นก็คือการซ่อน/แสดง Status Bar และ Navigation Bar นั่นเอง ซึ่งสามารถทำได้หลายแบบ โดยขึ้นอยู่กับความต้องการใช้งาน
ซึ่งจะมีรูปแบบต่างๆดังนี้
- Dimming
- Show/Hide Status Bar
- Show/Hide Navigation Bar
- Fullscreen
- Immersive Fullscreen
- Show/Hide Status Bar with Theme
สุดท้ายแล้วก็ขอให้เลือกใช้งานตามความเหมาะสมนะครับ