22 October 2014

Fragment Principle - มารู้จักกับ Fragment กันเถอะ~

Updated on


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

บทความที่เกี่ยวข้อง

        • Fragment Principle - มารู้จักกับ Fragment กันเถอะ~
        • Let's Fragment - เริ่มต้นง่ายๆกับ Fragment แบบพื้นฐาน
        • Let's Fragment - ว่าด้วยเรื่องการสร้าง Fragment จาก Constructor ที่ถูกต้อง
        • Let's Fragment - รู้จักกับ FragmentTransaction สำหรับการแสดง Fragment [ตอนที่ 1]
        • Let's Fragment - รู้จักกับ FragmentTransaction สำหรับการแสดง Fragment [ตอนที่ 2]
        • Let's Fragment - วงจรชัวิตของ Fragment (Fragment Lifecycle)
        • Let's Fragment - มาทำ View Pager กันเถิดพี่น้อง~ [ตอนที่ 1]
        • Let's Fragment - มาทำ View Pager กันเถิดพี่น้อง~ [ตอนที่ 2]

รู้จักกับ Fragment

        Fragment นั้นถูกนำเสนอขึ้นมาตั้งแต่ Android 3.0 Honeycomb ซึ่งเป็นยุคเริ่มต้นของ Android Tablet ที่มีหน้าจอขนาดใหญ่ขึ้น การใช้งานแตกต่างไปจาก Phone ดังนั้นในการออกแบบ UI แบบเดิมๆที่เคยมีอยู่ก็จะเปลี่ยนแปลงไปสิ้นเชิง

        เวลาเขียนแอพฯบน Phone แบบมีหลายหน้าก็ทำ Activity หลายๆ Activity โดยให้แต่ละหน้าแสดงต่างกันไปตามต้องการ



        และเมื่อมี Tablet ขึ้นมา วิธีที่ทำอยู่เช่นนี้ก็ยังคงใช้งานได้เหมือนเดิมนะ แต่ทว่าหน้าจอที่มันใหญ่ขึ้น ก็หมายความว่าต้องขยาย UI ให้ใหญ่ขึ้นตามหน้าจอ?



        มีนักพัฒนาหลายๆคนที่ใช้วิธี Scale จากหน้าจอแบบ Phone ให้พอดีกับแบบ Tablet แทน เพื่อที่ว่าจะได้ไม่ต้องแก้อะไรมาก


        แต่เอาเข้าจริง วิธีแบบนี้ถูกต้องแล้วหรือ?

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



        ดังนั้นเพื่อให้เกิดประโยชน์สูงสุดสำหรับ Tablet จึงต้องเปลี่ยนวิธีดีไซน์หน้าตาของแอพฯให้เหมาะสมกับแต่ละอุปกรณ์ยิ่งขึ้น (เป็นที่มาของคำว่า Fragmentation นั่นเอง) โดยที่สร้างแอพฯแค่ตัวเดียว แต่รันได้บนอุปกรณ์ทุกๆแบบ ไม่ต้องมานั่งทำเป็น For Tablet หรือ For Phone แยกกัน

        โดยแนวคิดนี้จะสมมติตัวอย่างง่ายๆว่า แอพฯโดยพื้นฐานจะมีหน้าเมนูหลักที่กดแล้วจะไปแต่ละหน้าย่อย



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


        ซึ่งวิธีนี้เรียกว่า Multi-pane UI โดยจะช่วยให้ใช้ประโยชน์จากพื้นที่ในอุปกรณ์แต่ละแบบได้อย่างมีประสิทธิภาพมากขึ้น แต่ถึงกระนั้นก็ต้องทำในแต่ละหน้าให้ Flexible UI อยู่ดีนะ

        แล้วจะทำยังไงล่ะ?

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



        เงื่อนไขคือแอพฯตัวนั้นต้องรองรับทั้งแบบ Tablet และ Phone ตามที่วางไว้ ดังนั้นดูภาพตัวอย่างข้างบนแล้วนึกภาพตามคร่าวๆเลย ว่าถ้าจะต้องมานั่งเขียน Activity หนึ่งตัว ให้ Manage สองแบบ โดยเช็คว่าเป็น Tablet หรือ Phone แล้วค่อยดึง Layout แยกกัน และใช้คำสั่งต่างกัน

        แค่นึกก็ท้อแล้วใช่มั้ยล่ะ? นี่ยังไม่รวมว่ามีหลายๆหน้าอีกด้วยนะ

        แต่เอาเข้าจริงก็มีผู้ที่หลงเข้ามาอ่านที่ใช้วิธีสร้าง Layout ให้มีแถบ Menu ทุกๆหน้าซะเลย แล้วมองว่าเป็น Activity ปกติ เวลาเปลี่ยนหน้าก็ใช้ Intent เหมือนเดิม


        แต่วิธีนี้ก็ไม่ถูกต้องเช่นกัน เพราะว่าต้องแปะคำสั่งของเมนูไว้ที่ทุกๆ Activity กลายเป็น Duplicate Code ซะงั้น และเวลาเปลี่ยนหน้าก็จะเกิด Transition ไปทั้งหน้าจอด้วย ซึ่งดูไม่ค่อย Make Sense ซักเท่าไร


        ดังนั้นจึงเลิกคิดที่จะเอา Activity มาทำแบบนี้ได้เลย

เลิกใช้ TabActivity กันได้แล้วนะ

        อีกวิธีที่มีผู้ที่หลงเข้ามาอ่านหลายๆคนใช้อยู่ คือ TabActivity ซึ่งเป็น Activity ที่ใช้งานร่วมกับ Tab Host เพื่อแสดง Activity ย่อยไว้ใน Activity อีกทีหนึ่ง

        ข้อดีคือ Activity ถูกแยกเป็นสัดส่วนเรียบร้อยแล้ว โค๊ดสำหรับหน้านั้นๆก็อยู่ที่ Activity ย่อย ส่วนหน้าเมนูก็อยู่ใน TabActivity ไปเลย ดังนั้นเวลากดเลือกเมนูใดๆใน TabActivity ก็จะเป็นการสั่งเปลี่ยน Activity ที่แสดงนั่นเอง



        แต่ทว่า TabActivity ก็เกิดมาในสมัยที่มีแต่ Phone เท่านั้น ซึ่งไม่ได้รองรับกับปัญหา Fragmentation ในเรื่องของหน้าจอซักเท่าไร อีกทั้งยังมีปัญหาในเรื่องของความยุ่งยากเนื่องจาก Activity มีการทำงานที่ซ้อนกัน รวมไปถึงปัญหาการส่งข้อมูลระหว่าง Activity ก็ยังทำได้ไม่ค่อยดีนัก (เช่น อยากจะกดปุ่มใน Activity แรก แล้วแสดงผลใน Activity ที่สอง)


        แล้วมันทำได้มั้ย? ทำได้ แต่ไม่สนุกหรอก...แม้แต่การส่งค่ากลับไปยัง TabActivity ก็ตาม


        เนื่องจาก TabActivity ทำขึ้นมาเพื่อให้ Activity ที่อยู่ในนั้นทำงานแยกออกจากกันซะมากกว่า ดังนั้นแต่ละส่วนจึงทำงานร่วมกันได้ค่อนข้างยาก


Fragment เกิดมาเพื่อยกระดับการทำงานที่ Activity ไม่ตอบโจทย์

        ทีม Android ได้มองเห็นถึงปัญหาเรื่อง Fragmentation อยู่แล้วจึงพัฒนา Fragment ขึ้นมาเพื่อรองรับการทำงานให้ยืดหยุ่นต่อการใช้งานมากขึ้น ซึ่งขึ้นอยู่กับว่าผู้ที่หลงเข้ามาอ่านจะหยิบ Fragment ไปใช้งานยังไงนั่นเอง

        Fragment เรียกได้ว่าเกิดมาเพื่อ Activity เลยก็ว่าได้ แต่ไม่ได้สืบทอดมาจาก Activity นะ อย่าเข้าใจผิดล่ะ โดย Fragment แต่ละตัวสามารถผูกเข้ากับ Layout เพื่อแสดงผลได้ (ทำหน้าที่เป็น Controller คล้ายกับ Activity) มี Life Cycle เป็นของตัวเอง (ต่างกับ Activity เล็กน้อย) เพื่อให้จัดการกับการทำงานใน Fragment นั้นๆได้สะดวก


        หรือจะพูดว่า Fragment เป็น Sub Controller ก็ว่าได้ มีการทำงานและความสามารถใกล้เคียงกับ Activity แต่ก็มีบางอย่างที่แตกต่างกันอยู่ เพราะ Fragment จะทำงานได้ก็ต่อเมื่อถูกแปะอยู่บน Activity เท่านั้น (ไม่สามารถแสดงผลได้ด้วยตัวเองแบบ Activity)

        รวมไปถึงคำสั่งเฉพาะของ Activity บางอย่าง เช่น onBackPressed, onActivityResult และ startActivity ก็จะไม่มีให้ใช้งานใน Fragment เช่นกัน

        โดย Fragment นั้นๆก็สามารถเรียกย้อนกลับไปยัง Activity ของตัวเองได้ เช่น อยากให้ทำคำสั่งบางอย่างของ Activity ก็เรียก Activity นั้นๆใน Fragment แล้วเรียก Methods ที่อยู่ใน Activity ได้ทันที


        จากเดิมที่นักพัฒนาเคยเขียนโค้ดต่างๆนานาไว้ใน Activity เมื่อเปลี่ยนมาใช้ Fragment ก็จะทำให้สามารถแยกหน้าที่ตาม Fragment แต่ละตัวได้ ดังนั้นโค้ดการทำงานต่างๆก็จะถูกแยกออกจากกันระหว่างแต่ละ Fragment โดยมี Activity เป็นตัวควบคุมการทำงานโดยรวมของ Fragment

        และการทำงานโดยรวมที่ว่าของ Activity ที่รวมไปถึงการควบคุม Fragment ที่จะแสดงด้วย ว่าจะให้ Fragment มีลำดับอย่างไร ซึ่งตรงนี้ช่วยให้ Fragment นั้นสามารถ Reusable ได้



        ใน Activity หนึ่งตัวอยากจะให้มี Fragment หลายๆตัวแสดงผลพร้อมๆกันก็สามารถทำได้นะ



        ณ จุดนี้ขอทำความเข้าใจก่อนนะว่า Fragment ไม่จำเป็นต้องมีมากกว่า 1 หรือแสดงแบบ List เสมอไปนะ เพียงแค่ว่ามันยกตัวอย่างแล้วเห็นภาพได้เข้าใจง่ายดี ถ้าจะใช้งานแค่ Fragment ตัวเดียวก็ทำได้เช่นกัน อย่างเช่น Google Maps API นั่นเอง ถ้าผู้ที่หลงเข้ามาอ่านคนใดเคยใช้ก็จะสังเกตเห็นว่าเวลาสร้าง Layout จะใช้เป็น Fragment



        ที่ Google Maps API ทำเป็น Fragment ก็เพราะว่าเผื่อนักพัฒนาที่นำไปใช้งานร่วมกับ Fragment ตัวอื่นๆนั่นเอง ดังนั้นถ้าใช้กับ Activity โดยตรงก็ทำได้ และถ้าใช้ร่วมกับ Fragment ตัวอื่นๆก็ทำได้เช่นกัน



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


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

Fragment API 

        โดยที่ Fragment นั้นจะมีสองแบบคือ API แบบที่มากับ Android 3.0 Honeycomb และแบบที่เป็น Android Support Library v4 เพื่อให้สามารถทำงานบนแอนดรอยด์เวอร์ชันเก่าๆได้

        • android.app.Fragment
        • android.support.v4.app.Fragment

        สำหรับ API ที่มากับ Android 3.0 Honeycomb นั้นไม่แนะนำให้ใช้แล้ว ให้ไปใช้ของ Android Support Library v4 แทน ซึ่งในบทความของเจ้าของบล็อกก็จะใช้เป็นตัว Android Support Library v4 เท่านั้น

สรุป 

        Fragment เรียกได้ว่าเป็น Component ที่หยิบความสามารถของ Activity มาใช้ ช่วยแยกการทำงานออกเป็นส่วนๆ โดยที่ไม่ให้ Activity รับภาระการทำงานจนมากเกินไป ซึ่งจุดเด่นของ Fragment ก็คือสามารถนำไปใช้งานซ้ำได้ (Reusable)

        โดย Activity หนึ่งตัวสามารถมี Fragment หลายๆตัวก็ได้ แต่ Fragment แต่ละตัวนั้นก็จะเหมือนกันหรือไม่เหมือนกันก็ได้ จึงทำให้มียืดหยุ่นในการนำไปใช้งานค่อนข้างสูงมากกกกกก สามารถแก้ปัญหาของ Activity ที่ไม่สามารถทำอะไรแบบนั้นได้ซักเท่าไร

        อ๊ะๆ อยากอ่านต่อแล้วล่ะสิ แต่ต้องขอจบ Fragment Principle เพียงเท่านี้ก่อน เพราะรู้สึกว่ายิ่งพิมพ์ก็ยิ่งเพลิน ตอนแรกๆไม่ได้ตั้งใจจะทำเยอะขนาดนี้นะเนี่ย ฮ่าๆ ดังนั้นขอเบรกก่อนว่าจะเอา Fragment มาทำ Multi-pane UI ได้อย่างไรนะครับ

        บทความนี้กินพลังงานเยอะมาก