Lập trình với Recyclerview trong Android – Bài 2

Lập trình với Recyclerview trong Android – Bài 2
5 (100%) 1 Hài lòng

Ở 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:

Chia sẻ là sexy

Có “Lập trình với Recyclerview trong Android – Bài 2”

Thảo luận

This site uses Akismet to reduce spam. Learn how your comment data is processed.