• 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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    创建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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

    class AdapterDataSetObserver extends AdapterView.AdapterDataSetObserver {
        @Override
        public void onChanged() {
            super.onChanged();
            ......
        }
        ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    调用AdapterView的AdapterDataSetObserver的notifyChanged()

    class AdapterDataSetObserver extends DataSetObserver {
        ......
        @Override
        public void onChanged() {
            mDataChanged = true;
            mOldItemCount = mItemCount;
            mItemCount = getAdapter().getCount();
            ......
            checkFocus();
            requestLayout();
        }
        ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    调用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();
        ......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    调用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;
            ......
            }......
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    调用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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    调用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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    以下面代码为例

     @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;
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 第一次创建时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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    将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();
            }
        }
       	......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    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);
            ......
        }
    	......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    以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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    调用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 payloads) {
        onBindViewHolder(holder, position);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
  • 相关阅读:
    接口抓包分析与mock
    SQL优化--主键查询
    Hive【Hive(五)函数-高级聚合函数、炸裂函数】
    隐私计算综述
    系统学习Python——类(class)代码的编写基础与实例(一):代码的编写基础
    数据结构之手写HashMap
    (一)基于centos7.9安装最新版本kubernetes(1.25.2)
    设计模式(c++)
    Typora+MinIO+Python代码打造舒适协作环境
    嵌入式系统开发笔记103:解决DEV C++程序运行中文乱码问题
  • 原文地址:https://blog.csdn.net/qq_35258036/article/details/133764619