หลังจาก Android 7.0 Nougat ได้เปิดตัวไปในหลายเดือนก่อน ก็เป็นที่เฮฮาสำหรับเหล่าผู้ใช้แอนดรอยด์ โดยเฉพาะอย่างยิ่งกับผู้ใช้ Nexus รุ่นใหม่ๆ เพราะสามารถลองใช้งานได้เลย แต่ก็นั่นแหละ ความสุขของผู้ใช้งานมักจะมาพร้อมกับความลำบากของนักพัฒนาเสมอ ซึ่งหนึ่งในความลำบากของ Android 7.0 Nougat ก็คือ Multi Window นั่นเอง แล้วจะรับมือกับมันยังไงดีล่ะ?

Multi Window บน Android 7.0 Nougatเป็นยังไงล่ะ?

ก่อนจะเริ่มรับมือก็ต้องมาทำความรู้จักกันก่อนเนอะ ว่า Multi Window มันเป็นยังไงและทำงานยังไง

Multi Window เป็นฟีเจอร์หลักๆที่ Android 7.0 Nougat นั้นภูมิใจนำเสนอเลยก็ว่าได้ ซึ่งจริงๆเค้าก็แอบใส่ไว้ตั้งแต่สมัย Android 5.1 Lollipop แล้วล่ะ แต่ว่าซ่อนไว้ (แอบไปเปิดผ่าน ADB ได้) และก็พึ่งจะเปิดตัวให้ใช้งานกันในเวอร์ชัน N นั่นเอง ซึ่ง Multi Window จะแบ่งออกเป็น 2 แบบด้วยกันคือ Split Screen และ Freeform

ซึ่ง Split Screen จะเป็นการแบ่งหน้าจอเป็น 2 แอป ซึ่งสามารถกำหนดได้ว่าจะให้แอปไหนอยู่อีกฝั่งของหน้าจอ และจะมีแถบคั่นตรงกลางที่ให้ผู้ใช้สามารถปรับได้ว่าจะให้แอปฝั่งไหนแคบ ฝั่งไหนกว้าง

ส่วน Freeform จะเป็นหน้าต่างย่อยอยู่บนหน้าจอเลย เหมือนกับการใช้คอมพิวเตอร์นั่นเอง และยังสามารถปรับขนาดหน้าจอได้ตามใจชอบอีกด้วย ซึ่งจะเหมาะกับการใช้งานบน Tablet ที่ต้องการเปิดหน้าจอหลายๆอย่างพร้อมกัน

และนอกจากนี้ยังมี Picture in Picture ด้วย แต่มีเฉพาะบน Android TV เท่านั้น (ซึ่งบน Android TV ไม่มี Split Screen และ Freeform)

สำหรับ Split Screen จะใช้งานได้ในทุกๆเครื่องที่เป็น Android 7.0 Nougat แต่สำหรับ Freeform จะขึ้นอยู่กับผู้ผลิตว่าจะเปิดให้ใช้งานหรือป่าว (แต่สามารถใช้ ADB เข้าไปสั่งเปิดได้)

ทิศทาง (Orientation) ของหน้าจอแอป

ให้ลืมเรื่องทิศทางของเครื่องไปเลย เพราะว่า Orientation ของแอปนั้นจะขึ้นอยู่กับความกว้างและความสูงของหน้าจอแอป ซึ่ง

  • Portrait : ความสูงมากกว่าความกว้าง (Height > Width)
  • Landscape : ความกว้างมากกว่าความสูง (Width > Height)

ดังนั้นทิศทางของหน้าจอแอปจะขึ้นอยู่กับขนาดของหน้าจอแอป โดยไม่สนว่าเครื่องจะหันอยู่ในทิศทางไหนก็ตาม

เหมือน Multi Window ของ Samsung เลยสิ

ถ้าผู้ที่หลงเข้ามาอ่านคนไหนใช้อุปกรณ์แอนดรอยด์ของ Samsung อยู่ ก็อาจจะบอกว่า

“เฮ้ย มันมีใน Samsung มานานแล้วนี่!!”

ใช่ครับ มันมีมานานมากใน Samsung จนเกือบทุกเครื่องของ Samsung แล้วในตอนนี้

ดังนั้นมันจึงไม่ใช่เรื่องแปลกซักเท่าไรนัก แต่ก็จะมีความแตกต่างอยู่บ้างในบางจุด

เช่น Split Screen ของ Samsung สามารถปรับเส้นแบ่งระหว่างแอปได้ละเอียดกว่า เพราะของ Android 7.0 Nougat จะปรับได้ไม่กี่ระดับเท่านั้น

ส่วน Freeform ของ Android 7.0 Nougat นั้นสามารถปรับความกว้างความสูงได้ตามใจชอบ ในขณะที่ของ Samsung จะปรับได้แค่ Scale เท่านั้น โดยที่สัดส่วนระหว่างความกว้างและความสูงยังเท่าเดิม (อิงตามหน้าจอของเครื่อง) และ Freeform ของ Android 7.0 Nougat จะเป็นหน้าต่างแยกอีกส่วนแทน ส่วนของ Samsung จะอยู่บนหน้าจอหลักเลย ทั้งคู่สามารถย่อเก็บไว้ได้

แล้วการพัฒนาแอปต้องเปลี่ยนไปแค่ไหน?

คงจะเป็นคำถามแรกๆเลยที่อยากจะรู้เกี่ยวกับ Multi Window บน Android 7.0 Nougat แต่ทว่าเรื่องดีก็คือ Multi Window นั้นถูกคิดและออกแบบมาอย่างดีและใช้สิ่งที่มีอยู่แล้วนั่นเองที่จะมาช่วยให้นักพัฒนาจัดการได้ง่ายขึ้นโดยที่แทบจะไม่ต้องแก้ไขอะไรจากเดิมเลย นั่นก็คือ Configuration Changes และ Configuration Qualifier นั่นเอง

Handle configuration changes | Android Developers
App resources overview | Android Developers

Multi Window มีผลอย่างไรกับ Configuration Change?

เวลาที่ผู้ใช้ปรับขนาดหน้าจอเมื่อใช้งาน Split Screen หรือ Freeform อยู่ จะทำให้เกิด Configuration Change ส่งผลให้ Activity ถูกทำลายและสร้างขึ้นมาใหม่อย่างรวดเร็ว คล้ายๆกับตอนที่หมุนหน้าจอนั่นแหละ สิ่งที่เปลี่ยนแปลงก็คือค่า screenWidthDp และ screenHeightDp ซึ่งจะเปลี่ยนทุกครั้งที่ผู้ใช้ลากเพื่อเปลี่ยนขนาดหน้าจอของแอป (เรียกได้ว่า Change กันแบบรัวๆ)

อะไรนะ? Activity ถูกทำลายแล้วสร้างขึ้นมาใหม่? แล้วจะจัดการยังไงล่ะ?

ถ้าผู้ที่หลงเข้ามาอ่านคนใดยังไม่รู้จัก Configuration Changes ก็แนะนำให้ไปอ่านและทำความเข้าใจก่อนนะ Configuration Changes อีกหนึ่งอย่างที่นักพัฒนาแอนดรอยด์ควรรู้จัก

Configuration Changes เรื่องสำคัญที่ Android Dev ไม่ควรพลาด
อาจจะเคยได้ยินคำว่า Configuration Changes กันมาบ้าง แต่รู้หรือไม่ว่าจริง ๆ แล้วมันคืออะไร และทำไมถึงสำคัญมากขนาดที่ว่าถ้าไม่จัดการอย่างถูกต้องก็จะทำให้แอปมีปัญหาไปเลยก็เป็นได้

ดังนั้น Best Practice ก็คือการทำ Save/Restore State ให้กับแอปซะ (เชื่อว่าส่วนใหญ่ไม่ยอมทำกัน) ถ้าแอปของผู้ที่หลงเข้ามาอ่านจัดการเรื่องนี้อยู่แล้วก็ไม่ต้องห่วงอะไรครับ แต่ถ้ายังไม่ได้ทำ เจ้าของบล็อกก็แนะนำว่าให้ทำเถอะ มันไม่ได้มีผลแค่ตอนหมุนหน้าจอหรือปรับขนาดหน้าจอเท่านั้นนะครับ

ค่า android:configChanges ใน Android Manifest ที่ส่งผลกับ Multi Window จะมีอยู่ถึง 4 ค่าเลยทีเดียว ซึ่งก็คือ

  • screenSize
  • smallestScreenSize
  • screenLayout
  • orientation

และอย่าหวังว่าการกำหนด

android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"

แบบนี้จะช่วยให้ไม่ต้องจัดการอะไรนะครับ เพราะถ้าทำแบบนี้เมื่อไร เวลาหมุนหน้าจอจะทำให้แอปไม่ปรับขนาดตามหน้าจอทันทีเพราะการกำหนด smallestScreenSize กับ screenLayout จะทำให้แอปไม่ไปดึงค่า Resource ตามที่แยกเตรียมไว้

Multi Window มีผลอย่างไรกับ Configuration Qualifier ล่ะ?

อันนี้คงไม่ต้องบอกอะไรมาก เพราะเมื่อปรับขนาดหน้าจอแอปเวลาที่แสดงบน Multi Window นั่นก็หมายความว่าขนาดหน้าจอมีการเปลี่ยนแปลง อาจจะทำให้จากเดิมตัวแอปไปดึง Resource จาก values-sw600dp ก็เปลี่ยนไปดึงจาก values-sw400dp แทน เพราะขนาดหน้าจอเปลี่ยนนั่นเอง

เรื่อง Configuration Qualifier เนี่ย เจ้าของบล็อกคิดว่านักพัฒนาทุกคนต้องทำอยู่แล้วเนอะ?

แล้วนักพัฒนาจะต้องรับมืออย่างไร?

หลักๆก็คือจัดการเรื่อง Configuration Change และใช้ Configuration Qualifier ในการออกแบบหน้าตาแอปนั่นแหละครับ แต่เพื่อไม่ให้มันดูรวบรัดไป เลยทำเรียงออกมาเป็นข้อๆให้ไปลองเช็คดูนะครับ

  • ต้อง Save/Restore State ได้ เพราะเวลาที่ปรับขนาดหน้าจอบน Split Screen หรือ Freeform จะถือว่าเป็น Configuration Changes นั่นเอง ดังนั้นต้องเก็บ State ต่างๆภายในแอปให้ดี ไว้ว่าจะ View หรือ Model Data ต่างๆ รวมไปถึงการโหลดข้อมูลใดๆก็ตามควรทำงานต่อจากของเดิมได้
  • ออกแบบให้รองรับกับหน้าจอแนวตั้งและแนวนอน เพราะใน Multi Window จะไม่สามารถกำหนดได้ว่าผู้ใช้สามารถปรับขนาดมากน้อยแค่ไหน ซึ่งก็จะส่งผลถึง Orientation ของหน้าจอแอปนั่นเอง ดังนั้นควรออกแบบให้รองรับไว้ด้วยนะครับ
  • ออกแบบให้รองรับกับหน้าจอทุกขนาด ไม่ว่าจะมือถือขนาดเล็กหรือแทบเล็ตขนาดใหญ่ เพราะผู้ใช้อาจจะใช้งานบนแทบเลต แล้วปรับแอปให้มีขนาดเท่ามือถือ ดังนั้นไม่แนะนำให้ทำแอปแยกกันระหว่างมือถือและแทบเลตนะครับ แต่ควรทำให้รองรับแบบครอบคลุมแทน โดยใช้ Configuration Qualifier เข้ามาช่วย แยก Resource ระหว่างหน้าจอขนาดต่างๆโดยกำหนดจาก sw แทน ไม่ว่าจะ default, sw540dp และ sw720dp ก็จะช่วยให้รองรับได้อย่างครอบคลุมแล้วล่ะ
  • อย่าลืมทดสอบดูนะครับ ถ้าไม่มี Android 7.0 Nougat ให้ลองใช้งาน ก็ทดสอบด้วยการเปิดแอปแล้วดูว่าเมื่อหมุนหน้าจอยังสามารถทำงานทุกๆอย่างได้ปกติหรือไม่ แล้วก็ลองทดสอบกับหน้าจอขนาดต่างๆให้มากที่สุดเพื่อดูว่าครอบคลุมมากพอมั้ยครับ แต่ถ้ามี Android 7.0 Nougat ให้ใช้อยู่แล้ว เจ้าของบล็อกแนะนำว่าให้เปิด Freeform แล้วทดสอบดูได้เลยนะ (Split Screen ก็ทดสอบได้ แต่ Freeform ใช้ง่ายกว่าและปรับได้เยอะกว่า โดยไม่ต้องหมุนหน้าจอไปมา)

คำสั่งต่างๆที่เพิ่มเข้ามาสำหรับ Multi Window ใน Android 7.0 Nougat

Configuration สำหรับ Multi Window

android:resizeableActivity="true"

resizeableActivity เอาไว้กำหนดใน <application> หรือ <activity> ที่อยู่ใน Android Manifest กำหนดค่าเป็น Boolean โดยจะ Default ไว้เป็น true

ถ้าแอปของผู้ที่หลงเข้ามาอ่านกำหนดไว้เป็น false แต่ผู้ใช้กดเปิดแอปขณะที่อยู่ใน Multi Window ก็จะเป็นการเปิดแอปแบบเต็มหน้าจอให้อัตโนมัติ เพราะงั้นไม่ต้องห่วง

android:supportsPictureInPicture="true"

supportsPictureInPicture เอาไว้กำหนดใน <Application> หรือ <Activity> เช่นเดียวกับ resizeableActivity แต่ทว่าจะมีไว้กำหนดสำหรับ Android TV เพราะจะมี Picture in Picture ให้ใช้แทน Split Screen และ Freeform

Configuration สำหรับ Freeform

<layout> เพิ่มเข้ามาใน Android Manifest เพื่อใช้ใน <activity> สำหรับกำหนดค่าในการแสดงผลของ Freeform ในแต่ละ Activity

<activity android:name=".MyActivity">
    ...
    <layout android:defaultHeight="500dp"
        android:defaultWidth="600dp"
        android:gravity="top|end"
        android:minHeight="450dp"
        android:minWidth="300dp" />
</activity>

defaultHeight สำหรับกำหนดค่า Default ของความสูงหน้าต่างแอปเมื่อเข้าสู่ Freeform โดยกำหนดค่าเป็นหน่วย dp

defaultWidth สำหรับกำหนดค่า Default ของความกว้างหน้าต่างแอปเมื่อเข้าสู่ Freeform โดยกำหนดค่าเป็นหน่วย dp

gravity สำหรับกำหนดว่าจะให้เปิดแอปใน Freeform แล้วไปอยู่บริเวณไหนของหน้าจอ กำหนดค่าเหมือนกับ Gravity ที่อยู่ใน XML Layout เลย (top, bottom, left, center, center_vertical และบลาๆๆๆ)

minimalHeight สำหรับกำหนดความสูงต่ำสุดที่จะแสดงใน Freeform ถ้าผู้ใช้ปรับความสูงต่ำกว่าที่กำหนดไว้ หน้าต่างแอปก็จะถูก Crop หายไปแทน โดยกำหนดค่าเป็นหน่วย dp

minimalWidth สำหรับกำหนดความกว้างต่ำสุดที่จะแสดงใน Freeform ถ้าผู้ใช้ปรับความกว้างต่ำกว่าที่กำหนดไว้ หน้าต่างแอปก็จะถูก Crop หายไปแทน โดยกำหนดค่าเป็นหน่วย dp

<layout> ตัวนี้เป็นคนละอันกับใน XML Layout นะ อย่าเข้าใจผิดล่ะ!!

คำสั่ง Kotlin (ใช้ได้เฉพาะ Android 7.0 Nougat ขึ้นไปเท่านั้น)

Activity.isInMultiWindowMode()

isInMultiWindowMode() สำหรับเช็คว่า Activity ตัวนั้นๆแสดงอยู่ใน Multi Window หรือไม่

Activity.isInPictureInPictureMode()

isInPictureInPictureMode() สำหรับเช็คว่า Activity ตัวนั้นๆแสดงอยู่ใน Picture in Picture หรือไม่

เนื่องจากคำสั่งนี้มีไว้สำหรับ Android TV เท่านั้น แต่ว่า Picture in Picture นั้นเป็นส่วนหนึ่งของ Multi Window ดังนั้นถ้า isInPictureInPictureMode() เป็น true ก็จะได้ค่า isInMultiWindowMode() เป็น true ด้วยเช่นกัน แต่ถ้าไม่ใช่ Android TV ก็จะเป็นได้ isInPictureInPictureMode() เป็น false ตลอดนะ

Activity.onMultiWindowModeChanged(isInMultiWindowMode: Boolean)
Fragment.onMultiWindowModeChanged(isInMultiWindowMode: Boolean)

onMultiWindowModeChanged(...) เป็น Override Method สำหรับดัก Event เมื่อแอปเปลี่ยนการแสดงผลระหว่างแบบปกติกับ Multi Window และมีการส่ง Boolean มาให้เช็คด้วยว่าเป็น Multi Window หรือไม่ ซึ่งสามารถใช้ได้ทั้งใน Activity และ Fragment

Activity.onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean)
Fragment.onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean)

onPictureInPictureModeChanged(...) เป็น Override Method สำหรับดัก Event เมื่อแอปเปลี่ยนการแสดงผลระหว่างแบบปกติกับ Picture in Picture และมีการส่ง Boolean มาให้เช็คด้วยว่าเป็น Picture in Picture หรือไม่ ซึ่งสามารถใช้ได้ทั้งใน Activity และ Fragment

การรองรับ Drag & Drop

ก็คล้ายๆกับของ Samsung นั่นแหละครับ ที่ผู้ใช้สามารถลาก Content บางอย่างจากแอปอีกตัวไปใส่ในแอปอีกตัวได้ (เช่นลากภาพจากในแอปของเจ้าของบล็อกไปวางไว้ใน Gmail เพื่อแนบเป็นภาพประกอบ) ซึ่งตรงนี้ก็ไม่มีอะไรมากเพราะมันคือการทำ Drag & Drop ตามปกติของแอนดรอยด์ซึ่งข้อมูลพวกนี้จะอยู่ในรูปของ Clip Data (ลองไปอ่านเพิ่มเติมเอาเองนะ)

แต่ทว่าการลาก Content ข้ามแอปนั้นจะมีเรื่องของ Permission อยู่เล็กน้อย ซึ่งจะต้อง Request เพื่อขอใช้งาน Drag & Drop เสียก่อน โดยใช้คำสั่งที่มีอยู่แล้วใน Activity

Activity.requestDropPermissions(event: DragEvent)

กำหนดให้ Activity เปิดเป็นหน้าต่างแยกใน Multi Window

ในกรณีที่แอปแสดงอยู่บน Multi Window แล้วเกิดอยากจะให้กดเปิด Activity อีกตัวขึ้นมา แต่อยู่คนละหน้าต่างกัน ก็สามารถทำได้เช่นกัน โดยกำหนด Flag ที่ชื่อว่า FLAG_ACTIVITY_LAUNCH_ADJACENT เพิ่มลงไปใน Intent ตอนเรียก Activity ที่ต้องการ

val intent = Intent().apply {
    flags = Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT or
            Intent.FLAG_ACTIVITY_NEW_TASK or
            Intent.FLAG_ACTIVITY_MULTIPLE_TASK
}
startActivity(intent)

และถ้าแสดงใน Freeform สามารถกำหนดได้ด้วยว่าอยากจะให้หน้าจอโผล่ขึ้นมาที่ตรงไหนของหน้าจอและขนาดเท่าไหน โดยกำหนดที่ ActivityOptions ด้วยคำสั่งดังนี้

val context: Context = ...
val intent = Intent(context, HomeActivity::class.java).apply {
    flags = Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT or
            Intent.FLAG_ACTIVITY_NEW_TASK or
            Intent.FLAG_ACTIVITY_MULTIPLE_TASK
}
val activityOptions = ActivityOptions.makeBasic()
activityOptions.launchBounds = Rect(0, 0, 100, 100)
val bundle = activityOptions.toBundle()
startActivity(intent, bundle)

การกำหนดจะกำหนดค่าด้วย Rect ซึ่งกำหนดตำแหน่งเป็น Left, Top, Right และ Bottom ของหน้าจอ ซึ่งจะแปลงไปเป็น Bundle แล้วโยนให้คำสั่ง startActivity นั่นเอง

ซึ่งคำสั่งทั้งสองอย่างนี้จะไม่มีผลถ้าแอปไม่ได้แสดงอยู่ใน Multi Screen

ทำอย่างไร ถ้าไม่อยากให้แอปรองรับ Multi Window

จริงๆก็มีอยู่บางเงื่อนไขครับที่จะทำให้แอปไม่สามารถแสดงผลเป็น Multi Window ได้ ซึ่งจะมีดังนี้

  • กำหนดไว้ใน Android Manifest ว่า resizeableActivity เป็น false
  • กำหนด Orientation เป็นทิศทางใดทิศทางหนึ่งเท่านั้น (หมุนหน้าจอไปมาไม่ได้)

วิธีเหล่านี้อาจจะเป็นทางเลือกที่ดีสำหรับนักพัฒนาที่ไม่อยากปวดหัวกับ Multi Window

สรุป

เรียกได้ว่า Multi Screen จะเข้ามาทำให้รูปแบบการแสดงผลของแอปนั้นเปลี่ยนแปลงไปมากพอสมควร โดยเฉพาะ Freeform ที่ทำให้แทบเลตแอนดรอยด์สามารถทำงานได้สะดวกขึ้น Multitasking มากขึ้น ไม่ต้องคอยกดสลับไปมาให้น่ารำคาญอีกต่อไป

ซึ่งเป็นฟีเจอร์ใหม่ที่อาจจะเรียกได้ว่ามันคือความลำบากที่นักพัฒนาต้องรับมือ แต่จริงๆแล้วทางแนอดรอยด์ก็ไม่ได้กำหนดให้ต้องเขียนใหม่อะไรมากมาย กลับเอาสิ่งที่ต้องทำอยู่แล้วมาช่วยให้สะดวกมากขึ้น ดังนั้นความลำบากที่แท้จริงก็คือการที่นักพัฒนาสร้างแอปไม่ตรงตาม Best Practice นั่นเอง ดังนั้นทำให้มันถูกต้องซะ

โดยส่วนตัวเจ้าของบล็อกแนะนำให้ทำแอปที่รองรับ Multi Window นะครับ ถึงแม้ว่าจะสามารถเลี่ยงได้ก็ตาม เพราะมันจะช่วยเพิ่มความน่าใช้งานของแอปผู้ที่หลงเข้ามาอ่านให้มากขึ้น อาจจะฟังดูไม่สำคัญ แต่บ่อยครั้งเจ้าของบล็อกก็เจอผู้ใช้แอปบางตัวที่ถามว่า “ทำไมแอป… ไม่สามารถทำ…ได้นะ” ซึ่งสาเหตุก็มาจากนักพัฒนาไม่ยอมทำให้รองรับนั่นเอง ดังนั้นถ้าทำได้ก็ควรทำเถอะครับ เพื่อให้แอปนั้นดูน่าใช้งานในสายตาผู้ใช้ แต่ถ้าไม่ต้องการจริงๆก็สามารถปิดได้ครับ

แหล่งอ้างอิงข้อมูล