น้อยคนนักที่จะรู้จักคำว่า System UI ในแอปพลิเคชันแอนดรอยด์ แต่ถ้าพูดถึงการทำให้แอปฯแสดงหน้าจอแบบ Fullscreen หรือ Immersive ก็คงจะร้องอ๋อกัน ซึ่งในบทความนี้ก็จะขอพูดถึงการควบคุมการทำงานของ System UI ภายในแอปฯกันครับ

ซึ่ง System UI ที่ว่านี้ก็หมายถึง Status Bar, Navigation Bar และ Action Bar (หรือ Toolbar) ที่คุ้นเคยกันดีนั่นเอง ซึ่งระบบแอนดรอยด์ก็เปิดให้แอปฯสามารถเปลี่ยนรูปแบบการแสดงผลของ System UI ให้เหมาะกับการใช้งานแอปฯในแต่ละรูปแบบได้

ซึ่งควบคุมยังไงได้บ้างนั้นก็จะขึ้นอยู่กับเวอร์ชันของแอนดรอยด์ด้วย เพราะเวอร์ชันเก่าๆก็อาจจะทำงานบางอย่างไม่ได้ แต่จะมีอะไรบ้างนั้นมาดูกันต่อเลย

Dimming (Low Profile)

หรืออีกชื่อหนึ่งที่เรียกว่า Low Profile เป็นความสามารถที่ถูกเพิ่มเข้ามาตั้งแต่สมัย Android 4.0 Ice Cream Sandwich(API 14) โดยจะลดการแสดงผลของ Status Bar และ Navigation Bar เพื่อให้ผู้ใช้สนใจเนื้อหาในแอปฯเป็นระยะเวลานานๆ

มีข้อดีตรงที่ผู้ใช้ยังคงสามารถใช้งาน Status Bar กับ Navigation Bar ได้ทันที แต่ก็จะไม่ได้เพิ่มพื้นที่หน้าจอให้มากขึ้นเหมือนแบบ Fullscreen

โดยการสั่งงานสำหรับ 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 ให้ใช้คำสั่งซ่อน Status Bar แบบนี้แทน

val window: Window = /* ... */
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN

และอยากให้ Status Bar กลับมาแสดงอีกครั้งก็ให้เคลียร์ค่าเป็น 0 ซะ

val window: Window = /* ... */
window.decorView.systemUiVisibility = 0

แต่ถ้าสังเกตดีๆจะเห็นว่า เมื่อมีการซ่อน/แสดง Status Bar จะทำให้ UI ของแอปขยับชิดขอบบนตามด้วย

การซ่อน/แสดง Navigation Bar

หลังจากซ่อน/แสดง Status Bar แล้ว คราวนี้ถึงคราวของ Navigation Bar บ้าง ซึ่งการซ่อน Navigation Bar นั้นจะทำได้ตั้งแต่ API 16 ขึ้นไปเท่านั้น

// คำสั่งซ่อน Navigation Bar
val window: Window = /* ... */
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION

// คำสั่งแสดง Navigation Bar
val window: Window = /* ... */
window.decorView.systemUiVisibility = 0

จะเห็นว่าปุ่ม Save เจ้าของบล็อกได้จัดไว้ให้อยู่ตำแหน่งข้างล่างสุด แต่เมื่อ Navigation Bar ถูกซ่อน จึงทำให้ปุ่มต้องขยับตามไปด้วย (แบบเดียวกับ Status Bar เลย)

ซ่อน/แสดง Navigation Bar และ 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 ครั้งแรก จะมีหน้าต่างแสดงให้ผู้ใช้รับรู้ด้วย

สำหรับการออกจาก 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 เท่านั้นเอง

ซึ่งผลลัพธ์จะออกมาเป็นลักษณะแบบนี้

จะเห็นว่าผู้ใช้จะอยู่ในโหมด 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 ได้เลย

จากภาพข้างบนก็จะเห็นว่าการสั่งให้ 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

สุดท้ายแล้วก็ขอให้เลือกใช้งานตามความเหมาะสมนะครับ