Giới thiệu
RecyclerView là một ViewGroup mới được giới thiệu trong Android L (Android API 21). Đây là một ViewGroup có chức năng tương tự ListView nhưng nó tỏ ra mạnh mẽ, linh hoạt hơn rất nhiều. ListView chỉ hỗ trợ bạn scroll các item trong ListView theo chiều dọc (vertical) mà không hỗ trợ scroll theo chiều ngang (horizontal) . RecyclerView support được tất cả những thứ đó và hơn thế nữa. Trong bài viết này tôi sẽ giới thiệu cho các bạn về một ViewGroup này.
Trước khi đi vào tìm hiểu RecyclerView mình khuyên các bạn nên đọc lại 3 bài viết về ListView từ cơ bản tới nâng cao:
ListView Multiple Selection và ListView Single Selection trong Android
RecyclerView so với ListView
So với ListView thì RecyclerView có những điểm mạnh mẽ vượt trội hơn như sau:
+ Yêu cầu sử dụng ViewHolder Pattern trong Adapter: Với ListView chúng ta không cần sử dụng ViewHolder pattern để cải thiện performance của ListView nhưng ngược lại khi chúng ta tạo một Adapter sử dụng với RecyclerView bắt buộc phải sử dụng ViewHolder để cải thiện performance.
Mục đích sử dụng ViewHolder để tái sử dụng View nhằm tránh việc tạo View mới và findViewById quá nhiều
+ ListView chỉ support cho chúng ta danh sách dạng scroll dọc. Nhưng với RecylerView cung cấp cho chúng ta RecyclerView.LayoutManager cho phép Layout các item trong listView theo các kiểu khác nhau (ngang, dọc, dạng grid, dạng staggered grid).
+ Với ListView việc xử lý animation cho các item trong ListView không hề dễ dàng. Nhưng đối với RecyclerView có hổ trợ ItemAnimator giúp chúng ta có thể xử lý animation khi add hay remove một item ra khỏi Recycler một cách dễ dàng. Mặc định RecyclerView sử dụng DefaulItemAnimtor.
+ Với ListView việc sử dụng divider không được linh hoạt nhưng với RecylerView có hỗ trợ ItemDecoration cho phép chúng ta draw divider một cách tuỳ thích.
+ ListView có support các phương thức phương thức setOnItemClickListener và setOnLongItemListener để chọn 1 item trong ListView. Nhưng RecylerView chỉ support một phương thức đó là onItemTouchListener.
Các thành phần khi sử dụng RecyclerView
Khi sử dụng RecyclerView chúng ta phải làm việc với những thành phần sau đây:
RecyclerView.Adapter
Cũng giống như ListView thì đây là thành phần xử lý data collecion (dữ liệu kiểu danh sách) và bind (gắn) những dữ liệu này lên các Item của RecyclerView.
Khi tạo custom Adapter chúng ta phải override lại hai phương thức chính là:
onCreateViewHolder: Phương thức dùng để tạo view mới cho RecyclerView. Nếu RecyclerView đã cached lại View thì phương thức này sẽ không gọi.
onBindViewHolder: Phương thức này dùng để gắn data và view.
LayoutManager
Là thành phần có chức năng sắp xếp các item trong RecylerView. Các item scroll dọc hay ngang phụ thuộc chúng ta set LayoutManager này cho RecyclerView.
Các class con của LayoutManager:
LinenarLayoutManager: Hỗ trợ scroll các item theo chiều ngang hay chiều dọc.
GridLayoutManager: Layout các item trong RecyclerView dưới dạng Grid giống như khi chúng ta sử dụng GridView.
StaggerdGridLayoutManager: Layout cá item trong ListView dưới dạng Grid so le.
ItemAnimator: Là thành phần hỗ trợ animation khi chúng ta add hay remove một item ra khỏi RecyclerView. Tôi sẽ hướng dẫn chi tiết về ItemAnimator trong bài viết sau. Để tìm hiểu rõ phần này tôi gọi ý cho các bạn là nên tìm hiểu các class sau
ItemAnimator: Là class đại diện, khung sườn của animation trong RecyclerView.
SimpleItemAnimator: class wrapper lại ItemAnimator.
DefaultItemAnimtor: class xử lý animtion mặc định sử dụng trong RecyclerView.
Sử dụng RecyclerView trong Android
Mặc định RecyclerView không có sẵn trong Android SDK mà chúng ta phải import thư viện. Các import thư viện bạn chỉ cần paste dòng dưới đây vào build.gradle của module app sau đó nhấn Sync Now để Andorid Studio download và nạp thư viện tự động cho chúng ta.
1 |
compile 'com.android.support:recyclerview-v7:23.0.0' |
Hoặc có một cách khác là chúng ta vào File > Project Structure
Chọn module app và chuyển sang tab Dependencies. Sau đó nhấn vào dấu + vào chọn Library Dependencies
Tìm thư viện RecyclerView sau đó nhấn OK để import thư viện:
Sau khi đã import thư viện chúng ta bắt đầu tìm hiểu cách sử dụng RecyclerView nhé!
Các bước tôi sẽ làm như sau:
- Tạo model class để chứa data
- Thêm RecyclerView vào main_activity.xml
- Tạo giao diện cho một row
- Tạo Custom Adapter và gán dữ liệu cho từng row trong Adapter
- Setup RecyclerView trong MainActivity.java
Bước 1: Tạo model để chứa data
Class Song đại diện cho 1 bài hát gồm các thông tin:
- mCode: Mã số bài hát
- mTitle: Tên bài hát
- mLyric: Lyric của bài hát.
- mAstist: Tên ca sĩ
File Song.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
package com.eitguide.nguyennghia.recyclerviewandroid; /** * Created by nguyennghia on 8/25/16. */ public class Song { private String mCode; private String mTitle; private String mLyric; private String mArtist; public Song() { } public Song(String code, String title, String lyric, String artist) { this.mCode = code; this.mTitle = title; this.mLyric = lyric; this.mArtist = artist; } public String getCode() { return mCode; } public String getTitle() { return mTitle; } public String getLyric() { return mLyric; } public String getArtist() { return mArtist; } public void setCode(String code) { this.mCode = code; } public void setTitle(String title) { this.mTitle = title; } public void setLyric(String lyric) { this.mLyric = lyric; } public void setArtist(String artist) { this.mArtist = artist; } } |
Bước 2: Thêm RecyclerView vào main_activity.xml
File main_activity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.eitguide.nguyennghia.recyclerviewandroid.MainActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/rv_songs" android:layout_width="match_parent" android:layout_height="match_parent"/> </RelativeLayout> |
Bước 3: Tạo giao diện cho một row
File row_item_song.xml trong thư mục layout của project được thiết kể để hiển thị row như hình dưới đây:
Và mã nguồn của file này:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingBottom="3dp" android:paddingLeft="6dp" android:paddingRight="6dp" android:paddingTop="3dp"> <FrameLayout android:id="@+id/fl_code" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_centerVertical="true"> <TextView android:id="@+id/tv_code" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#19b395" android:textSize="18dp" /> </FrameLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="8dp" android:layout_toRightOf="@+id/fl_code" android:orientation="vertical"> <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:ellipsize="end" android:textColor="#2c3e50" android:textSize="16dp" android:textStyle="bold" /> <TextView android:id="@+id/tv_lyric" android:layout_width="wrap_content" android:layout_height="wrap_content" android:ellipsize="end" android:textColor="#34495e" android:textSize="14dp" /> <TextView android:id="@+id/tv_artist" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#7f8c8d" android:textSize="14dp" /> </LinearLayout> </RelativeLayout> |
Bước 4: Tạo Custom Adapter và bind dữ liệu cho từng row trong Adapter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
package com.eitguide.nguyennghia.recyclerviewandroid; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import java.util.List; /** * Created by nguyennghia on 8/25/16. */ public class SongAdapter extends RecyclerView.Adapter<SongAdapter.SongViewHolder> { private static final String TAG = "SongAdapter"; private List<Song> mSongs; private Context mContext; private LayoutInflater mLayoutInflater; public SongAdapter(Context context, List<Song> datas) { mContext = context; mSongs = datas; mLayoutInflater = LayoutInflater.from(context); } @Override public SongViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { //inflate view from row_item_song.xml View itemView = mLayoutInflater.inflate(R.layout.row_item_song, parent, false); return new SongViewHolder(itemView); } @Override public void onBindViewHolder(SongViewHolder holder, int position) { //get song in mSong via position Song song = mSongs.get(position); //bind data to viewholder holder.tvCode.setText(song.getCode()); holder.tvTitle.setText(song.getTitle()); holder.tvLyric.setText(song.getLyric()); holder.tvArtist.setText(song.getArtist()); } @Override public int getItemCount() { return mSongs.size(); } class SongViewHolder extends RecyclerView.ViewHolder { private TextView tvCode; private TextView tvTitle; private TextView tvLyric; private TextView tvArtist; public SongViewHolder(View itemView) { super(itemView); tvCode = (TextView) itemView.findViewById(R.id.tv_code); tvTitle = (TextView) itemView.findViewById(R.id.tv_title); tvLyric = (TextView) itemView.findViewById(R.id.tv_lyric); tvArtist = (TextView) itemView.findViewById(R.id.tv_artist); } } } |
Chúng ta thấy rằng tạo tạo Custom Adapter trong RecyclerView thực sự dễ dàng hơn so với ListView. Chỉ cần tạo một class kế thừa RecyclerView.ViewHolder và việc còn chị chỉ cần xử lý trong onCreateViewHolder để tạo mới ViewHolder và onBindViewHolder để gán data lấy từ collection gán vào ViewHolder.
Bước 5: Setup RecyclerView trong MainActivity.java
Không giống như ListView chúng ta chỉ cần set Adapter cho ListView là đủ nhưng đối với RecyclerView chúng ta phải set 2 thành phần bắt buộc dưới đây:
+ set adapter cho RecyclerView sử dụng phương thức setAdapter
+ set layout manager để chỉ ra chúng ta muốn layout các item như thế nào (vertical, horizotal, grid, staggered grid) bằng cách sử dụng phương thức setLayoutManager.
Source file MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
package com.eitguide.nguyennghia.recyclerviewandroid; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { private RecyclerView rvSongs; private SongAdapter mSongAdapter; private List<Song> mSongs; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); rvSongs = (RecyclerView)findViewById(R.id.rv_songs); //create song data mSongs = new ArrayList<>(); mSongs = new ArrayList<>(); mSongs.add(new Song("60696", "NẾU EM CÒN TỒN TẠI", "Khi anh bắt đầu một tình yêu Là lúc anh tự thay", "Trịnh Đình Quang")); mSongs.add(new Song("60701", "NGỐC", "Có rất nhiều những câu chuyện Em dấu riêng mình em biết", "Khắc Việt")); mSongs.add(new Song("60650", "HÃY TIN ANH LẦN NỮA", "Dẫu cho ta đã sai khi ở bên nhau Cô yêu thương", "Thiên Dũng")); mSongs.add(new Song("60610", "CHUỖI NGÀY VẮNG EM", "Từ khi em bước ra đi cõi lòng anh ngập tràng bao", "Duy Cường")); mSongs.add(new Song("60656", "KHI NGƯỜI MÌNH YÊU KHÓC", "Nước mắt em đang rơi trên những ngón tay Nước mắt em", "Phạm Mạnh Quỳnh")); mSongs.add(new Song("60685", "MỞ", "Anh mơ gặp em anh mơ được ôm anh mơ được gần", "Trịnh Thăng Bình")); mSongs.add(new Song("60752", "TÌNH YÊU CHẮP VÁ", "Muốn đi xa nơi yêu thương mình từng có Để không nghe", "Mr. Siro")); mSongs.add(new Song("60608", "CHỜ NGÀY MƯA TAN", "Một ngày mưa và em khuất xa nơi anh bóng dáng cứ", "Trung Đức")); mSongs.add(new Song("60603", "CÂU HỎI EM CHƯA TRẢ LỜI", "Cần nơi em một lời giải thích thật lòng Đừng lặng im", "Yuki Huy Nam")); mSongs.add(new Song("60720", "QUA ĐI LẶNG LẼ", "Đôi khi đến với nhau yêu thương chẳng được lâu nhưng khi", "Phan Mạnh Quỳnh")); mSongs.add(new Song("60856", "QUÊN ANH LÀ ĐIỀU EM KHÔNG THỂ - REMIX", "Cần thêm bao lâu để em quên đi niềm đâu Cần thêm", "Thiện Ngôn")); mSongAdapter = new SongAdapter(this, mSongs); rvSongs.setAdapter(mSongAdapter); RecyclerView scroll vertical LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); rvSongs.setLayoutManager(linearLayoutManager); } } |
Các bạn chú ý đến đoạn mã
1 |
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); |
Vì tôi muốn RecyclerView của tôi cuộn theo chiều dọc nên tôi sử dụng LinearLayoutManager và truyền vào constructor là VERTICAL.
Nếu bạn muốn cuộn theo chiều ngang thì có thể set là
1 |
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false); |
Các bạn có thể sử dụng những class sau để setLayoutManager cho RecyclerView.
GridLayoutManager: Layout các item trong RecyclerView theo dạng grid.
StaggeredLayoutManager: Layout các item trong RecyclerView theo dạng grid so le nhau.
Select item trong RecyclerView
Chúng ta có thể select item trong RecyclerView bằng cách setOnClickListenner cho itemView trong Constructor của ViewHolder như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class SongViewHolder extends RecyclerView.ViewHolder { private TextView tvCode; private TextView tvTitle; private TextView tvLyric; private TextView tvArtist; public SongViewHolder(View itemView) { super(itemView); tvCode = (TextView) itemView.findViewById(R.id.tv_code); tvTitle = (TextView) itemView.findViewById(R.id.tv_title); tvLyric = (TextView) itemView.findViewById(R.id.tv_lyric); tvArtist = (TextView) itemView.findViewById(R.id.tv_artist); itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Song song = mSongs.get(getAdapterPosition()); Toast.makeText(mContext, song.getTitle(), Toast.LENGTH_SHORT).show(); } }); } } |
Và dưới đây là video demo:
Source code sử dụng trong bài viết: RecyclerViewAndroid
hay, bạn giới thiệu thêm kiểu nâng cao recycle trong recycle đi 🙂
Kiểu nâng cao trong RecyclerView là như nào vậy bạn?
chắc mình dùng từ cũng không đúng lắm , ý mình là 1 cái layout có RecyclerView trong RecyclerView
expandable recycle view đi bạn.
Mình có viết một bài về ExpandableListView trong Android rồi bạn. Về phần Expandable RecyclerView mình sẽ không viết nữa.
Nếu như muốn khi chọn vào một item đồng thời một item trong đó cũng thay đổi thì mình làm sao bạn?
Nếu như có thay đổi về mặt data thì bạn phải gọi phương thưc notifyDatasetChange để cập nhật lại UI.
Mình xài Gridlayout, các ô của mình màu trắng, mình chọn bất kỳ một ô thì sẽ chuyển sang màu xanh sau đó mình chọn ô mới thì ô đó chuyển sang màu xanh, ô ban đầu quay về màu trắng. Mình biết cách lấy vị trí của từng ô nhưng chưa biết cách để làm được điều ở trên bạn :((, bạn có thể giúp mình không?
Vấn đề trên bạn hoàn toàn có thể giải quyết bằng cách như sau: Khi click vào 1 item thì set background màu xanh cho item đó và reset các item còn lại.
Hướng dẫn làm wrappanel đi bạn. Như hình này nè: http://imageshack.com/a/img924/937/19RbiN.jpg
Ad giải đáp giúp mình nhé. Như cái listview thì có cái để kiểm tra convertview đã tạo hay chưa. Nhưng cái này mình không hiểu cách nó kiểm tra item trên RecyclerView đã tạo chưa bằng cách nào?
RecyclerView luôn bắt buộc có class phụ VH extends ViewHolder rồi, và bản thân nó sẽ tự recycle lại nên việc kiểm tra if (convertView == null) trong getView(…) được tự nó làm, bạn không cần phải tự kiểm tra.
oh ra vậy, bạn cho mình hỏi thế lần đầu tiên tạo thì RecyclerView nó sẽ tạo hết các phần tử trong danh sách hay chỉ tạo những phần tử mình có thể nhìn được thôi?
Mặc định, nếu bạn không xác định lượng cache thì nó sẽ căn cứ vào bộ nhớ của hệ thống cấp cho ứng dụng mà sẽ tính toán, tạo cache số lượng các items cho phù hợp. Nhưng bao giờ thì lượng items đó cũng lớn hơn hoặc bằng số phần tử bạn thấy. Tuy nhiên bạn vẫn yêu cầu nó tạo số lượng cached items theo ý bạn vẫn được. Vd: Trong ArrayList nguồn có 200 elements, nhưng trên màn hình có 10 items thấy được, thì số cached items mặc định ít nhất là 10, nhưng bạn vẫn có thể yêu cầu nó định lượng số items này là 11, 20, 35 hay 200.
Mình hiểu cách làm việc của nó như này, nếu có sai bạn sử giúp mình nhé. Trong t.h màn hình chỉ chiứa được 10 item Adapter sẽ tạo 1 số lượng view nhất định (ít nhất có thể) để tiết kiệm bộ nhớ. Sau đấy RecyclerView yêu cầu item nào, adapter sẽ bind data lên các view đã có. Còn về cơ chế tái sd, ví dụ khi ta scroll tới item nào thì nó sẽ lấy lại cái view của item bị remove rồi setData lại đúng không bạn? Sr nếu mình nói không thoát ý.
Bạn đã hiểu đúng rồi đó.
Thanks Bảo Huy nhiều nhé
Always welcome. Chúc bạn sẽ tìm được những kiến thức bạn đang cần trên EITGUIDE.
Việc k sử dụng static cho class songViewHolder có lẽ sẽ làm giảm hiệu suất của ứng dụng khi có một lượng dữ liệu lớn?!
Rất có tâm.
Cám ơn thím!
số lượng phần tử tối đa của 1 recycle view là bao nhiêu vậy ad ?
Không giới hạn.