写的过程中发现有人写的也很完整,可以去看看 RecyclerView 和 ListView 使用对比分析。 2021/02/24补充
主要工作是继承RecycleView.Adapter,并重写
1.RecyclerView.ViewHolder
2.onCreateViewHolder()用于创建ViewHolder实例,并把加载的布局传入到构造函数去,再把ViewHolder实例返回。
3.onBindViewHolder()则是用于对子项的数据进行赋值,会在每个子项被滚动到屏幕内时执行。position得到当前项的Fruit实例。
LayoutManager用于指定RecyclerView的布局方式。LinearLayoutManager指的是线性布局
一般使用方式:
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
FruitAdapter adapter = new FruitAdapter(fruitList); recyclerView.setAdapter(adapter);
通过调用setOrientation()把布局的排列方向改为水平排列。
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
除了LinearLayoutManager,RecyclerView还提供了GridLayoutManager(网格布局)和StaggeredGridLayoutManager(瀑布流布局)
可以参考: https://www.cnblogs.com/chen-ying/p/12386712.html
>还是自己也整理下吧
ListView需要使用自定义的ViewHolder和ConvertView来完成复用优化工作.(setTag && getTag)
RecycleView中的RecycleView.ViewHolder已经封装了复用,不需要自己优化了。
需要重写的方法肯定是不同的,翻看源码即知。(getView Vs onCreateViewHolder && onBindViewHolder)
RecycleView 只负责管理视图的重复利用,然后将布局的管理全权交给了 LayoutManager.
LayoutManager 是一个抽象类,系统已经为我们提供了三个相关的实现: LinearLayoutManager(线性布局效果)、GridLayoutManager(网格布局效果)、 StaggeredGridLayoutManager(瀑布流布局效果)。 RecyclerView 默认就能支持 线性布局、 网格布局、瀑布流布局 三种 ,通过配置和切换 LayoutManager 就可以获得不同的布局效果。所以 如果想自定义出更多布局效果,可以继承重写自己的LayoutManager,通过LayoutManager还可以 设置滚动方向、获取Item的位置
ListView现有提供了setEmptyView方法,详细使用可参考: android笔记之ListView的setEmptyView方法 ,核心内容是在ListView同级设置空数据需要展示的视图,交由ListView进行数据是否为null的判断,从而设置empty的visible。
RecycleView没有setEmptyView方法,需要自己实现。可以参考: RecyclerView添加EmptyView ,核心内容是在重写的RecycleView中重写AdapterDataObserver并注册它, 在数据变化时判断数据是否为null,从而设置RecycleView本身以及EmptyView的显示和隐藏。
ListView中可以通过addHeaderView() 与 addFooterView()来添加头部item与底部item,来当我们需要实现下拉刷新或者上拉加载的情况;而且这两个API不会影响Adapter的编写。
使用时需要注意一些东西: addHeaderView与addFooterView
RecycleView没有这两个API,但可以在Adapter中编写,根据ViewHolder的Type与View来实现自己的Header,Footter与普通的item,但是这样就会影响到Adapter,改动较大。(这是常见的作法)
可以参照: RecyclerView头部尾部添加方法,实现原理是追加Header&Footer两种View,并设置三种类型(TYPE_HEAD&TYPE_FOOTER&TYPE_NORMAL),复写getItemViewType和修改getItemCount&onCreateViewHolder&onBindViewHolder。
onCreateViewHolder(ViewGroup parent, int viewType)
该方法中的viewType就是通过getItemViewType设置的。 详细可参照链接写个demo尝试下。
ListView没有为item提供动画,需要自己实现,具体可以参照:listview添加item动画
整体设置动画:
listView.setLayoutAnimation(getAnimationController());
RecycleView有为item提供默认动画: DefaultItemAnimator,在使用RecycleView时进行增删的时候,会很明显看到动画效果。 若是想深入研究或自定义动画,可以参照: 自定义DefaultItemAnimator
ListView的刷新notifyDataSetChanged,它会重绘所有item,所以不能应用于局部刷新。问题来了,ListView如何局部刷新呢?
a. 官方推荐方式:
public View getView(int position, View convertView, ViewGroup parent) {
// 获取到指定位置的View,主动调用getView方法
View view = listView.getChildAt(position- listView.getFirstVisiblePosition());
getView(position, view, listView);
b.大众的方式: (在Adapter中添加一个局部刷新的方法)
public void updateItem(ListView mListView, int posi) {
if (mListView != null) {
// 获取第一个显示的item
int visiblePos = mListView.getFirstVisiblePosition();
// 计算出当前选中的position和第一个的差,也就是当前在屏幕中的item位置
int offset = posi - visiblePos;
int lenth = mListView.getChildCount();
// 只有在可见区域才更新,因为不在可见区域得不到Tag,会出现空指针,所以这是必须有的一个步骤
if ((offset < 0) || (offset >= lenth)) return;
View convertView = mListView.getChildAt(offset);
ViewHolder viewHolder = (ViewHolder) convertView.getTag();
// 已知view||viewholder,就可以进行自己想要的数据刷新
// Tips:别忘了数据的更新
}
}
RecycleView提供了局部刷新的方法: notifyItemChanged,源码如下.
public final void notifyItemChanged(int position) {
mObservable.notifyItemRangeChanged(position, 1);
}
ItemTouchHelper 是帮助Recycleview实现拖拽和侧滑的工具类。
// 1. 继承并重写 ItemTouchHelper.Callback
public class ItemTouchHelperCallback extends ItemTouchHelper.Callback{
// 重写getMovementFlags()方法来指定可以支持的拖放和滑动的方向使用
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlags, swipeFlags);
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
mRecyclerAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
return false;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
mRecyclerAdapter.onItemDismiss(viewHolder.getAdapterPosition());
}
}
// 2. 建立与RecycleView的链接
ItemTouchHelper mItemTouchHelper = new ItemTouchHelper(new ItemTouchHelperCallback());
mItemTouchHelper.attachToRecyclerView(mRecyclerView);
// 补充说明
// onItemMove中主要做notifyItemMoved(fromPosition, toPosition); 以及数据的删除
以上可以轻松实现Recycleview的侧滑和拖拽的动画效果。
但是ListView没有提供此类方法,实现ListView的侧滑和拖拽可以参考: ListView的拖动和侧滑实现 (WindowManager实现拖动, ViewDragHelper实现侧滑), 写的比较清晰,大家可以尝试下。
ListView中提供了setOnItemClickListener、 setOnItemLongClickListener、setOnItemSelectedListener等方法,但要考虑设置了header和footer后的数组越界问题。
Recycleview没有提供这些方法,因此需要通过viewHolder去给子项设置具体的监听。(很常见,就不贴什么代码了)
Android 触摸事件分发机制:Touch 事件在进行分发的时候,由父 View 向它的子 View 传递,一旦某个子 View 开始接收进行处理,那么接下来所有事件都将由这个 View 来进行处理,它的 ViewGroup 将不会再接收到这些事件,直到下一次手指按下。而嵌套滚动机制(NestedScrolling)就是为了弥补这一机制的不足,为了让子 View 能和父 View 同时处理一个 Touch 事件。
CollapsingToolbarLayout 这种需要嵌套滚动的机制才能达到效果的控件,那么 首选RecyclerView (Recycleview实现了NestedScrollingChild接口),因为 ListView无效。同样的,ScrollView 也是不支持嵌套滚动机制,但是你可以使用 NestedScrollView 。
ListView缓存机制
RecycleBin机制,RecycleBin定义在AbsListView当中。其中使用View[] mActiveViews存储View,是屏幕当中正在使用的View,mActiveViews中存储的View只能被获取一次;ArrayList[] mScrapViews和ArrayList mCurrentScrap则是用于存储移动出屏幕后被废弃的View。当getView方法在适配器中被调用的时候,其中传入的convertView如果不为空,就是从废弃的View当中获得的。
RecyclerView缓存机制
Scrap和Cache分别是通过position去找ViewHolder可以直接复用;
在RecyclerView当中也并不是每次都重新创建ViewHolder对象,不是每次都重新绑定ViewHolder数据,而是通过Recycler来获得下一个ViewHolder。
RecyclerView使用Recycler管理缓存ViewHolder,对于不同状态的ViewHolder存储在了不同的集合中,RecyclerView有四级缓存,分别是ArrayList mAttachedScrap、ArrayList mCachedViews、ViewCacheExtension mViewCacheExtension 和 RecycledViewPool mRecyclerPool,缓存的对象是ViewHolder。
而从RecycledViewPool 中复用的ViewHolder需要重新绑定数据,并且复用的ViewHolder只能复用于ViewType相同的表项,因为RecycledViewPool 对ViewHolder是按照ViewType分类存储的,RecycledViewPool通过type来获取ViewHolder,获取的ViewHolder是个全新,需要重新绑定数据;ViewCacheExtension自定义缓存,目前来说应用场景比较少却需慎用;从mCachedViews中复用ViewHolder只能复用于指定位置的item;而从mAttachedScrap复用的ViewHolder不需要重新创建也不需要重新绑定数据。
如果四个层级的缓存都没有命中,才会重新创建ViewHolder对象并且绑定。
第二章节大致介绍了两者的缓存机制。
详细的源码分析可以参照: 明日补充,一时找不到之前看的链接了。看中了人家的流程图。(实在是没有太多精力细细的肝源码了)
可以参考: https://juejin.cn/post/6844903661726859271#heading-4
想认真的理解并掌握所有的区别以及原理,怕不是那么容易的事情哟。
知道的越多,不知道的越多。
参考文章:
https://www.cnblogs.com/chen-ying/p/12386712.html
https://www.jianshu.com/p/3e9aa4bdaefd
https://juejin.cn/post/6844903661726859271#heading-4