Ở bài trước chúng ta đã cài đặt thành công và hiển thị được dạng danh sách đơn giản. Trong bài này chúng ta sẽ đi thực hiện ý tưởng sau:
Lấy danh sách ở bài trước hiển thị lên, khi người dùng click vào row nào thì check vào row đó. Ý tưởng khá là đơn giản tuy nhiên trong thực tế có những bài toán phức tạp kết hợp nhiều điều kiện dẫn đến việc chúng ta nhầm lẫn và gây lỗi. Theo kinh nghiệm của mình thì những lúc như vậy cần tỉnh táo và nhớ lại những kiến thức basic nhất là giải quyết được.
Các thứ chúng ta cần làm:
- Thêm checkbox vào layout của từng row.
- Tạo 1 class thể hiện dữ liệu.
- Khởi tạo data đưa vào Adapter.
- Cấu hình lại ViewHolder.
- Cấu hình lại Adapter.
- Xử lí click từng row.
- Xử lí click vào checkbox từng row.
Về môi trường thì y hệt như ở bài 1 mình đang dùng, nếu bạn chưa đọc thì nên xem qua ở link này https://dotrinh.com/lap-trinh-voi-recyclerview-trong-android-bai-1/
Nào chúng ta bắt đầu thôi.
Thêm checkbox vào layout của từng row
Mở file layout/row_item.xml và thêm checkbox vào như sau:
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/cardView" android:layout_width="match_parent" android:layout_height="80dp" android:layout_margin="8dp" android:clickable="true" android:focusable="true" android:foreground="?android:attr/selectableItemBackground" app:cardCornerRadius="11dp"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_vertical" > <TextView android:id="@+id/rowItem" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_gravity="center_vertical" android:layout_marginStart="20dp" android:text="test.com" android:textColor="@android:color/holo_blue_dark" android:textSize="23sp" /> <CheckBox android:id="@+id/checkBox" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:text="" android:layout_marginEnd="10dp"/> </RelativeLayout> </android.support.v7.widget.CardView>
Tạo 1 class thể hiện dữ liệu
Ta sẽ tạo 1 class teen là Person.java để chứa thông tin của một người. Thuộc tính isCheck để đánh dấu xem row hiện tại đã được click hay chưa, thuộc tính này rất quan trọng vì nếu không có nó thì danh sách của chúng ta lúc scroll sẽ lộn xộn. Phải rất lưu ý cho mình ở chỗ này.
Tại hàm onBindViewHolder kiểm tra thuộc tính isCheck, chú ý chỉ kiểm tra và chỉ hiển thị không nên cập nhật dữ liệu gì ở hàm này.
package com.dotrinh.recyclerview.model; public class Person { int id; String name; boolean isCheck; public Person(int id, String name, boolean isCheck) { this.id = id; this.name = name; this.isCheck = isCheck; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isCheck() { return isCheck; } public void setCheck(boolean check) { isCheck = check; } }
Khởi tạo data đưa vào Adapter
Thêm vào MainActivity 1 field để chứa danh sách Person.
ArrayList<Person> people = new ArrayList<>();
Và thêm 1 func để khởi tạo data:
void initData() { for (int i = 0; i < 20; i++) { people.add(new Person(i, "Dotrinh", false)); } }
Tại hàm initData này thông thường ta lấy từ database hay từ server về, nhưng trong bài này mình sẽ tạo data giả như trên.
Cấu hình lại ViewHolder
Trong class ChildViewHolder ta cần ánh xạ đến cái checkbox vừa thêm vào:
CheckBox checkBox; ..... checkBox = itemView.findViewById(R.id.checkBox);
Cấu hình lại Adapter
Trong class inner này ta cần sửa lại:
Đếm lại cho đúng số lượng item có trong mảng trong hàm getItemCount như sau:
@Override public int getItemCount() { return people.size(); }
Tại hàm onBindViewHolder như đã nói bên trên:
@Override public void onBindViewHolder(@NonNull ChildAdapter.ChildViewHolder holder, int position) { // this method will call every time when we refresh recyclerview holder.rowItem.setText(people.get(position).getName()); holder.checkBox.setChecked(people.get(position).isCheck()); LogI("dong thu: " + position); }
Xử lí click từng row
Trong bài trước chúng ta đã xử lí sự kiện onclick rồi nhưng chỉ là Toast ra thôi, bây giờ ta sửa lại như sau:
@Override public void onClick(View v) { people.get(getAdapterPosition()).setCheck(!people.get(getAdapterPosition()).isCheck()); notifyDataSetChanged(); //Toast.makeText(MainActivity.this, "dong thu: " + getAdapterPosition(), Toast.LENGTH_SHORT).show(); }
Ở đây rất đơn giản, ý nghĩa của nó là:
getAdapterPosition: lấy được vị trí của row đang click, chú ý là vị trí đầu tiên là 0 chứ không phải 1.
people.get(getAdapterPosition()).setCheck(!people.get(getAdapterPosition()).isCheck()): là set giá trị phủ định của giá trị isCheck hiện tại.
notifyDataSetChanged: Sau khi cập nhật xong giá trị ta cần bảo RecyclerView refresh lại các row với dữ liệu mới.
Bây giờ bạn build sẽ chạy rồi nhưng để user click được cả vào row và cả checkbox thì cần làm thêm bước nữa.
Xử lí click vào checkbox từng row
Trong class ChildViewHolder implement thêm OnCheckedChangeListener:
class ChildViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, CheckBox.OnCheckedChangeListener { ... @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { people.get(getAdapterPosition()).setCheck(isChecked); }
Ở đây do lớp Android Framework đã vẽ lại trạng thái checkbox cho ta rồi nên ta không cần gọi notifyDataSetChanged nữa.
Full code của class MainActivity:
public class MainActivity extends AppCompatActivity { ArrayList<Person> people = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RecyclerView myRecycleView = findViewById(R.id.myRecycleView); myRecycleView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)); // myRecycleView.setLayoutManager(new GridLayoutManager(this, 2)); initData(); myRecycleView.setAdapter(new ChildAdapter()); } void initData() { for (int i = 0; i < 20; i++) { people.add(new Person(i, "Dotrinh", false)); } } public class ChildAdapter extends RecyclerView.Adapter<ChildAdapter.ChildViewHolder> { @Override public int getItemCount() { return people.size(); } public ChildAdapter.ChildViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_item, parent, false); return new ChildViewHolder(itemView); } @Override public void onBindViewHolder(@NonNull ChildAdapter.ChildViewHolder holder, int position) { // this method will call every time when we refresh recyclerview holder.rowItem.setText(people.get(position).getName() + " " + position); holder.checkBox.setChecked(people.get(position).isCheck()); LogI("dong thu: " + position); } // VIEW HOLDER class ChildViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, CheckBox.OnCheckedChangeListener { CardView card_view; TextView rowItem; CheckBox checkBox; ChildViewHolder(@NonNull View itemView) { super(itemView); card_view = itemView.findViewById(R.id.cardView); rowItem = itemView.findViewById(R.id.rowItem); checkBox = itemView.findViewById(R.id.checkBox); card_view.setOnClickListener(this); checkBox.setOnCheckedChangeListener(this); } @Override public void onClick(View v) { people.get(getAdapterPosition()).setCheck(!people.get(getAdapterPosition()).isCheck()); notifyDataSetChanged(); } @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { people.get(getAdapterPosition()).setCheck(isChecked); } } } }
Build lại lần nữa thì click vào row hay check vào checkbox đều giữ lại được trạng thái của row đó khi scroll rồi.
Mã nguồn toàn bộ series mình đặt ở github, hãy checkout ở đây https://github.com/dotrinhdev/Recyclerview
Các bài viết không xem thì tiếc:
- Lập trình với Recyclerview trong Android – Bài 3 | dotrinh.com
- Lập trình với Recyclerview trong Android – Bài 1 | dotrinh.com
- Chuyển một đối tượng sang Json trong Android
- Lập trình phóng to thu nhỏ ảnh pinch in – pinch out trong Android
- Truyền dữ liệu giữa 2 fragment trong android
- Tạo seekbar và kiến thức hữu ích về seekbar trong Android
- Phóng to thu nhỏ trong Android với ScaleGestureDetector
- Truyền dữ liệu giữa các Activity trong android
- Cách dùng AsyncTask trong Android
- Cách dùng Eventbus để truyền dữ liệu trong Android
- Ý nghĩa của clipToPadding trong Android
- Show Indicator trong Android | Hiển thị indicator trong Android
- Làm sao thiết kế nhiều màn hình trong Android
- Siêu tổng hợp android code snippets (cập nhật thường xuyên)
- Gửi dữ liệu đến BLESerial3 bằng Bluetooth LE Android
1 Comment