Sobes.tech
Middle

Jak rozwiązać problem z różnymi typami widoków dla RecyclerView?

sobes.tech AI

Odpowiedź od AI

Istnieje kilka podejść:

  1. Użycie wielu ViewTypes w adapterze. Najczęstszy i najbardziej elastyczny sposób. Adapter nadpisuje metodę getItemViewType, która zwraca typ widoku dla konkretnej pozycji. Następnie w metodach onCreateViewHolder i onBindViewHolder używa się tego typu do tworzenia i wiązania odpowiedniego ViewHolder.

    class MyAdapter(private val items: List<Item>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    
        private val VIEW_TYPE_TEXT = 0
        private val VIEW_TYPE_IMAGE = 1
    
        override fun getItemViewType(position: Int): Int {
            return when (items[position]) {
                is TextItem -> VIEW_TYPE_TEXT
                is ImageItem -> VIEW_TYPE_IMAGE
            }
        }
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
            return when (viewType) {
                VIEW_TYPE_TEXT -> {
                    val view = LayoutInflater.from(parent.context).inflate(R.layout.item_text, parent, false)
                    TextViewHolder(view)
                }
                VIEW_TYPE_IMAGE -> {
                    val view = LayoutInflater.from(parent.context).inflate(R.layout.item_image, parent, false)
                    ImageViewHolder(view)
                }
                else -> throw IllegalArgumentException("Nieprawidłowy typ widoku")
            }
        }
    
        override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
            when (holder.itemViewType) {
                VIEW_TYPE_TEXT -> {
                    val textHolder = holder as TextViewHolder
                    val textItem = items[position] as TextItem
                    textHolder.bind(textItem)
                }
                VIEW_TYPE_IMAGE -> {
                    val imageHolder = holder as ImageViewHolder
                    val imageItem = items[position] as ImageItem
                    imageHolder.bind(imageItem)
                }
            }
        }
    
        override fun getItemCount(): Int {
            return items.size
        }
    
        // ViewHolders dla różnych typów
        class TextViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
            private val textView: TextView = itemView.findViewById(R.id.textView)
            fun bind(item: TextItem) {
                textView.text = item.text
            }
        }
    
        class ImageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
            private val imageView: ImageView = itemView.findViewById(R.id.imageView)
            fun bind(item: ImageItem) {
                // Wczytywanie obrazu
            }
        }
    }
    
    data class TextItem(val text: String)
    data class ImageItem(val imageUrl: String)
    
  2. Użycie bibliotek. Istnieją biblioteki upraszczające pracę z wieloma ViewTypes, np. Groupie lub Epoxy. Zapewniają abstrakcje do definiowania "items" z odpowiednimi ViewHolders i logiką wiązania.

    • Groupie:

      class MyItemAdapter(private val items: List<GroupieItem<*>>) : GroupAdapter<ViewHolder>() {
          init {
              addAll(items)
          }
      }
      
      // Przykład itemu Groupie
      class TextItemGroupie(private val text: String) : Item<ViewHolder>() {
          override fun getLayout(): Int = R.layout.item_text
      
          override fun bind(viewHolder: ViewHolder, position: Int) {
              viewHolder.itemView.findViewById<TextView>(R.id.textView).text = text
          }
      }
      
      class ImageItemGroupie(private val imageUrl: String) : Item<ViewHolder>() {
          override fun getLayout(): Int = R.layout.item_image
      
          override fun bind(viewHolder: ViewHolder, position: Int) {
             // Wczytywanie obrazu
          }
      }
      
    • Epoxy: Używa adnotacji do generowania EpoxyModel i EpoxyController.

  3. Użycie złożonych ViewHolders. Mniej popularne podejście, gdy jeden ViewHolder zawiera kilka różnych widoków, a wybór widocznego widoku odbywa się wewnątrz ViewHolder (np. przez setVisibility lub inne metody). Niezalecane dla dużej liczby różnych typów lub złożonej logiki.

  4. Oddzielenie danych. Jeśli dane można wyraźnie podzielić na grupy z różnymi widokami, można użyć kilku niezależnych RecyclerView (co często nie jest optymalne) lub kompozycji adapterów.