Triển khai chỉnh sửa văn bản trong RecyclerView Android

RecyclerView Android là một cách phổ biến để tạo danh sách động và đã phần nào thay thế ListView cổ điển. Tuy nhiên, giống như bất kỳ công cụ nào, nó có những ưu điểm và nhược điểm riêng.

RecyclerView tái sử dụng các view không hiển thị trên màn hình, điều này có thể gây ra vấn đề khi xử lý nội dung động. Hãy xem xét một ví dụ về RecyclerView chứa một danh sách các trường chỉnh sửa văn bản (EditText).

class EditTextAdapter(private val items: List<String>) :
    RecyclerView.Adapter<EditTextViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = EditTextViewHolder(
        LayoutInflater.from(parent.context).inflate(R.layout.item_edit_text, parent, false)
    )

    override fun onBindViewHolder(holder: EditTextViewHolder, position: Int) {
        // Chưa thực hiện gì ở đây
    }

    override fun getItemCount() = items.size
}

class EditTextViewHolder(view: View) : RecyclerView.ViewHolder(view)

Adapter như trên truyền một danh sách các mục rỗng và tạo ra RecyclerView bằng cách sử dụng EditTextViewHolder của chúng ta. Đầu ra của adapter này sẽ giống như sau:

Khi các trường đầu vào của người dùng ra khỏi màn hình, chúng sẽ bị tái sử dụng và RecyclerView không thể phân biệt giữa view rỗng và view đầu vào do dữ liệu động mà người dùng nhập vào. Kết quả là, khi người dùng cuộn xuống danh sách, các giá trị đầu vào lại được hiển thị liên tục.

Triển khai chỉnh sửa văn bản trong RecyclerView Android

Giải pháp 1

Một giải pháp là làm cho RecyclerView Android không thể tái sử dụng bằng cách sử dụng <ViewHolder>.setIsRecyclable(false). Điều này sẽ ngăn các view thay thế nhau và các giá trị đầu vào của người dùng sẽ được duy trì.

override fun onBindViewHolder(holder: EditTextViewHolder, position: Int) {
    holder.setIsRecyclable(false)
}

Mặc dù phương pháp này phù hợp với các danh sách nhỏ, nhưng không lý tưởng cho các danh sách lớn hoặc được phân trang, vì nó làm mất đi mục đích của RecyclerView Android.

Thay vào đó, tôi rất khuyến nghị giải pháp thứ hai, vì nó có thể mở rộng và hoạt động trong tất cả các tình huống.

Giải pháp 2

class EditTextAdapter(private val items: List<String>) :
    RecyclerView.Adapter<EditTextViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = EditTextViewHolder(
        LayoutInflater.from(parent.context).inflate(R.layout.item_edit_text, parent, false)
    )

    override fun onBindViewHolder(holder: EditTextViewHolder, position: Int) {
        // Chưa thực hiện gì ở đây
    }

    override fun getItemCount() = items.size

    override fun getItemViewType(position: Int) = position

    override fun getItemId(position: Int) = position.toLong()
}

class EditTextViewHolder(view: View) : RecyclerView.ViewHolder(view)

Như bạn có thể thấy, tôi đã ghi đè hai phương thức mới - getItemViewTypegetItemId.

Phương thức đầu tiên cung cấp một viewType duy nhất cho mỗi mục trong RecyclerView (giá trị mặc định là 0), trong khi phương thức thứ hai cung cấp một ID duy nhất (mặc định là RecyclerView.NO_ID, tức là -1).

Những phương thức này thông báo cho RecyclerView Android rằng một view cụ thể là duy nhất và sẽ được tái sử dụng trong khi vẫn giữ lại các giá trị động.

Trong trường hợp của chúng ta, chúng ta truyền vị trí vào cả hai phương thức vì nó được đảm bảo là duy nhất cho mỗi mục. Nếu bạn chạy mã của mình bây giờ, bạn sẽ thấy vấn đề đã được giải quyết.

Nếu bạn đã sử dụng getItemViewType (ví dụ: để hiển thị các bố cục khác nhau như danh sách trò chuyện), hãy đảm bảo rằng bạn gán một giá trị duy nhất cho mỗi phần tử. Ví dụ, sử dụng các bội số chẵn và lẻ của vị trí.

Bằng cách này, bạn có thể phân biệt giữa các loại view của mình trong khi vẫn gán một viewType khác nhau cho mỗi mục.

Phương pháp này không chỉ giới hạn ở trường chỉnh sửa văn bản - nó sẽ hoạt động với tất cả các đầu vào động như hộp kiểm, nút radio, thanh trượt, spinner, v.v.

Và đó là tất cả! Bạn có thể sử dụng trường chỉnh sửa văn bản hoặc bất kỳ mục động nào khác trong RecyclerView Android mà không phải lo lắng.