在日常项目中对于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订阅者模式?
- 当时据集合发生改变时,我们通过调用.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