美文网首页Android开发
RecyclerView: 忘掉Adapter, LayoutM

RecyclerView: 忘掉Adapter, LayoutM

作者: DoKar | 来源:发表于2021-03-04 12:17 被阅读0次
    LazyRecycler Demo

    传统的RecyclerView用法:

    • 继承RecyclerView.Adapter实现getItemCount()createViewHolder(), onBindViewHolder()等方法。
    • 继承RecyclerView.ViewHolder实现数据绑定等。
    • 添加点击事件的接口。
    • 如果需要DiffUtil,还得实现自己的ItemCallback,然后整合进Adapter。
    • 选择LayoutManger,并调用recyclerView.setLayoutManager
    • 实例化Adapter,并调用recyclerView.setAdapter
    • 实现点击事件的接口。
    • 更新数据时调用adapter.notifyDataChanged()等方法,使用ListAdapter时调用submitList

    这个过程相比于要被替代的ListView来说不仅没有简化,还变得更复杂了。猜测这也是RecylerView虽然强大,但却一直没有彻底代替ListView等老组件的原因之一。

    后来Jetpack Compose出来后,看到LazyColumn的写法如此简洁:

    LazyColumn {
        items(itemsList) {
            Text("Item is $it")
        }
    
        item {
            Text("Single item")
        }
    
        itemsIndexed(itemsIndexedList) { index, item ->
            Text("Item at index $index is $item")
        }
    }
    

    当时也并没有什么想法。。。后来看到了sqaure/recycler库配置RecyclerView的方式:

    val recycler = Recycler.create<ItemType>(context, id = R.id.myrecycler) {
        ...
        row<I, S, V> {
          forItemsWhere { subitem -> ...boolean... }
          create(R.layout.my_layout) {
            // you can get references to sub-elements inside view
            val subView = view.findViewById(...)
            bind { subItem ->
              // assign values from subItem to view or sub-elements
            }
          }
          ...more row options...
        }
    }
    

    完全省略了定义Adapter的步骤,但感觉仍然还是有些复杂。那时萌生了一个想法:能不能把LazyColumn那套定义方式带给RecyclerView呢?于是就有了下面这部分。

    LazyRecycler

    LazyRecycler使用和LazyColumn相似的方式来写RecylcerView,把AdapterLayoutManagerDiffUtil点击事件等定义全部放到一个代码块中:

    LazyRecycler(recyclerView, spanCount = 3) {
        // 多种view type支持
        item(R.layout.header, Unit) {}
    
        items(listOfNews) { binding: ItemNewsBinding, news ->
            // 绑定
        }.clicks { view, item ->
            // 点击事件
        }.spanSize { position ->
            if (position % 3 == 0) 3 else 1
        }.differ {
            areItemsTheSame { oldItem, newItem ->
                oldItem.id == newItem.id
            }
            areContentsTheSame { oldItem, newItem ->
                oldItem.title == newItem.title && ...
            }
        }
        
        item(R.layout.footer, Unit) {}
    }
    

    相比cycler的layout id还支持ViewBinding,不用在bind外边findViewById了。
    为了体验更加接近Compose的LazyColumn,还支持Flow, LiveData, RxJava的Observable作为数据源:

    val source: Flow<List<Item>> = ...
    ...
    items(source) { ... }
    

    调用recycler.observeChanges()观察数据变化,自动更新Adapter。

    使用

    implementation 'io.github.dokar3:lazyrecycler:0.1.1'
    
    // Flow支持
    implementation 'io.github.dokar3:lazyrecycler-flow:0.1.1'
    // LiveData支持
    implementation 'io.github.dokar3:lazyrecycler-livedata:0.1.1'
    // RxJava支持
    implementation 'io.github.dokar3:lazyrecycler-rxjava3:0.1.1'
    

    创建LazyRecycler

    // 传入RecyclerView
    LazyRecycler(recyclerView) {
        ...
    }
    
    // attachTo()
    val lazyRecycler = LazyRecycler {
        ...
    }
    lazyRecycler.attachTo(recyclerView)
    

    创建item / items:

    item(R.layout.header, Unit) { view -> 
        val title: TextView = view.findViewById(...)
        bind { 
            title.text = ...
        }
    }
    
    item<HeaderBinding> { ... }
    
    items(R.layout.item_news, newsList) { view ->
        val title: TextView = view.findViewById(...)
        val cover: ImageView = view.findViewById(...)
        ...
        bind { news ->
            title.text = news.title
            cover.load(news.cover)
            ...
        }
    }
    
    items(newsList) { binding: ItemNewsBinding, news -> 
        binding.title.text = news.title
        binding.cover.load(news.cover)
        ...
    }
    

    各类配置:

    items(...) {
        ...
    }.clicks { view, news ->
        // 点击事件
    }.longClicks { view, news ->
        // 长按事件
        true
    }.spanSize { position
        // SpanSizeLookup.getSpanSize的映射
        1
    }.differ {
        // DiffUtil.ItemCallback的映射
        areItemsTheSame { old, new -> ... }
        areContentsTheSame { old, new -> ... }
    }.subSection(sectionBreakingNews) { news, position ->
        // 同数据源不同item type
        news.priority == ...
    }
    ...
    

    更多使用请查看Github:
    https://github.com/dokar3/LazyRecycler

    相关链接:

    相关文章

      网友评论

        本文标题:RecyclerView: 忘掉Adapter, LayoutM

        本文链接:https://www.haomeiwen.com/subject/wtnhqltx.html