Danh sách có thể mở rộng bằng ListAdapter – Android

Tram Ho

Hôm nay mình muốn chia sẻ với các bạn một ví dụ về Expandable List sử dụng ListAdapter + Data Binding và multi-type trong RecyclerView.

Các bạn có thể tham khảo source code của mình tại đây!

Mình đang sử dụng Gradle 6.5Android Studio 4.1 nhé.

Ví dụ này sẽ hiển thị một danh sách các Section, khi chúng ta tap vào một Section nó sẽ mở rộng ra và hiển các Item của nó.

Dependencies

Đầu tiên chúng ta sẽ cần một vài dependencies trong build.gradle của module như sau:

Section Model

Chúng ta sẽ có một Section model chứa các thông tin:

Các bạn chú ý 2 thuộc tính sau nhé:

  • isExpandable: Xác định trạng thái có thể mở rộng. true nếu đang thu gọn, false nếu đang mở rộng.
  • items: Danh sách các item sẽ hiển thị khi list mở rộng.

Ở đây mình khuyến khích sử dụng data class để tiện cho việc compare trong DiffUtil sau này.

ViewHolder

Tiếp theo, chúng ta sẽ có 2 ViewHolder để hiển thị cho Section và các Item của nó:

SectionViewHolder

Mình sẽ tạo một layout là item_section.xml chứa một TextView để hiển thị name của Section và một AppCompatImageView để hiển thị expand icon, cái này thay đổi theo isExpandable trong Section model.

Mình có thêm một BindingAdaptersectionExpandable để cập nhật expand icon khi isExpandable thay đổi.

Để hiển thị các Item của Section, mình sẽ xây dựng thêm một ViewHodder khác là:

SectionItemViewHolder

Đối với Section Item mình chỉ cần một TextView để hiển thị name của Item đó.

ListAdapter

Bây giờ, chúng ta sẽ xử lý ListAdapter để tích hợp với các ViewHolder trên.

Lý do để mình sử dụng ListAdapter đó là nó tích hợp DiffUtil để compare sự khác nhau của list được submit. Các bạn có thể tham khảo thêm ở đây.

Đầu tiên, chúng ta sẽ cần cung cấp cho nó một DiffUtil.ItemCallback như sau:

Trong Adapter sẽ có 2 types, một cho Section và một cho Section Item.

Để có thể display 2 types này trong RecyclerView thì Adapter cần xác định chúng thông qua việc override getItemViewType(_: Int) : Int method.

Tiếp theo, chúng ta sẽ tạo ViewHolder tướng ứng với các ViewType đã định nghĩa và bind data cho chúng.

MainViewModel

Bây giờ, mình sẽ sử dụng ViewModel để xử lý dữ liệu cho Section cũng như cập nhật lại Section list khi có action expanding.

class MainViewModel sẽ như sau:

  • getSectionList(): Random một Section list
  • expand(_: Int): Cập nhật trạng thái khi một section expading.
  • refresh(): Refresh lại Section list.

Cuối cùng chúng ta sẽ apply tất cả chúng vào RecyclerView trong MainActivity.

MainActivity

activity_main.xml

Trong activity_main.xml layout, mình chỉ có một RecyclerViewSwipeRefreshLayout phục vụ cho việc refresh lại Section list.

  • app:onRefreshListener="@{() -> viewModel.refresh()}" nhận action refresh từ user và gọi refresh() method.
  • app:refreshing="@{viewModel.isRefreshing()}" hiển thị icon loading theo isRefreshing().
  • app:sectionList="@{viewModel.sectionListLiveData}" là một BindingAdapter mình định nghĩa trong MainActivity.Binding, để submit một list mới khi sectionListLiveData changed.

MainActivity

Trong BindingAdapter sectionList, mình sẽ check isExpandable để add hoặc không add section item.

Khi isExpandablefalse, nghĩa là đang mở rộng thì chúng ta sẽ add các Section Item vào. Và ngược lại, chúng ta chỉ add Section thôi.

Túm lại

Mình đã giới thiệu xong một example về Expandable List. Có thể đây không phải cách tối ưu nhất khi xử lý vấn đề này. Nhưng hi vọng nó sẽ giúp một chút gì cho các bạn.

Nếu có bất kì vấn đề nào hoặc góp ý cho bài viết, hãy comment dưới nhé…

Cám ơn các bạn đã đọc đến đây.

Thank you and Happy coding!!!

Source Code

Chia sẻ bài viết ngay

Nguồn bài viết : Viblo