"ความรักต้องไม่พยายาม"
[โหมด Geek] RecyclerView สิ่งใหม่ที่กูเกิ้ลหวังว่าจะทำให้แอพฯแอนดรอยด์ดีขึ้น
12 Oct 2014 14:04   [20489 views]

เข้าสู่โหมด Geek แบบลงโค้ดกันบ้างนิดหน่อยกับ RecyclerView ที่กูเกิ้ลประกาศเป็น View Component ตัวใหม่บน Android L คู่กับ CardView และเจ้า RecyclerView นี้ก็ถูกวางไว้เพื่อแทนที่ ListView อย่างจริงจัง ตอนนี้เราสามารถเอามาใช้ในรุ่นก่อน L ได้ด้วยผ่าน Support Libraries และด้วยความกระหายใครรู้ว่าทำไมต้องออกมาในเมื่อมี ListView อยู่แล้ว เราก็เลยเล่นมันเป็นที่เรียบร้อย เอามาเล่าให้ฟังว่ามันต่างจาก ListView ยังไงทั้งแง่ Concept และการใช้จริง

และเช่นเคย ... Rate G(eek) นะจ๊ะ เริ่มหละ

ความเหมือนที่แตกต่าง

ถ้าให้ดูภาพรวมแล้ว ต้องบอกว่า RecyclerView และ ListView ไม่มีอะไรต่างกันเลยจากภายนอก มันก็คือ List นั่นแหละ ถ้าเป็นผู้ใช้จะไม่มีทางดูออกเลย เพราะผลที่ได้เหมือนกัน

แต่ถ้ามาดูโค้ดหละก็ เรียกว่าต่างกันโดยสิ้นเชิงในแง่ของ Concept เพราะ Adapter ของ ListView จะเน้นการสร้าง View และคืน View ออกมา แต่กับ RecyclerView จะเป็นการังคับให้สร้าง ViewHolder แล้วคืนมาให้ระบบเก็บไว้แทนที่จะเป็น View

แล้วถ้าดูผลออกมาเหมือนกัน ทำไมต้องทำ RecyclerView ออกมา เจ้าสองแนวคิดนี้ต่างกันยังไง?

บังคับให้ใช้ ViewHolder Pattern และ Recycle View อัตโนมัติ

ปัญหาที่ถือว่าใหญ่พอสมควรของการใช้ ListView คือ Developer ใช้ผิดวิธี

คือตัว ListView เนี่ย มันสามารถ Recycle View ได้ ไม่ต้องสร้าง View ใหม่ทุกครั้ง แต่ Developer เริ่มต้นส่วนใหญ่ไม่รู้ว่ามีวิธีนี้ ก็เลยสร้าง View ใหม่ทุกครั้งที่มีการ Scroll ไปถึง ผลคือกระตุกๆๆๆๆๆๆ

ViewHolder Pattern ไม่ใช่เรื่องใหม่ จริงๆเป็นเรื่องเก่ามาก กูเกิ้ลพยายามแล้วพยายามอีกที่จะออก Best Practices ว่าควรใช้ ViewHolder Pattern นะ ต้องเช็ค convertView นะว่ามีรึเปล่า บลาๆๆๆ แต่สุดท้าย Developer ทั่วไปก็ไม่เข้าใจและไม่ใช้กันอย่างน่าประหลาด

สุดท้ายกูเกิ้ลก็เลยทุบโต๊ะปัง! ไม่ยอมใช้ใช่ม้ายยยย ด้ายยยยยยยย แก้ด้วยการออก View ใหม่เลยละกันชื่อว่า RecyclerView โดยมีคุณสมบัติว่า

1) บังคับให้ใช้ View Holder - เพราะตัว Adapter จะ Return เป็น ViewHolder ดังนั้นคุณต้องสร้าง ViewHolder บังคับเลย เอาสิ

public class RecyclerViewDemoAdapter extends RecyclerView.Adapter<RecyclerViewDemoAdapter.MyViewHolder> {    @Override    public MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {        View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_demo, viewGroup, false);        return new MyViewHolder(itemView);    }    @Override    public void onBindViewHolder(MyViewHolder viewHolder, int position) {        viewHolder.txt1.setText("Pos: " + position);        viewHolder.txt2.setText("Hello");    }    @Override    public int getItemCount() {        return 10;    }    public static class MyViewHolder extends RecyclerView.ViewHolder {        public TextView txt1;        public TextView txt2;        private MyViewHolder(View v) {            super(v);            this.txt1 = (TextView) v.findViewById(R.id.txt1);            this.txt2 = (TextView) v.findViewById(R.id.txt2);        }    }}

สังเกตดูว่าจะมีอยู่สองขั้นตอนด้วยกันคือ onCreateViewHolder และ onBindViewHolder สองอย่างนี้ทำหน้าที่ต่างกันคืออันแรกมีไว้สร้าง ViewHolder ส่วนอันหลังมีไว้ Assign ค่าต่างๆให้ถูกตำแหน่ง

ถ้าเป็น Pattern ของ ListView เดิม ทั้งสองนี้จะไปกองรวมกันอยู่ใน getView ให้เราเช็คและ Handle เอง แต่พอเป็น RecyclerView มันจัดการให้ทุกอย่าง

2) Recycle View อัตโนมัติ - และนี่เองเป็นที่มาของชื่อ RecyclerView เพราะมัน Recycle View ให้อัตโนมัติ ถ้าเป็น ListView เราจะต้องเช็ค convertView เอง แต่กับตัว RecyclerView มันจะเก็บ ViewHolder ที่ถูกสร้างไว้ พอ Scroll ถึงจุดที่ต้องใช้แล้วยังมี View เหลือเก็บไว้ ก็จะเอาสิ่งนั้นโยนให้ onBindViewHolder ไปยัดค่าทันที แต่ถ้าไม่มี View เหลือในสต็อคแล้ว จำเป็นต้องสร้างใหม่ ระบบก็จะเรียก onCreateViewHolder เพื่อสร้างขึ้นมาโดยอัตโนมัติ

สรุปคือเราไม่ต้องสนใจเรื่องการ Recycle View อะไรเลย มันจัดการให้หมด

ผลของคุณสมบัติสองอย่างนี้คือ ถ้าโปรแกรมไหนเรียกใช้ RecyclerView ก็มั่นใจได้ระดับหนึ่งว่าการทำงานของ List เข้าใกล้จุด Best Practice โดยที่ไม่รู้ตัว แต่จะ Scroll กระตุกมั้ยมันก็ไม่ใช่แค่เท่านี้แหละ มันขึ้นอยู่กับว่าเรา Bind ถูกวิธีรึเปล่าอีกด้วย โดยเฉพาะอย่างยิ่งการโหลดรูปมาแสดงใน List ต้องไปทำใน Background Thread ไม่งั้นยังไงก็กระตุก (ตรงนี้ Picasso ช่วยได้ ถ้าใช้ Picasso เป็นก็สบายไปอีกแสนเท่า)

แยก Component ออกมาอย่างอิสระต่อกัน

ไม่ได้จบแค่นั้น ListView เป็นการออกแบบที่ค่อนข้างเก่า แทบทุกอย่างต้องไปยัดใน Adapter ถ้าเราจะทำอะไรหวือหวาเช่น Animation เราต้องไปจัดการตรงกับ View หรือถ้าจะทำพวก Divider แปลกๆ ก็ต้องไปทำที่ View อีกเช่นกัน

ดังนั้น RecyclerView จึงแก้ปัญหาตรงนี้ด้วยการแยก Component ออกจากกัน และทำงานแยกกันไปเลย ดังต่อไปนี้

LayoutManager - เราสามารถกำหนดตำแหน่งของแต่ละ View ใน List ได้อย่างอิสระด้วย LayoutManager เบื้องต้นถ้าจะทำให้เป็น Vertical List หรือ Horizontal List ธรรมดา ระบบก็มี LinearLayoutManager มาให้ใช้เลย (อ่านเพิ่มด้านล่างนะสำหรับ LayoutManager)

ItemDecoration - เราสามารถ "วาด" อะไรบางอย่างรอบๆแต่ละ List Item ได้ ยกตัวอย่างเช่นสร้างกรอบให้กับ List Item หรือสร้าง Divider มาคั่นได้อย่างอิสระ เป็นอีกหนึ่งที่ ListView ทำไม่ได้ง่ายๆ แต่ RecyclerView ทำได้สบาย

ItemAnimator - สามารถสร้าง Animation เมื่อมีการเพิ่มหรือลบ List Item ทิ้ง จะไม่หายไปเฉยๆ แต่จะวึบบบเข้ามาไม่ก็วืบบบหายไป (Transition เป็นสิ่งสำคัญ) โดย Default จะเป็นการวืบแว้บแบบง่ายๆ แต่ถ้าอยากหวือหวา เราก็สามารถสร้างเองได้ตามต้องการ

และด้วยการที่มันแยกทุกอย่างออกมาอย่างอิสระก็คือ คนสามารถสร้าง Library เพื่อให้คนอื่นเอาไปใช้ได้ทันทีแบบง่ายๆ ประมาณบรรทัดเดียวก็ใช้ได้แล้ว ถ้าเป็น ListView ต้องตัดแปะๆ ลำบากมาก

        mRecyclerView = (RecyclerView) view.findViewById(android.R.id.list);        mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.HORIZONTAL_LIST));        mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));        mRecyclerView.setAdapter(mAdapter);        mRecyclerView.setItemAnimator(new DefaultItemAnimator());

ตอนนี้มีคนสร้างมาเพียบละ ยกตัวอย่างเช่น TwoWayView

LayoutManager สิทธิประโยชน์เหนือ ListView

ถามว่าอะไรที่เป็นข้อแตกต่างใหญ่สุดและสำคัญสุดของ RecyclerView?

ส่วนตัวเนี่ย ถ้าแค่เรื่อง ViewHolder ถือว่าเล็กน้อยมาก มันยังทำบน ListView ได้อยู่ ไม่มีคุณค่าจะเปลี่ยนไปใช้เลย แต่พอเจอ LayoutManager เท่านั้นแหละ ถือว่าเป็นแนวคิดที่เจ๋งและเป็น Killer ของ ListView เลย

ด้วยความสามารถของ LayoutManager เปิดให้เราสามารถกำหนดตำแหน่งของแต่ละ Item ได้อย่างอิสระ ทำให้สามารถทำอะไรแบบนี้ได้บนแอนดรอยด์อย่างง่ายๆแล้ว

ถ้าใครมีแอพฯที่ออกแบบ UI แบบนี้ ก็ลองไปใช้ TwoWayView ดูครับ มีออกมาค่อนข้างครบ (แต่เรายังใช้ไม่ได้เลย Sync Gradle ไม่รอด)

ตอนนี้มี LayoutManager แบบต่างๆมากมายออกมาให้ใช้ ลองไปหาโหลดหรือไม่ก็ลองเขียนเล่นดูสักตัวก็ได้ครับ =D

ถึงเวลาเปลี่ยนไปใช้ RecyclerView รึยัง?

เท่าที่ลองดูต้องถือว่า API ถือว่าสมบูรณ์ระดับหนึ่งแล้ว ใช้งานได้ค่อนข้างครบ ติดแค่เรื่องเดียวคือการ Handle List ที่มี View หลายแบบ ตัว Adapter ออกแบบมาเพื่อรองรับตรงนี้แล้วด้วยคำสั่ง getItemViewType เพื่อให้ตัว Adapter รู้ว่าต้อง Recycle ตำแหน่งไหนด้วย ViewHolder แบบใด แต่ตัว onCreateViewHolder ก็กลับ Return ViewHolder ได้แบบเดียว นั่นแปลว่าเราต้องกองทุกอย่างไว้ใน ViewHolder ตัวเดียว ซึ่งดูไม่ใช่วิธีที่ดีเท่าไหร่

ก็มีอีกวิธีคือสร้าง ViewHolder หลายแบบ แล้วสร้างตัวแม่ขึ้นมาตัวนึงแล้ว Cast ไป Cast มา แต่ก็รู้สึกว่ามันยังอ้อมอยู่ ข่าวดีคือ RecyclerView ยังคงพัฒนาอยู่ ยังไม่ Final ดังนั้นอาจจะเห็นตัวที่ดีกว่าตอนนี้ในอนาคตได้คร้าบผม

ดังนั้นถามว่าควรจะเปลี่ยนไปใช้รึยัง? คงตอบว่ามีฟีเจอร์อะไรใน RecyclerView ที่เราต้องใช้รึเปล่า (เช่น LayoutManager) ถ้ามีก็เปลี่ยนไปใช้ได้เลย แต่ถ้าไม่มีก็ใช้ ListView ต่อไปได้ แต่ต้องเข้าใจมันให้ดีเพื่อที่แอพฯจะได้ไม่พังจ้า


จบ Blog Geek มากไปอีกหนึ่ง ...

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

Aug 25, 2014, 00:50
6187 views
เก็บมาเล่า รายงาน Android Fragmentation ของ Open Signal vs ความเป็นจริงที่ต้อง Concern
Aug 9, 2014, 22:04
5673 views
บันทึกการทำแอพฯแบบ Material Design พร้อม Transition บน Android KitKat
0 Comment(s)
Loading