Giả sử nếu bạn được đưa cho một giao diện người dùng như sau và bạn được yêu cầu để thiết kế nó, suy nghĩ đầu tiên sẽ xuất hiện trong đầu bạn là gì?
Bạn có thể đưa ra một loạt các ý tưởng khác nhau để thực hiện nó. Nhưng ý tưởng phổ biến nhất sẽ là sử dụng RecyclerView với nhiều kiểu view type trong một RecyclerView
.
Nhưng với giải pháp được đưa ra ở trên, vấn đề nảy sinh chính là việc quản lý nhiều view type khá là phức tạp. Trong ví dụ trên, chúng ta chỉ có ba loại view type. Nhưng trong thực tế, bạn có thể sẽ phải quản lý rất nhiều view type mà không chỉ là ba.
Vậy làm thế nào chúng ta có thể thiết kế một view một cách dễ dàng mà không phải quản lý các view type khác nhau?
MergeAdapter chính là là giải pháp cho vấn đề này.
Trong bài viết này, chúng ta sẽ sử dụng MergeAdapter
để xây dựng màn hình trên.
Bước 01
Trước tiên, chúng ta hãy thêm dependency
của merge adapter (nó là một phần của recyclerView) vào trong build.gradle
của ứng dụng:
1 2 | implementation <span class="token string">"androidx.recyclerview:recyclerview:1.2.0-alpha02"</span> |
Bước 02
Đối với màn hình ở trên, chúng ta có ba loại View khác nhau. Vì vậy, chúng ta cần tạo ra ba data class
khác nhau – User, MyDetail, Banner.
1 2 3 4 5 6 | data <span class="token keyword">class</span> <span class="token class-name">User</span><span class="token punctuation">(</span> val id<span class="token operator">:</span> Int <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> val name<span class="token operator">:</span> String <span class="token operator">=</span> <span class="token string">""</span><span class="token punctuation">,</span> val avatar<span class="token operator">:</span> String <span class="token operator">=</span> <span class="token string">""</span> <span class="token punctuation">)</span> |
1 2 3 4 5 6 | data <span class="token keyword">class</span> <span class="token class-name">MyDetail</span><span class="token punctuation">(</span> val id<span class="token operator">:</span> Int <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> val name<span class="token operator">:</span> String <span class="token operator">=</span> <span class="token string">""</span><span class="token punctuation">,</span> val aboutMe<span class="token operator">:</span> String <span class="token operator">=</span> <span class="token string">""</span> <span class="token punctuation">)</span> |
1 2 3 4 | data <span class="token keyword">class</span> <span class="token class-name">Banner</span><span class="token punctuation">(</span> val bannerImage<span class="token operator">:</span> Int <span class="token operator">=</span> <span class="token number">0</span> <span class="token punctuation">)</span> |
Bước 03
Trong phần này, chúng ta sẽ tạo nguồn dữ liệu cho danh sách người dùng để hiển thị danh sách. Vì thế, chúng ta sẽ tạo một đối tượng của DataSource và trong đó:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | object DataSource <span class="token punctuation">{</span> fun <span class="token function">getUser</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span> ArrayList<span class="token generics function"><span class="token punctuation"><</span>User<span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>apply <span class="token punctuation">{</span> <span class="token function">add</span><span class="token punctuation">(</span><span class="token function">User</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token string">"Himanshu"</span><span class="token punctuation">,</span> <span class="token string">"https://s3.amazonaws.com/uifaces/faces/twitter/sunlandictwin/128.jpg"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token function">add</span><span class="token punctuation">(</span><span class="token function">User</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token string">"Alford Hoeger"</span><span class="token punctuation">,</span> <span class="token string">"https://s3.amazonaws.com/uifaces/faces/twitter/mufaddal_mw/128.jpg"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token function">add</span><span class="token punctuation">(</span><span class="token function">User</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">,</span> <span class="token string">"Terrance Halvorson"</span><span class="token punctuation">,</span> <span class="token string">"https://s3.amazonaws.com/uifaces/faces/twitter/mufaddal_mw/128.jpg"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token function">add</span><span class="token punctuation">(</span><span class="token function">User</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">,</span> <span class="token string">"Morgan McGlynn"</span><span class="token punctuation">,</span> <span class="token string">"https://s3.amazonaws.com/uifaces/faces/twitter/allfordesign/128.jpg"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token function">add</span><span class="token punctuation">(</span><span class="token function">User</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">,</span> <span class="token string">"Ms. Ramiro DuBuque"</span><span class="token punctuation">,</span> <span class="token string">"https://s3.amazonaws.com/uifaces/faces/twitter/shaneIxD/128.jpg"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token function">add</span><span class="token punctuation">(</span><span class="token function">User</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">,</span> <span class="token string">"Kolby Orn"</span><span class="token punctuation">,</span> <span class="token string">"https://s3.amazonaws.com/uifaces/faces/twitter/johnsmithagency/128.jpg"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token function">add</span><span class="token punctuation">(</span><span class="token function">User</span><span class="token punctuation">(</span><span class="token number">6</span><span class="token punctuation">,</span> <span class="token string">"Elijah Schoen MD"</span><span class="token punctuation">,</span> <span class="token string">"https://s3.amazonaws.com/uifaces/faces/twitter/alxndrustinov/128.jpg"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> fun <span class="token function">getBanner</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">Banner</span><span class="token punctuation">(</span>R<span class="token punctuation">.</span>drawable<span class="token punctuation">.</span>publication_site_cover<span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Trong đó getUser()
sẽ được sử dụng để lấy danh sách người dùng và getBanner()
được sử dụng để lấy hình ảnh banner từ thư mục drawable
.
Bước 04
Ở bước này, ta sẽ tạo ra 3 adapter cho 3 kiểu View cần thiết.
Để hiển thị MyDetails, ta sẽ tạo MyDetailAdapter
:
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 | <span class="token keyword">class</span> <span class="token class-name">MyDetailAdapter</span><span class="token punctuation">(</span> <span class="token keyword">private</span> val myDetail<span class="token operator">:</span> MyDetail <span class="token punctuation">)</span> <span class="token operator">:</span> RecyclerView<span class="token punctuation">.</span>Adapter<span class="token generics function"><span class="token punctuation"><</span>MyDetailAdapter<span class="token punctuation">.</span>DataViewHolder<span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">class</span> <span class="token class-name">DataViewHolder</span><span class="token punctuation">(</span>itemView<span class="token operator">:</span> View<span class="token punctuation">)</span> <span class="token operator">:</span> RecyclerView<span class="token punctuation">.</span><span class="token function">ViewHolder</span><span class="token punctuation">(</span>itemView<span class="token punctuation">)</span> <span class="token punctuation">{</span> fun <span class="token function">bind</span><span class="token punctuation">(</span>user<span class="token operator">:</span> MyDetail<span class="token punctuation">)</span> <span class="token punctuation">{</span> itemView<span class="token punctuation">.</span>textViewUser<span class="token punctuation">.</span>text <span class="token operator">=</span> user<span class="token punctuation">.</span>name itemView<span class="token punctuation">.</span>textViewAboutMe<span class="token punctuation">.</span>text <span class="token operator">=</span> user<span class="token punctuation">.</span>aboutMe <span class="token punctuation">}</span> <span class="token punctuation">}</span> override fun <span class="token function">onCreateViewHolder</span><span class="token punctuation">(</span>parent<span class="token operator">:</span> ViewGroup<span class="token punctuation">,</span> viewType<span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">DataViewHolder</span><span class="token punctuation">(</span> LayoutInflater<span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span>parent<span class="token punctuation">.</span>context<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">inflate</span><span class="token punctuation">(</span> R<span class="token punctuation">.</span>layout<span class="token punctuation">.</span>item_layout_my_detail<span class="token punctuation">,</span> parent<span class="token punctuation">,</span> <span class="token boolean">false</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> override fun <span class="token function">getItemCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> Int <span class="token operator">=</span> <span class="token number">1</span> override fun <span class="token function">onBindViewHolder</span><span class="token punctuation">(</span>holder<span class="token operator">:</span> DataViewHolder<span class="token punctuation">,</span> position<span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token operator">=</span> holder<span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>myDetail<span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Adapter này sẽ được sử dụng để hiển thị MyUserDetails, về cơ bản là phần đầu tiên của danh sách. Ở đây, R.layout.item_layout_my_detail
sẽ như sau:
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 | <span class="token operator"><</span><span class="token operator">?</span>xml version<span class="token operator">=</span><span class="token string">"1.0"</span> encoding<span class="token operator">=</span><span class="token string">"utf-8"</span><span class="token operator">?</span><span class="token operator">></span> <span class="token operator"><</span>LinearLayout xmlns<span class="token operator">:</span>android<span class="token operator">=</span><span class="token string">"http://schemas.android.com/apk/res/android"</span> xmlns<span class="token operator">:</span>app<span class="token operator">=</span><span class="token string">"http://schemas.android.com/apk/res-auto"</span> xmlns<span class="token operator">:</span>tools<span class="token operator">=</span><span class="token string">"http://schemas.android.com/tools"</span> android<span class="token operator">:</span>id<span class="token operator">=</span><span class="token string">"@+id/container"</span> android<span class="token operator">:</span>layout_width<span class="token operator">=</span><span class="token string">"match_parent"</span> android<span class="token operator">:</span>layout_height<span class="token operator">=</span><span class="token string">"wrap_content"</span> android<span class="token operator">:</span>background<span class="token operator">=</span><span class="token string">"@android:color/black"</span> android<span class="token operator">:</span>orientation<span class="token operator">=</span><span class="token string">"vertical"</span> android<span class="token operator">:</span>paddingStart<span class="token operator">=</span><span class="token string">"16dp"</span> android<span class="token operator">:</span>paddingTop<span class="token operator">=</span><span class="token string">"16dp"</span> android<span class="token operator">:</span>paddingEnd<span class="token operator">=</span><span class="token string">"16dp"</span> android<span class="token operator">:</span>paddingBottom<span class="token operator">=</span><span class="token string">"16dp"</span><span class="token operator">></span> <span class="token operator"><</span>androidx<span class="token punctuation">.</span>appcompat<span class="token punctuation">.</span>widget<span class="token punctuation">.</span>AppCompatTextView android<span class="token operator">:</span>id<span class="token operator">=</span><span class="token string">"@+id/textViewUser"</span> style<span class="token operator">=</span><span class="token string">"@style/TextAppearance.AppCompat.Large"</span> android<span class="token operator">:</span>layout_width<span class="token operator">=</span><span class="token string">"match_parent"</span> android<span class="token operator">:</span>layout_height<span class="token operator">=</span><span class="token string">"wrap_content"</span> android<span class="token operator">:</span>textColor<span class="token operator">=</span><span class="token string">"@android:color/white"</span> tools<span class="token operator">:</span>text<span class="token operator">=</span><span class="token string">"MindOrks"</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span>androidx<span class="token punctuation">.</span>appcompat<span class="token punctuation">.</span>widget<span class="token punctuation">.</span>AppCompatTextView android<span class="token operator">:</span>id<span class="token operator">=</span><span class="token string">"@+id/textViewAboutMe"</span> style<span class="token operator">=</span><span class="token string">"@style/TextAppearance.AppCompat.Small"</span> android<span class="token operator">:</span>layout_width<span class="token operator">=</span><span class="token string">"match_parent"</span> android<span class="token operator">:</span>layout_height<span class="token operator">=</span><span class="token string">"wrap_content"</span> android<span class="token operator">:</span>textColor<span class="token operator">=</span><span class="token string">"@android:color/darker_gray"</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>LinearLayout<span class="token operator">></span> |
Sau đó, để hiển thị danh sách người dùng, chúng ta sẽ tạo ra một adapter khác ods là UsersAdapter:
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 | <span class="token keyword">class</span> <span class="token class-name">UsersAdapter</span><span class="token punctuation">(</span> <span class="token keyword">private</span> val users<span class="token operator">:</span> ArrayList<span class="token generics function"><span class="token punctuation"><</span>User<span class="token punctuation">></span></span> <span class="token punctuation">)</span> <span class="token operator">:</span> RecyclerView<span class="token punctuation">.</span>Adapter<span class="token generics function"><span class="token punctuation"><</span>UsersAdapter<span class="token punctuation">.</span>DataViewHolder<span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">class</span> <span class="token class-name">DataViewHolder</span><span class="token punctuation">(</span>itemView<span class="token operator">:</span> View<span class="token punctuation">)</span> <span class="token operator">:</span> RecyclerView<span class="token punctuation">.</span><span class="token function">ViewHolder</span><span class="token punctuation">(</span>itemView<span class="token punctuation">)</span> <span class="token punctuation">{</span> fun <span class="token function">bind</span><span class="token punctuation">(</span>user<span class="token operator">:</span> User<span class="token punctuation">)</span> <span class="token punctuation">{</span> itemView<span class="token punctuation">.</span>textViewUserName<span class="token punctuation">.</span>text <span class="token operator">=</span> user<span class="token punctuation">.</span>name Glide<span class="token punctuation">.</span><span class="token function">with</span><span class="token punctuation">(</span>itemView<span class="token punctuation">.</span>imageViewAvatar<span class="token punctuation">.</span>context<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">load</span><span class="token punctuation">(</span>user<span class="token punctuation">.</span>avatar<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">into</span><span class="token punctuation">(</span>itemView<span class="token punctuation">.</span>imageViewAvatar<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> override fun <span class="token function">onCreateViewHolder</span><span class="token punctuation">(</span>parent<span class="token operator">:</span> ViewGroup<span class="token punctuation">,</span> viewType<span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">DataViewHolder</span><span class="token punctuation">(</span> LayoutInflater<span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span>parent<span class="token punctuation">.</span>context<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">inflate</span><span class="token punctuation">(</span> R<span class="token punctuation">.</span>layout<span class="token punctuation">.</span>item_layout<span class="token punctuation">,</span> parent<span class="token punctuation">,</span> <span class="token boolean">false</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> override fun <span class="token function">getItemCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> Int <span class="token operator">=</span> users<span class="token punctuation">.</span>size override fun <span class="token function">onBindViewHolder</span><span class="token punctuation">(</span>holder<span class="token operator">:</span> DataViewHolder<span class="token punctuation">,</span> position<span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token operator">=</span> holder<span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>users<span class="token punctuation">[</span>position<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Adapter này sẽ được sử dụng để hiển thị danh sách người dùng, trong đóR.layout.item_layout
sẽ như sau:
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 | <span class="token operator"><</span>androidx<span class="token punctuation">.</span>constraintlayout<span class="token punctuation">.</span>widget<span class="token punctuation">.</span>ConstraintLayout xmlns<span class="token operator">:</span>android<span class="token operator">=</span><span class="token string">"http://schemas.android.com/apk/res/android"</span> xmlns<span class="token operator">:</span>app<span class="token operator">=</span><span class="token string">"http://schemas.android.com/apk/res-auto"</span> xmlns<span class="token operator">:</span>tools<span class="token operator">=</span><span class="token string">"http://schemas.android.com/tools"</span> android<span class="token operator">:</span>id<span class="token operator">=</span><span class="token string">"@+id/container"</span> android<span class="token operator">:</span>layout_width<span class="token operator">=</span><span class="token string">"match_parent"</span> android<span class="token operator">:</span>layout_height<span class="token operator">=</span><span class="token string">"60dp"</span><span class="token operator">></span> <span class="token operator"><</span>de<span class="token punctuation">.</span>hdodenhof<span class="token punctuation">.</span>circleimageview<span class="token punctuation">.</span>CircleImageView android<span class="token operator">:</span>id<span class="token operator">=</span><span class="token string">"@+id/imageViewAvatar"</span> android<span class="token operator">:</span>layout_width<span class="token operator">=</span><span class="token string">"60dp"</span> android<span class="token operator">:</span>layout_height<span class="token operator">=</span><span class="token string">"0dp"</span> android<span class="token operator">:</span>padding<span class="token operator">=</span><span class="token string">"4dp"</span> app<span class="token operator">:</span>civ_border_color<span class="token operator">=</span><span class="token string">"@android:color/black"</span> app<span class="token operator">:</span>civ_border_width<span class="token operator">=</span><span class="token string">"2dp"</span> app<span class="token operator">:</span>layout_constraintBottom_toBottomOf<span class="token operator">=</span><span class="token string">"parent"</span> app<span class="token operator">:</span>layout_constraintStart_toStartOf<span class="token operator">=</span><span class="token string">"parent"</span> app<span class="token operator">:</span>layout_constraintTop_toTopOf<span class="token operator">=</span><span class="token string">"parent"</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span>androidx<span class="token punctuation">.</span>appcompat<span class="token punctuation">.</span>widget<span class="token punctuation">.</span>AppCompatTextView android<span class="token operator">:</span>id<span class="token operator">=</span><span class="token string">"@+id/textViewUserName"</span> style<span class="token operator">=</span><span class="token string">"@style/TextAppearance.AppCompat.Large"</span> android<span class="token operator">:</span>layout_width<span class="token operator">=</span><span class="token string">"0dp"</span> android<span class="token operator">:</span>layout_height<span class="token operator">=</span><span class="token string">"wrap_content"</span> android<span class="token operator">:</span>layout_marginStart<span class="token operator">=</span><span class="token string">"8dp"</span> android<span class="token operator">:</span>layout_marginLeft<span class="token operator">=</span><span class="token string">"8dp"</span> android<span class="token operator">:</span>layout_marginTop<span class="token operator">=</span><span class="token string">"4dp"</span> app<span class="token operator">:</span>layout_constraintEnd_toEndOf<span class="token operator">=</span><span class="token string">"parent"</span> app<span class="token operator">:</span>layout_constraintStart_toEndOf<span class="token operator">=</span><span class="token string">"@+id/imageViewAvatar"</span> app<span class="token operator">:</span>layout_constraintTop_toTopOf<span class="token operator">=</span><span class="token string">"parent"</span> app<span class="token operator">:</span>layout_constraintBottom_toBottomOf<span class="token operator">=</span><span class="token string">"parent"</span> tools<span class="token operator">:</span>text<span class="token operator">=</span><span class="token string">"MindOrks"</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>androidx<span class="token punctuation">.</span>constraintlayout<span class="token punctuation">.</span>widget<span class="token punctuation">.</span>ConstraintLayout<span class="token operator">></span> |
và cuối cùng, ta sẽ tạo một adapter để hiển thị banner đó là BannerAdapter:
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 | <span class="token keyword">class</span> <span class="token class-name">BannerAdapter</span><span class="token punctuation">(</span> <span class="token keyword">private</span> val banner<span class="token operator">:</span> Banner <span class="token punctuation">)</span> <span class="token operator">:</span> RecyclerView<span class="token punctuation">.</span>Adapter<span class="token generics function"><span class="token punctuation"><</span>BannerAdapter<span class="token punctuation">.</span>DataViewHolder<span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">class</span> <span class="token class-name">DataViewHolder</span><span class="token punctuation">(</span>itemView<span class="token operator">:</span> View<span class="token punctuation">)</span> <span class="token operator">:</span> RecyclerView<span class="token punctuation">.</span><span class="token function">ViewHolder</span><span class="token punctuation">(</span>itemView<span class="token punctuation">)</span> <span class="token punctuation">{</span> fun <span class="token function">bind</span><span class="token punctuation">(</span>banner<span class="token operator">:</span> Banner<span class="token punctuation">)</span> <span class="token punctuation">{</span> Glide<span class="token punctuation">.</span><span class="token function">with</span><span class="token punctuation">(</span>itemView<span class="token punctuation">.</span>imageViewBanner<span class="token punctuation">.</span>context<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">load</span><span class="token punctuation">(</span>user<span class="token punctuation">.</span>bannerImage<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">into</span><span class="token punctuation">(</span>itemView<span class="token punctuation">.</span>imageViewBanner<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> override fun <span class="token function">onCreateViewHolder</span><span class="token punctuation">(</span>parent<span class="token operator">:</span> ViewGroup<span class="token punctuation">,</span> viewType<span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">DataViewHolder</span><span class="token punctuation">(</span> LayoutInflater<span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span>parent<span class="token punctuation">.</span>context<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">inflate</span><span class="token punctuation">(</span> R<span class="token punctuation">.</span>layout<span class="token punctuation">.</span>item_layout_banner<span class="token punctuation">,</span> parent<span class="token punctuation">,</span> <span class="token boolean">false</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> override fun <span class="token function">getItemCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> Int <span class="token operator">=</span> <span class="token number">1</span> override fun <span class="token function">onBindViewHolder</span><span class="token punctuation">(</span>holder<span class="token operator">:</span> DataViewHolder<span class="token punctuation">,</span> position<span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token operator">=</span> holder<span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>banner<span class="token punctuation">)</span> <span class="token punctuation">}</span> |
File R.layout.item_layout_banner
:
1 2 3 4 5 6 | <span class="token operator"><</span><span class="token operator">?</span>xml version<span class="token operator">=</span><span class="token string">"1.0"</span> encoding<span class="token operator">=</span><span class="token string">"utf-8"</span><span class="token operator">?</span><span class="token operator">></span> <span class="token operator"><</span>ImageView xmlns<span class="token operator">:</span>android<span class="token operator">=</span><span class="token string">"http://schemas.android.com/apk/res/android"</span> android<span class="token operator">:</span>id<span class="token operator">=</span><span class="token string">"@+id/imageViewBanner"</span> android<span class="token operator">:</span>layout_width<span class="token operator">=</span><span class="token string">"match_parent"</span> android<span class="token operator">:</span>layout_height<span class="token operator">=</span><span class="token string">"wrap_content"</span> <span class="token operator">/</span><span class="token operator">></span> |
Bây giờ, chúng ta đã hoàn thành việc thiết kế ba adapter để hiển thị 3 loại view khác nhau trên reycler view.
Bước 05
Bây giờ, để có thể sử dụng các adapter mà chúng ta tạo trong MergeAdapter
, trước tiên, ta sẽ tạo MainActivity làm launcher activity:
1 2 3 4 5 6 7 8 | <span class="token keyword">class</span> <span class="token class-name">MainActivity</span> <span class="token operator">:</span> <span class="token function">AppCompatActivity</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> override fun <span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token operator">:</span> Bundle<span class="token operator">?</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token punctuation">)</span> <span class="token function">setContentView</span><span class="token punctuation">(</span>R<span class="token punctuation">.</span>layout<span class="token punctuation">.</span>activity_main<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
File R.layout.activity_main:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <span class="token operator"><</span>androidx<span class="token punctuation">.</span>constraintlayout<span class="token punctuation">.</span>widget<span class="token punctuation">.</span>ConstraintLayout xmlns<span class="token operator">:</span>android<span class="token operator">=</span><span class="token string">"http://schemas.android.com/apk/res/android"</span> xmlns<span class="token operator">:</span>app<span class="token operator">=</span><span class="token string">"http://schemas.android.com/apk/res-auto"</span> xmlns<span class="token operator">:</span>tools<span class="token operator">=</span><span class="token string">"http://schemas.android.com/tools"</span> android<span class="token operator">:</span>layout_width<span class="token operator">=</span><span class="token string">"match_parent"</span> android<span class="token operator">:</span>layout_height<span class="token operator">=</span><span class="token string">"match_parent"</span> tools<span class="token operator">:</span>context<span class="token operator">=</span><span class="token string">".MainActivity"</span><span class="token operator">></span> <span class="token operator"><</span>androidx<span class="token punctuation">.</span>recyclerview<span class="token punctuation">.</span>widget<span class="token punctuation">.</span>RecyclerView android<span class="token operator">:</span>id<span class="token operator">=</span><span class="token string">"@+id/recyclerView"</span> android<span class="token operator">:</span>layout_width<span class="token operator">=</span><span class="token string">"0dp"</span> android<span class="token operator">:</span>layout_height<span class="token operator">=</span><span class="token string">"0dp"</span> app<span class="token operator">:</span>layout_constraintBottom_toBottomOf<span class="token operator">=</span><span class="token string">"parent"</span> app<span class="token operator">:</span>layout_constraintEnd_toEndOf<span class="token operator">=</span><span class="token string">"parent"</span> app<span class="token operator">:</span>layout_constraintStart_toStartOf<span class="token operator">=</span><span class="token string">"parent"</span> app<span class="token operator">:</span>layout_constraintTop_toTopOf<span class="token operator">=</span><span class="token string">"parent"</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>androidx<span class="token punctuation">.</span>constraintlayout<span class="token punctuation">.</span>widget<span class="token punctuation">.</span>ConstraintLayout<span class="token operator">></span> |
Bây giờ, ta cần thêm 4 biến ở trong MainActivity:
1 2 3 4 5 | lateinit var adapter<span class="token operator">:</span> MergeAdapter lateinit var myDetailAdapter<span class="token operator">:</span> MyDetailAdapter lateinit var userVerticalAdapter<span class="token operator">:</span> UsersAdapter lateinit var bannerAdapter<span class="token operator">:</span> BannerAdapter |
chúng ta cũng sẽ tạo một biến khác sẽ được cấu trúc theo lớp dữ liệu MyDetail
và chứa thông tin chi tiết của người dùng,
1 2 | val myDetail <span class="token operator">=</span> <span class="token function">MyDetail</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token string">"Himanshu Singh"</span><span class="token punctuation">,</span> <span class="token string">"I am an writer and Open Source contributor in MindOrks."</span><span class="token punctuation">)</span> |
Tiếp theo, chúng ta cần tạo ra một phương thức để khởi tạo những thứ liên quan tới recycler view:
1 2 3 4 5 6 7 8 9 | <span class="token keyword">private</span> fun <span class="token function">setupDataInRecyclerView</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> recyclerView<span class="token punctuation">.</span>layoutManager <span class="token operator">=</span> <span class="token function">LinearLayoutManager</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span> userVerticalAdapter <span class="token operator">=</span> <span class="token function">UsersAdapter</span><span class="token punctuation">(</span>DataSource<span class="token punctuation">.</span><span class="token function">getUser</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> bannerAdapter <span class="token operator">=</span> <span class="token function">BannerAdapter</span><span class="token punctuation">(</span>DataSource<span class="token punctuation">.</span><span class="token function">getBanner</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> myDetailAdapter <span class="token operator">=</span> <span class="token function">MyDetailAdapter</span><span class="token punctuation">(</span>myDetail<span class="token punctuation">)</span> adapter <span class="token operator">=</span> <span class="token function">MergeAdapter</span><span class="token punctuation">(</span>myDetailAdapter<span class="token punctuation">,</span> userVerticalAdapter<span class="token punctuation">,</span> bannerAdapter<span class="token punctuation">)</span> recyclerView<span class="token punctuation">.</span>adapter <span class="token operator">=</span> adapter <span class="token punctuation">}</span> |
Tại đây, chúng ta sẽ khởi tạo các adapter và truyền dữ liệu cần thiết cho mỗi adapter.
Lưu ý: Thứ tự mà chúng ta truyền vào các adapter sẽ quy định thứ tự hiển thị trên danh sách của view tương ứng trên recycler view.
Khi bạn chạy ứng dụng, bạn sẽ có giao diện mà mình mong muốn:
Đây là cách bạn có thể thiết kế recyclerView với nhiều view type theo cách tuần tự bằng cách sử dụng dữ liệu từ các bộ adapter riêng lẻ.
Điểm cần nhớ:
- Giả sử nếu chúng ta cần sử dụng một loại adapter nhiều lần trong recycler view thì ta có thể tạo ra nhiều
instance
cho adapter đó và truyền vào hàm khởi tạo củaMergeAdapter
. - Chúng ta nên thực hiện logic trong từng adapter riêng.
- Khi chúng ta cập nhật dữ liệu trong bất kì adapter nào sử dụng
notifyDataSetChanged()
thì merge adapter cũng sẽ gọi phương thứcnotifyDataSetChanged()
của nó. - Thay vì truyền vào các adapter một cách riêng rẽ, ta có thể truyền vào một danh sách các adapter, ví dụ:
1 2 | val listOfAdapters <span class="token operator">=</span> listOf<span class="token operator"><</span>RecyclerView<span class="token punctuation">.</span>Adapter<span class="token operator"><</span>out RecyclerView<span class="token punctuation">.</span>ViewHolder<span class="token operator">>></span><span class="token punctuation">(</span>myDetailAdapter<span class="token punctuation">,</span> userVerticalAdapter<span class="token punctuation">,</span> bannerAdapter<span class="token punctuation">)</span> |
và ở trong hàm khởi tạo, ta có thể truyền như sau:
1 2 | adapter <span class="token operator">=</span> <span class="token function">MergeAdapter</span><span class="token punctuation">(</span>listOfAdapters<span class="token punctuation">)</span> |
Hãy nói thêm về một use case khác
- Nhìn chung, ta sử dụng một view holder cho một adapter. Nhưng trong một số trường hợp khi ta cần phải tái sử dụng view holder cho nhiều adapter thì ta cần phải truyền vào một
config
trong hàm khởi tạo, ở đó ta phải setisolateViewTypes
thànhfalse
.
Để tạo một config:
1 2 3 | val configBuilder <span class="token operator">=</span> MergeAdapter<span class="token punctuation">.</span>Config<span class="token punctuation">.</span><span class="token function">Builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span> configBuilder<span class="token punctuation">.</span><span class="token function">setIsolateViewTypes</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span> |
và ở trong MergeAdapter, ta truyền như sau:
1 2 | val adapter <span class="token operator">=</span> <span class="token function">MergeAdapter</span><span class="token punctuation">(</span>configBuilder<span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> myDetailAdapter<span class="token punctuation">,</span> userVerticalAdapter<span class="token punctuation">,</span> bannerAdapter<span class="token punctuation">)</span> |
Tương tự ở đây, ta có thể truyền một danh sách các adapter:
1 2 | val adapter <span class="token operator">=</span> <span class="token function">MergeAdapter</span><span class="token punctuation">(</span>configBuilder<span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> listOfAdapters<span class="token punctuation">)</span> |
- Giả sử, chúng ta không muốn truyền adapter vào trong hàm khởi tạo mà muốn thêm nó vào một lúc bất kì nào đó vào trong
MergeAdapter
, ta có thể làm như sau:
1 2 3 | val adapter <span class="token operator">=</span> <span class="token function">MergeAdapter</span><span class="token punctuation">(</span>listOfAdapters<span class="token punctuation">)</span> adapter<span class="token punctuation">.</span><span class="token function">addAdapter</span><span class="token punctuation">(</span>bannerAdapter<span class="token punctuation">)</span> |
Việc này sẽ thêm adapter vào vị trí cuối cùng danh sách. Nếu bạn muốn thêm adapter này vào một vị trí cụ thể, hãy làm như sau:
1 2 | adapter<span class="token punctuation">.</span><span class="token function">addAdapter</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> bannerAdapter<span class="token punctuation">)</span> |
Ở đây, chúng ta đã thêm một adapter ở vị trí thứ 0, tức là bắt đầu danh sách. Bây giờ, recyclerView sẽ như sau:
- Tương tự, để xóa một adapter:
1 2 | adapter<span class="token punctuation">.</span><span class="token function">removeAdapter</span><span class="token punctuation">(</span><span class="token comment">//adapter)</span> |
- Để lấy ra số lượng item trong recyclerView từ tất cả các adapter ta sử dụng:
1 2 | adapter<span class="token punctuation">.</span>itemCount |
- Để lấy ra danh sách các adapter được thêm vào, sử dụng:
1 2 | adapter<span class="token punctuation">.</span>adapters |
Đây là cách bạn có thể sử dụng MergeAdapter thay cho việc quản lý nhiều view type trong recycler view.
Nguồn: https://blog.mindorks.com/implementing-merge-adapter-in-android-tutorial