RecyclerView 专题问题分析与总结(一)

在日常项目中对于RecyclerView的使用很多,但是之前看到鸿洋的RecyclerView的十九问,感觉还有很多问题还是很模糊,没有系统的理解明白的其中原理,甚至有些应用方法都没有深入了解过。我觉得有必要写个RecyclerView的专题好好整理和总结一下;

(一)请说一下RecyclerView?adapter的作用是什么,几个方法是做什么用的?如何理解adapter订阅者模式?

RecyclerView结构如下所示:
  • RecyclerView.Adapter - 处理数据集合并负责绑定视图;
  • ViewHolder - 持有所有的用于绑定数据或者需要操作的View;
  • LayoutManager - 负责摆放视图等相关操作;
  • ItemDecoration - 负责绘制Item附近的分割线;
  • ItemAnimator - 为Item的一般操作添加动画效果,如:增删条目等;
adapter的作用:
  • 根据不同ViewType创建与之相应的的Item-Layout
  • 访问数据集合并将数据绑定到正确的View上
adapter内方法具体描述:
  • public VH onCreateViewHolder(ViewGroup parent, int viewType)
    创建Item视图,并返回相应的ViewHolder
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (mContext == null) {
            mContext = parent.getContext();
        }
        // 加入loading动画效果
        if (rotateAnimation == null) {
            rotateAnimation = AnimationUtils.loadAnimation(mContext, R.anim.loading);
            rotateAnimation.setInterpolator(new LinearInterpolator());
        }
        // 根据不同viewType加载不同viewHolder
        if (viewType == VIEW_TYPE_CONTENT) {
            return new ContentViewHolder(LayoutInflater.from(mContext).inflate(R.layout.record_item, parent, false));
        } else {
            return new FooterViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_footer, parent, false));
        }
    }
  • public void onBindViewHolder(VH holder, int position)
    绑定数据到正确的Item视图上。
    @SuppressLint("SetTextI18n")
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (holder.getItemViewType() == VIEW_TYPE_CONTENT) {
            MyRecordModel.MyRecordItem myRecordModel = mFruitList.get(position);
            //这里必须强制转换
            //如果外层的判断条件改为if(holder instance ContentViewHolder),这里输入holder后会自动转换
            String relPhone = myRecordModel.getRegisterMobile();

            String securityPhoneNum = CebTools.getSecurityPhoneNum(relPhone);
            // 手机号字段
            ((ContentViewHolder) holder).tvPhone.setText(securityPhoneNum);
            // 加入时间字段
            ((ContentViewHolder) holder).tvTime.setText(myRecordModel.getRegisterDate() + "加入");

        } else {
            //Log.d("mytest", "isCanLoadMore: " + isCanLoadMore);
            //根据是否需要更多加载修复
            if (isCanLoadMore) {
                ((FooterViewHolder) holder).showLoading();
            } else {
                ((FooterViewHolder) holder).showTextOnly("没有更多数据");
            }
        }

    }
  • public int getItemCount()
    返回该Adapter所持有的Itme数量
    /*
     * 本例中所有数据加载完毕后还是保留footerView并显示已加载完全,所以footerView一直存在。
     */
    @Override
    public int getItemCount() {
        // 根据isCanLoadMore 进行判断是否拥有footer
        return isCanLoadMore ? mFruitList.size() + 1 : mFruitList.size();
        //return mFruitList.size() + 1;
    }
  • public int getItemViewType(int position)
    用来获取当前项Item(position参数)是哪种类型的布局
    @Override
    public int getItemViewType(int position) {
        // 根据isCanLoadMore 进行判断是否拥有footer
        if (isCanLoadMore) {
            if (position == getItemCount() - 1) {
                return VIEW_TYPE_FOOTER;
            }
            return VIEW_TYPE_CONTENT;
        } else {
            return VIEW_TYPE_CONTENT;
        }

    }
如何理解adapter订阅者模式?
  1. 当时据集合发生改变时,我们通过调用.notifyDataSetChanged(),来刷新列表,因为这样做会触发列表的重绘。
  • 首先看.notifyDataSetChanged()源码
public final void notifyDataSetChanged() {
            this.mObservable.notifyChanged();
 }
  • 接着查看.notifyChanged()源码
    被观察者AdapterDataObservable,内部持有观察者AdapterDataObserver集合。
static class AdapterDataObservable extends Observable<RecyclerView.AdapterDataObserver> {
        AdapterDataObservable() {
        }

        public boolean hasObservers() {
            return !this.mObservers.isEmpty();
        }

        public void notifyChanged() {
            for(int i = this.mObservers.size() - 1; i >= 0; --i) {
                ((RecyclerView.AdapterDataObserver)this.mObservers.get(i)).onChanged();
            }

        }

        public void notifyItemRangeChanged(int positionStart, int itemCount) {
            this.notifyItemRangeChanged(positionStart, itemCount, (Object)null);
        }

        public void notifyItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
            for(int i = this.mObservers.size() - 1; i >= 0; --i) {
                ((RecyclerView.AdapterDataObserver)this.mObservers.get(i)).onItemRangeChanged(positionStart, itemCount, payload);
            }

        }

        public void notifyItemRangeInserted(int positionStart, int itemCount) {
            for(int i = this.mObservers.size() - 1; i >= 0; --i) {
                ((RecyclerView.AdapterDataObserver)this.mObservers.get(i)).onItemRangeInserted(positionStart, itemCount);
            }

        }

        public void notifyItemRangeRemoved(int positionStart, int itemCount) {
            for(int i = this.mObservers.size() - 1; i >= 0; --i) {
                ((RecyclerView.AdapterDataObserver)this.mObservers.get(i)).onItemRangeRemoved(positionStart, itemCount);
            }

        }

        public void notifyItemMoved(int fromPosition, int toPosition) {
            for(int i = this.mObservers.size() - 1; i >= 0; --i) {
                ((RecyclerView.AdapterDataObserver)this.mObservers.get(i)).onItemRangeMoved(fromPosition, toPosition, 1);
            }

        }
  }

观察者AdapterDataObserver,具体实现为RecyclerViewDataObserver,当数据源发生变更时,及时响应界面变化。

public static abstract class AdapterDataObserver {
    public void onChanged() {
        // Do nothing
    }
    public void onItemRangeChanged(int positionStart, int itemCount) {
        // do nothing
    }
    public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
        onItemRangeChanged(positionStart, itemCount);
    }

}
  • 接着查看setAdapter()源码中的setAdapterInternal(adapter, false, true)方法
    setAdapter源码:
public void setAdapter(Adapter adapter) {
    // bail out if layout is frozen
    setLayoutFrozen(false);
    setAdapterInternal(adapter, false, true);
    requestLayout();
}

setAdapterInternal(adapter, false, true)源码:

private void setAdapterInternal(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) {
        //注册一个观察者RecyclerViewDataObserver
        adapter.registerAdapterDataObserver(mObserver);
        adapter.onAttachedToRecyclerView(this);
    }
    if (mLayout != null) {
        mLayout.onAdapterChanged(oldAdapter, mAdapter);
    }
    mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
    mState.mStructureChanged = true;
    markKnownViewsInvalid();
}
  • .notifyChanged()被调用,刷新数据
    当数据变更时,调用notify**方法时,Adapter内部的被观察者会遍历通知已经注册的观察者的对应方法,这时界面就会响应变更。

转载于:https://www.jianshu.com/p/f1d1b2dd88a0

猜你喜欢

转载自blog.csdn.net/weixin_34258078/article/details/91073973
今日推荐