Android中级——ListView和RecycleView解析

ListView和RecycleView

ListView

使用步骤可看Android基础------ListView,其setAdapter()如下,回调getCount()获取Item个数

@Override
public void setAdapter(ListAdapter adapter) {
    if (mAdapter != null && mDataSetObserver != null) {
        mAdapter.unregisterDataSetObserver(mDataSetObserver);
    }
    resetList();
    mRecycler.clear();
    if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
        mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);
    } else {
        mAdapter = adapter;
    }
	......

    super.setAdapter(adapter);
    if (mAdapter != null) {
        ......
        mItemCount = mAdapter.getCount();
        checkFocus();
        mDataSetObserver = new AdapterDataSetObserver();
        mAdapter.registerDataSetObserver(mDataSetObserver);
        ......
    }......
    requestLayout();
}

创建AdapterDataSetObserver,调用ListAdapter的registerDataSetObserver(),其实现类是BaseAdapter

private final DataSetObservable mDataSetObservable = new DataSetObservable();

public void registerDataSetObserver(DataSetObserver observer) {
    mDataSetObservable.registerObserver(observer);
}

public void unregisterDataSetObserver(DataSetObserver observer) {
    mDataSetObservable.unregisterObserver(observer);
}

public void notifyDataSetChanged() {
    mDataSetObservable.notifyChanged();
}

当调用notifyDataSetChanged()时会回调AdapterDataSetObserver的notifyChanged(),其在ListView的父类AbsListView里面

class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
    @Override
    public void onChanged() {
        super.onChanged();
        ......
    }
    ......
}

调用AdapterView的AdapterDataSetObserver的notifyChanged()

class AdapterDataSetObserver extends DataSetObserver {
    ......
    @Override
    public void onChanged() {
        mDataChanged = true;
        mOldItemCount = mItemCount;
        mItemCount = getAdapter().getCount();
        ......
        checkFocus();
        requestLayout();
    }
    ......
}

调用requestLayout()会重新布局,调用AdapterView的onLayout()

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    super.onLayout(changed, l, t, r, b);
   	......
    layoutChildren();
    ......
}

调用ListView的layoutChildren(),根据mLayoutMode设置布局方式

@Override
protected void layoutChildren() {
    final boolean blockLayoutRequests = mBlockLayoutRequests;
    if (blockLayoutRequests) {
        return;
    }
    mBlockLayoutRequests = true;
    try {
        super.layoutChildren();
        invalidate();
        ......
        
        switch (mLayoutMode) {
        ......
        case LAYOUT_FORCE_BOTTOM:
            sel = fillUp(mItemCount - 1, childrenBottom);
            adjustViewsUpOrDown();
            break;
        case LAYOUT_FORCE_TOP:
            mFirstPosition = 0;
            sel = fillFromTop(childrenTop);
            adjustViewsUpOrDown();
            break;
        ......
        }......
    }
}

调用ListView的fillFromTop()会调用fillDown()从上到下布局,若不到底部且小于总数,则循环创建ItemView

private View fillDown(int pos, int nextTop) {
    View selectedView = null;
    int end = (mBottom - mTop);
    if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {
        end -= mListPadding.bottom;
    }
    while (nextTop < end && pos < mItemCount) {
        boolean selected = pos == mSelectedPosition;
        View child = makeAndAddView(pos, nextTop, true, mListPadding.left, selected);
        nextTop = child.getBottom() + mDividerHeight;
        if (selected) {
            selectedView = child;
        }
        pos++;
    }
    setVisibleRangeHint(mFirstPosition, mFirstPosition + getChildCount() - 1);
    return selectedView;
}

private View makeAndAddView(int position, int y, boolean flow, int childrenLeft, boolean selected) {
   	......
    final View child = obtainView(position, mIsScrap);
    setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);
    return child;
}

调用AdapterView的obtainView(),这里会回调getItemViewType()、getView()

View obtainView(int position, boolean[] outMetadata) {
    ......
    final View transientView = mRecycler.getTransientStateView(position);	//从缓存列表mTransientStateViews指定位置获取Item
    if (transientView != null) {
        final LayoutParams params = (LayoutParams) transientView.getLayoutParams();
        if (params.viewType == mAdapter.getItemViewType(position)) {	//若ViewType没变则回调getView()获取View重新绑定数据
            final View updatedView = mAdapter.getView(position, transientView, this);
            if (updatedView != transientView) {		//若没有复用,返回的不是同一个View,则添加到ScrapView
                setItemViewLayoutParams(updatedView, position);
                mRecycler.addScrapView(updatedView, position);
            }
        }
     	......
        return transientView;
    }
    final View scrapView = mRecycler.getScrapView(position);	//若不在缓存列表,取出ScrapView列表对应位置的scrapView 
    final View child = mAdapter.getView(position, scrapView, this);	//回调getView()获取child,若scrapView为空则返回新的View,若不为空也可能返回的不是同一个View,如Header
    if (scrapView != null) {
        if (child != scrapView) {	//若scrapView和child不一样,说明是新的View,将scrapView添加到缓存列表mTransientStateViews
            mRecycler.addScrapView(scrapView, position);
        }......
    }
    ......
    setItemViewLayoutParams(child, position);
    ......
    return child;
}

以下面代码为例

 @Override
 public View getView(int position, View convertView, ViewGroup parent) {
     ViewHolder holder = null;
     if (convertView == null) {
         holder = new ViewHolder();
         convertView = mInflater.inflate(R.layout.list_item, null);
         holder.dataIv = convertView.findViewById(R.id.list_iv);
         holder.dataTv = convertView.findViewById(R.id.list_tv);
         convertView.setTag(holder);
     } else {
         holder = (ViewHolder) convertView.getTag();
     }
     Data data = (Data) getItem(position);
     holder.dataIv.setImageResource(data.getImageId());
     holder.dataTv.setText(data.getName());
     return convertView;
 }
  • 第一次创建时convertView为空,会创建铺满屏幕个数的Item+1,如下图为8个
  • 当滑动时将Item1缓存起来,并赋值给convertView,下一个Item9就会复用Item1重新绑定数据

RecycleView

使用步骤可看Android基础------RecycleView

  • onCreateViewHolder()、onBindViewHolder()相当于ListView的getView()
  • 操作对象变成ViewHolder,无需像ListView那样判断缓存

其setAdapter()方法如下

public void setAdapter(@Nullable Adapter adapter) {
    setLayoutFrozen(false);
    setAdapterInternal(adapter, false, true);
    processDataSetCompletelyChanged(false);
    requestLayout();
}

private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious, boolean removeAndRecycleViews) {
    if (mAdapter != null) {
        mAdapter.unregisterAdapterDataObserver(mObserver);
        mAdapter.onDetachedFromRecyclerView(this);
    }
    if (!compatibleWithPrevious || removeAndRecycleViews) {
        removeAndRecycleViews();
    }
    mAdapterHelper.reset();
    final Adapter oldAdapter = mAdapter;
    mAdapter = adapter;
    if (adapter != null) {
        adapter.registerAdapterDataObserver(mObserver);
        adapter.onAttachedToRecyclerView(this);
    }
    if (mLayout != null) {
        mLayout.onAdapterChanged(oldAdapter, mAdapter);
    }
    mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
    mState.mStructureChanged = true;
}

将RecyclerViewDataObserver注册到RecyclerViewAdapter,当调用notifyDataSetChanged()会回调onChanged()重新布局

private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();

public final void notifyDataSetChanged() {
    mObservable.notifyChanged();
}

private class RecyclerViewDataObserver extends AdapterDataObserver {
    ......
    @Override
    public void onChanged() {
        ......
        if (!mAdapterHelper.hasPendingUpdates()) {
            requestLayout();
        }
    }
   	......
}

requestLayout()会调用onLayout()、dispatchLayout()、dispatchLayoutStep1()

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    ......
    dispatchLayout();
    ......
}

void dispatchLayout() {
    ......
    if (mState.mLayoutStep == State.STEP_START) {
        dispatchLayoutStep1();
        mLayout.setExactMeasureSpecsFrom(this);
        dispatchLayoutStep2();
    }......
    dispatchLayoutStep3();
}


private void dispatchLayoutStep1() {
    ......
    if (mState.mRunPredictiveAnimations) {
        ......
        mLayout.onLayoutChildren(mRecycler, mState);
        ......
    }
	......
}

mLayout通过setLayoutManager()设置

public void setLayoutManager(@Nullable LayoutManager layout) {
    if (layout == mLayout) {
        return;
    }
    ......
    mLayout = layout;
    if (layout != null) {
        ......
        mLayout.setRecyclerView(this);
        if (mIsAttached) {
            mLayout.dispatchAttachedToWindow(this);
        }
    }
    mRecycler.updateViewCacheSize();
    requestLayout();
}

以LinearLayoutManager的onLayoutChildren()为例

public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state){
   ......
    if (mAnchorInfo.mLayoutFromEnd) {
       ......
    } else {
    	......
        fill(recycler, mLayoutState, state, false);
       ......
    }
    ......
}

int fill(RecyclerView.Recycler recycler, LayoutState layoutState, RecyclerView.State state, boolean stopOnFocusable) {
    ......
    int remainingSpace = layoutState.mAvailable + layoutState.mExtraFillSpace;	//计算当前可用空间
    LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
    while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
        ......
        layoutChunk(recycler, state, layoutState, layoutChunkResult);	//若还有空间则循环布局Item
        ......
    }
   	......
    return start - layoutState.mAvailable;
}

void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutState layoutState, LayoutChunkResult result) {
    View view = layoutState.next(recycler);	//获取View
    ......
    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();	//获取LayoutParams
    ......
    measureChildWithMargins(view, 0, 0);	//测量View
    result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);	//计算消耗的宽高
    int left, top, right, bottom;	//计算上下左右坐标
    if (mOrientation == VERTICAL) {
        if (isLayoutRTL()) {
            right = getWidth() - getPaddingRight();
            left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);
        } else {
            left = getPaddingLeft();
            right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
        }
        if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
            bottom = layoutState.mOffset;
            top = layoutState.mOffset - result.mConsumed;
        } else {
            top = layoutState.mOffset;
            bottom = layoutState.mOffset + result.mConsumed;
        }
    }......
    layoutDecoratedWithMargins(view, left, top, right, bottom);	//调用layout()
    ......
    result.mFocusable = view.hasFocusable();
}

public void layoutDecoratedWithMargins(@NonNull View child, int left, int top, int right, int bottom) {
    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
    final Rect insets = lp.mDecorInsets;
    child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,
            right - insets.right - lp.rightMargin,
            bottom - insets.bottom - lp.bottomMargin);
}

View next(RecyclerView.Recycler recycler) {
    ......
    final View view = recycler.getViewForPosition(mCurrentPosition);
    mCurrentPosition += mItemDirection;
    return view;
}

next()调用RecyclerView的getViewForPosition(),回调getItemViewType()

@NonNull
public View getViewForPosition(int position) {
    return getViewForPosition(position, false);
}

View getViewForPosition(int position, boolean dryRun) {
    return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
}

@Nullable
ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) {
    ......
    ViewHolder holder = null;
    if (mState.isPreLayout()) {
        holder = getChangedScrapViewForPosition(position);
        ......
    }
    if (holder == null) {
        holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
        ......
    }
    if (holder == null) {
        final int offsetPosition = mAdapterHelper.findPositionOffset(position);
        ......
        final int type = mAdapter.getItemViewType(offsetPosition);
        if (mAdapter.hasStableIds()) {
            holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);
           ......
        }
        if (holder == null && mViewCacheExtension != null) {
            ......
            final View view = mViewCacheExtension.getViewForPositionAndType(this, position, type);
            if (view != null) {
                holder = getChildViewHolder(view);
                ......
            }
        }
        if (holder == null) {
            ......
            holder = getRecycledViewPool().getRecycledView(type);
         	......
        }
        if (holder == null) {	//上面是各种缓存都为空,才调用createViewHolder()
            ......
            holder = mAdapter.createViewHolder(RecyclerView.this, type);
            ......
        }
    }
    ......
    boolean bound = false;
    if (mState.isPreLayout() && holder.isBound()) {
        ......
    } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) { //这里绑定数据
        ......
        final int offsetPosition = mAdapterHelper.findPositionOffset(position);
        bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
    }
    ......
    return holder;
}

调用RecyclerView的createViewHolder()、tryBindViewHolderByDeadline()

@NonNull
public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
    try {
        final VH holder = onCreateViewHolder(parent, viewType);
        ......
        holder.mItemViewType = viewType;
        return holder;
    }......
}

private boolean tryBindViewHolderByDeadline(@NonNull ViewHolder holder, int offsetPosition, int position, long deadlineNs) {
    ......
    mAdapter.bindViewHolder(holder, offsetPosition);
    ......
    return true;
}
 
public final void bindViewHolder(@NonNull VH holder, int position) {
    ......
    onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
    ......
}

public void onBindViewHolder(@NonNull VH holder, int position, @NonNull List<Object> payloads) {
    onBindViewHolder(holder, position);
}
相关推荐
一只傻小白,4 分钟前
JAVA项目中freemarker静态模板技术
java·开发语言
袁庭新4 分钟前
Spring Boot项目接收前端参数的11种方式
java·springboot·袁庭新·如何接收前端数据·boot接收数据
机跃5 分钟前
递归算法常见问题(Java)
java·开发语言·算法
程序员-小李25 分钟前
餐厅下单助手系统(Java+MySQL)
java·开发语言·mysql
开心工作室_kaic29 分钟前
springboot496基于java手机销售网站设计和实现(论文+源码)_kaic
java·开发语言·智能手机
像少年啦飞驰点、31 分钟前
SpringBoot + HttpSession 自定义生成sessionId
java·开发语言
珊珊来吃36 分钟前
EXCEL中给某一列数据加上双引号
java·前端·excel
我曾经是个程序员43 分钟前
使用C#生成一张1G大小的空白图片
java·算法·c#
向阳12181 小时前
mybatis SqlSessionFactory
java·mybatis
mask哥1 小时前
算法:LeetCode470_用Rand7()实现Rand10()_java实现
java·开发语言