使用recyclerView主要需要了解RecyclerViewAdapter、layoutManager。重写recyclerViewAdapter的三个方法,onCreateViewHodler、onBindViewHolder、getItemCount。Adpater的职责是对用数据对每个item进行填充。layoutManager的作用主要就是用摆放item的位置。
首先看recyclerView的注释
adapter:正如前文所诉,RecyclerView.Adapter 负责提供表示数据集中项目的视图。
index:子view的索引,可以用getChidAt()根据索引获得子view。
Recycle:缓存之前显示过的view。
Scrap(view):在布局时进入暂时分离状态的子视图。可以在不完全脱离父 RecyclerView 的情况下重用 Scrap 视图,如果不需要重新绑定则不修改,或者如果视图被认为是脏的则由适配器修改。
Dirty (view): 脏view,dirtyView显示在界面上上必须重新绑定。
从源码可以看出,在mLayout就是LayoutManger为null时,recyclerView会走defaultOnMeasure(),mLayout的自动测量模式就是,返回是否应该使用 RecyclerView 的 AutoMeasure 机制,或者是否应该由 LayoutManager 的 onMeasure(RecyclerView.Recycler, RecyclerView.State, int, int) 实现来完成。
protected void onMeasure(int widthSpec, int heightSpec) {
if (mLayout == null) {
//View.onMeasure(int, int) 的实现,可在此 RecyclerView 缺乏更好信息的各种场景中回退。
defaultOnMeasure(widthSpec, heightSpec);
return;
}
if (mLayout.isAutoMeasureEnabled()) {
//如果开启了自动测量模式
final int widthMode = MeasureSpec.getMode(widthSpec);
final int heightMode = MeasureSpec.getMode(heightSpec);
mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
// Calculate and track whether we should skip measurement here because the MeasureSpec
// modes in both dimensions are EXACTLY.
//是否应该跳过自动测量,如果MeasureSpec是精确的
mLastAutoMeasureSkippedDueToExact =
widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY;
if (mLastAutoMeasureSkippedDueToExact || mAdapter == null) {
return;
}
if (mState.mLayoutStep == State.STEP_START) {
//布局第一步,处理适配器更新 -
//决定应该运行哪个动画 - 保存有关当前视图的信息 - 如有必要,运行预测布局并保存其信息
dispatchLayoutStep1();
}
// set dimensions in 2nd step. Pre-layout should happen with old dimensions for
// consistency
mLayout.setMeasureSpecs(widthSpec, heightSpec);
mState.mIsMeasuring = true;
dispatchLayoutStep2();
RecyclerView的测量工作,是需要dispatchLayoutStep1,dispatchLayoutStep2,dispatchLayoutStep3这三步来执行的,step1里是进行预布局,主要跟记录数据更新时需要进行的动画所需的信息有关,step2就是实际循环执行了子View的测量布局的一步,而step3主要是用来实际执行动画。而且通过mLayoutStep记录了当前执行到了哪一步。在开启自动测量的情况下如果没有设置固定宽高,那么会执行setp1和step2。在step2执行完后就可以调用setMeasuredDimensionFromChildren方法,根据子类的测量布局结果来设置自身的大小
protected void onLayout(boolean changed, int l, int t, int r, int b) {
TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
dispatchLayout();
TraceCompat.endSection();
mFirstLayoutComplete = true;
}
void dispatchLayout() {
if (mAdapter == null) {//没有设置adapter,返回
Log.e(TAG, "No adapter attached; skipping layout");
// leave the state in START
return;
}
if (mLayout == null) {//没有设置LayoutManager,返回
Log.e(TAG, "No layout manager attached; skipping layout");
// leave the state in START
return;
}
mState.mIsMeasuring = false;
//在onMeasure阶段,如果宽高是固定的,那么mLayoutStep == State.STEP_START 而且dispatchLayoutStep1和dispatchLayoutStep2不会调用
//所以这里就会调用一下
if (mState.mLayoutStep == State.STEP_START) {
dispatchLayoutStep1();
mLayout.setExactMeasureSpecsFrom(this);
dispatchLayoutStep2();
} else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()|| mLayout.getHeight() != getHeight()) {
//在onMeasure阶段,如果执行了dispatchLayoutStep1,但是没有执行dispatchLayoutStep2,就会执行dispatchLayoutStep2
mLayout.setExactMeasureSpecsFrom(this);
dispatchLayoutStep2();
} else {
mLayout.setExactMeasureSpecsFrom(this);
}
//最终调用dispatchLayoutStep3
dispatchLayoutStep3();
}
在onLayout阶段会根据onMeasure阶段3个步骤执行到了哪个,然后会在onLayout中把剩下的步骤执行。
Recycler是recyclerkview的内部类,主要用于视图的回收和复用。
mCachedViews 一级缓存:ViewHolder数据还在,只有原来的position可用,不需要重新绑定数据 mCachedViews 默认大小为 2
RecycledViewPool 二级缓存:ViewHolder数据重置,需要重新绑定数据
detach操作,是一种轻量级的分离,只会把这个View在ViewGroup的children数组中的引用设为null,并设置他的parent为null。当一个视图被分离时,它的父级为 null 并且不能通过调用getChildAt(int)来检索。
//主要field
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
//一级缓存,可设置大小,默认缓存大小为2
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
//缓存池对象,多个RecyclerView可以指定使用一个pool对象
private RecycledViewPool mRecyclerPool;
//自定义的缓存机制对象,用户可以继承之实现自己的一个缓存对象,在指定时机会使用该对象里的缓存对象
private ViewCacheExtension mViewCacheExtension;
//主要方法
//1.获取缓存view
View getViewForPosition(int position, boolean dryRun) {
...
// 1) Find from scrap by position
if (holder == null) {
holder = getScrapViewForPosition(position, INVALID_TYPE, dryRun);//从scrapView里(attachViews或者一级缓存cachedViews)根据position获取view
if (holder != null) {
if (!validateViewHolderForOffsetPosition(holder)) {
// recycle this scrap
...
} else {
fromScrap = true;
}
}
}
if (holder == null) {
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
...
final int type = mAdapter.getItemViewType(offsetPosition);
// 2) Find from scrap via stable ids, if exists
if (mAdapter.hasStableIds()) {//有stableId就从scrapView里(attachViews或者一级缓存cachedViews)根据itemId获取view
holder = getScrapViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);
if (holder != null) {
// update position
holder.mPosition = offsetPosition;
fromScrap = true;
}
}
if (holder == null && mViewCacheExtension != null) {
//此时如果有自定义的cache会尝试从中复用
// We are NOT sending the offsetPosition because LayoutManager does not
// know it.
final View view = mViewCacheExtension
.getViewForPositionAndType(this, position, type);
...
}
if (holder == null) { // fallback to recycler
...
holder = getRecycledViewPool().getRecycledView(type);//从pool里获取
...
}
if (holder == null) {
holder = mAdapter.createViewHolder(RecyclerView.this, type);//新建ViewHolder
...
}
}
...
boolean bound = false;
if (mState.isPreLayout() && holder.isBound()) {
// do not update unless we absolutely have to.
holder.mPreLayoutPosition = position;
} else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
...
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
holder.mOwnerRecyclerView = RecyclerView.this;
mAdapter.bindViewHolder(holder, offsetPosition);
attachAccessibilityDelegate(holder.itemView);
bound = true;
if (mState.isPreLayout()) {
holder.mPreLayoutPosition = position;
}
}
//设置lp
final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
final LayoutParams rvLayoutParams;
if (lp == null) {
rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
holder.itemView.setLayoutParams(rvLayoutParams);
} else if (!checkLayoutParams(lp)) {
rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
holder.itemView.setLayoutParams(rvLayoutParams);
} else {
rvLayoutParams = (LayoutParams) lp;
}
rvLayoutParams.mViewHolder = holder;
rvLayoutParams.mPendingInvalidate = fromScrap && bound;
return holder.itemView;
}
//2.从pool里获取指定type的缓存对象
public ViewHolder getRecycledView(int viewType) {
final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType);
if (scrapHeap != null && !scrapHeap.isEmpty()) {
final int index = scrapHeap.size() - 1;
final ViewHolder scrap = scrapHeap.get(index);
scrapHeap.remove(index);
return scrap;
}
return null;
}
//3.回收ViewHolder
void recycleViewHolderInternal(ViewHolder holder) {
...
if (forceRecycle || holder.isRecyclable()) {
if (!holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED
| ViewHolder.FLAG_UPDATE)) {
// Retire oldest cached view
final int cachedViewSize = mCachedViews.size();
if (cachedViewSize == mViewCacheMax && cachedViewSize > 0) {
//一级缓存满了的话移除一个到pool里,然后再放入到一级缓存里
recycleCachedViewAt(0);
}
if (cachedViewSize < mViewCacheMax) {
mCachedViews.add(holder);
cached = true;
}
}
if (!cached) {//如果没有加入到一级缓存中,则放入pool里缓存
addViewHolderToRecycledViewPool(holder);
recycled = true;
}
}...
}
//4.根据type放入到指定type的缓存集合中
public void putRecycledView(ViewHolder scrap) {
final int viewType = scrap.getItemViewType();
final ArrayList scrapHeap = getScrapHeapForType(viewType);
...
scrap.resetInternal();
scrapHeap.add(scrap);
}
//5.缓存attach的view
void scrapView(View view) {
final ViewHolder holder = getChildViewHolderInt(view);
if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
|| !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {//满足条件的attach的view放入attached集合中
...
holder.setScrapContainer(this, false);
mAttachedScrap.add(holder);
}...
}
}
2.4ConcatAdapter
一个recyclerView使用两个adapter,文本列表的相关逻辑用一个Adapter中实现的,处理Button列表相关的逻辑用另一个Adapter中实现的,这样以来就降低了代码的耦合性,并提高了代码的复用性。
itemtouchhelper是recyclerView的一个工具类,为recyclerView提供了滑动消失和拖拽删除的功能。
//声明一个Callback
ItemTouchHelper.Callback callback = new ItemTouchHelper.Callback() {
@Override
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
//返回方向值
return 0;
}
@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
//拖拽处理
return false;
}
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
//滑动处理
}
};
//创建helper对象
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);
//关联recyclerView
itemTouchHelper.attachToRecyclerView(recyclerview);
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。