RecyclerView
前言
一、RecyclerView
曾经列表数据的显示通常会使用到ListView,但RecyclerView的强大性能和泛用性早早打败了ListView成为现在的主流,让我们首先了解RecyclerView的基本使用方法。
1.viewholder状态
方法 | FLAG | 含义 | 具体场景 |
---|---|---|---|
isInvalid() | FLAG_INVALID | viewholder数据无效 | 1. 调用了setAdapter() 2. 调用了notifyDataSetChanged()等方法 |
isRemoved() | FLAG_REMOVED | viewholder的数据已经被移除 | 调用了notifyItemRemoved |
isUpdated() | FLAG_UPDATE | viewholder的数据需要重新绑定 | 1. 调用了onBindViewHolder 2. 调用了notifyItemChanged() |
isBound() | FLAG_BOUND | 数据已经绑定在某个item上,数据是有效状态 | 调用了onBindViewHolder() |
2.缓存机制
RecyclerVeiw的高性能的根源就在于它优秀的缓存机制。
-
一级缓存
第一层缓存是轻量的,每次都只会保存当前页面所显示的item,并且每次用完后就会清空缓存。RecycleView局部刷新用到的就是一级缓存实现复用。mChangedScrap主要是为列表项数据发生变化时的动画效果服务的,而mAttachedScrap应对的则是剩下的绝大部分场景。 -
二级缓存
二级缓存CachView是用于当变得位置发生改变时,保存被移出屏幕的view,默认的容量是2,如果有需要可以修改这个容量数值(setItemViewCacheSize(int))。 -
三级缓存
三级缓存也可以称为一个缓存池RecycledViewPool,会保存一二级缓存里存储不下的viewholder,类似一个回收站。它会根据viewType分类,将相同类型的ViewHolder存放在同一个ArrayList中。从一二级缓存取数值需要根据position,而三级缓存根据viewType
3.布局管理器
LinearLayoutManager: 线性布局管理器
三种构造方法:
-
LinearLayoutManager(Context context) :
调用了第二个构造方法。 -
LinearLayoutManager(Context context,int orientation,boolean reverseLayout) :
int orientation :方向,垂直(RecyclerView.VERTICAL)和水平(RecyclerView.HORIZONTA ),默认为垂直。(也可通过setOrientation()设置)
boolean reverseLayout :是否倒序,设置为True,从最后一个item开始,倒序加载。此时,RecyclerView第一个item是添加进Adapter中的最后一个,最后一个item是第一个加进Adapter的数据,RecyclerView会自动滑到末尾,另外item整体是依靠下方的。(也可通过setReverseLayout()设置) -
LinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,int defStyleRes) :
可以使用自定义属性。
常用方法:
- setOrientation():设置方向
- setReverseLayout():设置是否倒序
- setStackFromEnd():是否从底部开始展示
setReverseLayout()与setStackFromEnd() :共同点是都自动滑动尾部。不同点是setStackFromEnd(true)不会影响内部的数据顺序,怎么添加进Adapter的,就怎么展示。 - scrollToPosition(int position):滑动到指定item
- findFirstVisibleItemPosition():第一个可见的item的postion
findLastVisibleItemPosition():最后一个可见的item的postion
若RecyclerView不在屏幕中,则全部返回-1
GridLayoutManager: 表格布局管理器
构造函数:
其它参数含义与LinearLayoutManager相同,除了下方参数:
- spanCount : 垂直方向时表示列数,水平方向时表示行数
setStackFromEnd()不支持GridLayoutManager(),但支持setReverseLayout(boolean)方法。
StaggeredGridLayoutManager: 瀑布流布局管理器
构造方法:
- StaggeredGridLayoutManager(int spanCount, int orientation):
- StaggeredGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
StaggeredGridLayoutManager与GridLayoutManager区别
- StaggeredGridLayoutManager效果图]:
- GridLayoutManager效果图]:
两者之间的效果差异如上图:StaggeredGridLayoutManager的item是可以有不同的宽高的,而GridLayoutManager的item每行都是相同宽高的。
GridLayoutManager高度是由每行最高的决定,宽度则默认都是1,也就是平分宽度,但也可以通过设置spansize来修改。
val m = GridLayoutManager(context, 2)
m.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
when (position) {
0 or 1 -> {
return 3
}
else -> return 1;
}
}
}
而StaggeredGridLayoutManager中,下一行的元素位置是按照上一行item的bottom哪个最小就先放在哪个下边的。
需要注意的是,StaggeredGridLayoutManager中只有item的高度是wrap_content的时候,才应该设置ItemDecoration。
2.局部刷新的实现
当你只需要修改RecycleView的一部分的时候,每次都整个刷新会导致观感很差,实际上RecycleView早已实现了局部刷新,并且具有动画效果,再配合SmartRefreshLayout即可实现优雅的刷新!
RecycleView提供的局部更新函数如下:
增加
- notifyItemInserted(int position):更新在position之前插入的item
- notifyItemRangeInserted(int position, int count):更新在position之前插入的items,数目为count
删除
- notifyItemRemoved(int position):删除指定位置item
- notifyItemRangeRemoved(int positionStart, int itemCount):删除指定位置往后数itemCount个的items
修改
- notifyItemChanged(int position):修改指定位置item
- notifyItemChanged(int position, Object payload):详细参考文章:RecyclerView局部刷新机制
灵活使用payload可以实现处理不同的局部刷新
override fun onBindViewHolder(holder: BookTypeHolder, position: Int, payloads: MutableList<Any>) {
if (payloads.isNotEmpty() && "0" == payloads[0]) {
holder.view.isSelected = position == curPosition
} else {
// 如果没有 payloads ,或者不是我们指定的,还是返回默认的整个刷新
onBindViewHolder(holder, position)
}
}
//需要局部刷新时像这样调用即可:
notifyItemChanged(prePosition, SELECT_CHANGED)
二、与SmartRefreshLayout、MutableList搭配
1.MutableList使用
kotlin中数组是Array<>,列表是list,list是不可改变的,当需要实现可改变的列表的话则需要使用MutableList。
- 创建:mutableListOf()
- 末尾/指定位置添加元素:add(item)/add(int, item)
- 添加另一个Lsit/Array/Set:addAll()
- 移除指定位置元素:removeAt(int)
- 移除指定元素:remove(item)
- 替换指定位置元素:set(int, item)/[int] =
- 取子列表:subList(start, end)
- 清空:clear()/removeAll(item)
2.SmartRefresh的使用
xml:
<com.scwang.smart.refresh.layout.SmartRefreshLayout
android:id="@+id/refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible"
app:srlEnableLoadMore="true">
<com.scwang.smart.refresh.header.ClassicsHeader
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:srlEnableLastTime="false"
app:srlPrimaryColor="@color/white"
app:srlTextFinish="" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/read_view_rv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.scwang.smart.refresh.footer.ClassicsFooter
android:id="@+id/footer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:srlTextFinish="" />
</com.scwang.smart.refresh.layout.SmartRefreshLayout>
SmartRefreshLayout使用:
//下拉
refresh_layout.setOnRefreshListener(refreshLayout -> {
//加载之前的数据
topRefresh();
refreshLayout.finishRefresh(500);
});
//上拉
refresh_layout.setOnLoadMoreListener(refreshLayout -> {
//加载之后的数据
bottomRefresh();
refreshLayout.finishLoadMore(500);
});
adapter中局部刷新函数:
//刷新上方的数据
fun notifyTop(items: List<A>) {
var index = 0
hpIndexs.forEach {
list.add(index++, it)
}
this.notifyItemRangeInserted(0, list.size)
}
//刷新下方的数据
fun notifyBottom(hpIndexs: List<HPIndex>) {
val startIndex = list.size - 1
hpIndexs.forEach {
list.add(it)
}
this.notifyItemRangeInserted(startIndex, hpIndexs.size)
}