25 เมษายน 2559

[Android Code] รู้จัก Multi Window บน Android N และวิธีการรับมือ



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

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

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

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

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


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


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

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

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

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

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


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

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

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

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

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


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

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

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

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

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

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

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

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

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

        ดังนั้น Best Practice ก็คือการทำ Save/Restore Instance 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-400dp แทน เพราะขนาดหน้าจอเปลี่ยนนั่นเอง

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

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

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

        • ต้อง Save/Restore Instance State ได้ เพราะเวลาที่ปรับขนาดหน้าจอบน Split Screen หรือ Freeform จะถือว่าเป็น Configuration Changes นั่นเอง ดังนั้นต้องเก็บ State ต่างๆภายในแอพให้ดี ไว้ว่าจะ View หรือ Model Data ต่างๆ รวมไปถึงการโหลดข้อมูลใดๆก็ตามควรทำงานต่อจากของเดิมได้

        • ออกแบบให้รองรับกับหน้าจอแนวตั้งและแนวนอน เพราะใน Multi Window จะไม่สามารถกำหนดได้ว่าผู้ใช้สามารถปรับขนาดมากน้อยแค่ไหน ซึ่งก็จะส่งผลถึง Orientation ของหน้าจอแอพนั่นเอง ดังนั้นควรออกแบบให้รองรับไว้ด้วยนะครับ

        • ออกแบบให้รองรับกับหน้าจอทุกขนาด ไม่ว่าจะมือถือขนาดเล็กหรือแทบเล็ตขนาดใหญ่ เพราะผู้ใช้อาจจะใช้งานบนแทบเลต แล้วปรับแอพให้มีขนาดเท่ามือถือ ดังนั้นไม่แนะนำให้ทำแอพแยกกันระหว่างมือถือและแทบเลตนะครับ แต่ควรทำให้รองรับแบบครอบคลุมแทน โดยใช้ Configuration Qualifier เข้ามาช่วย แยก Resource ระหว่างหน้าจอขนาดต่างๆโดยกำหนดจาก sw แทน ไม่ว่าจะ sw320dp, sw360dp, sw480dp, sw600dp และ sw720dp ก็จะช่วยให้รองรับได้อย่างครอบคลุมแล้วล่ะ

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


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

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:minimalHeight="450dp"
        android:minimalWidth="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 นะ อย่าเข้าใจผิดล่ะ!!

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

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

Activity.onPictureInPictureModeChanged(boolean isInPictureInPictureMode)
Fragment.onPictureInPictureModeChanged(boolean isInPictureInPictureMode)
        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(DragEvent event)

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

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

Intent intent = ...
intent.setFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT |
        Intent.FLAG_ACTIVITY_NEW_TASK |
        Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
startActivity(intent);


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

Intent intent = new Intent(MainActivity.this, Main2Activity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT |
        Intent.FLAG_ACTIVITY_NEW_TASK |
        Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
ActivityOptions activityOptions = ActivityOptions.makeBasic();
activityOptions.setLaunchBounds(new Rect(0, 0, 100, 100));
Bundle 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 นะครับ ถึงแม้ว่าจะสามารถเลี่ยงได้ก็ตาม เพราะมันจะช่วยเพิ่มความน่าใช้งานของแอพผู้ที่หลงเข้ามาอ่านให้มากขึ้น อาจจะฟังดูไม่สำคัญ แต่บ่อยครั้งเจ้าของบล็อกก็เจอผู้ใช้แอพบางตัวที่ถามว่า "ทำไมแอพ... ไม่สามารถทำ...ได้นะ" ซึ่งสาเหตุก็มาจากนักพัฒนาไม่ยอมทำให้รองรับนั่นเอง ดังนั้นถ้าทำได้ก็ควรทำเถอะครับ เพื่อให้แอพนั้นดูน่าใช้งานในสายตาผู้ใช้ แต่ถ้าไม่ต้องการจริงๆก็สามารถปิดได้ครับ

        และในตอนนี้ Android N ยังอยู่ในช่วง Developer Preview อยู่ ดังนั้นอาจจะมีการเปลี่ยนแปลงบางอย่างเกี่ยวกับ Multi Window ในภายหลัง ถ้ามีอะไรเปลี่ยนแปลง (และมีเวลา) เจ้าของบล็อกก็จะกลับมาอัพเดทให้นะครับ

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

        • Multi-Window Support [Android Developers]
        • 5 tips for preparing for Multi-Window in Android N




เหล่าพันธมิตรแอนดรอยด์

Devahoy Layer Net NuuNeoI The Cheese Factory Somkiat CC Mart Routine Artit-K Arnondora Kamonway Try to be android developer Oatrice Benz Nest Studios Kotchaphan@Medium Jirawatee@Medium Travispea@Medium