用法
普通布局BaseQuickAdapter
BaseQuickAdapter homeAdapter = new BaseQuickAdapter(R.layout.home_item_view, mDataList){
@Override
protected void convert(BaseViewHolder helper, HomeItem item) {
helper.setText(R.id.text, item.getTitle());
helper.setImageResource(R.id.icon, item.getImageResource());
}
};
多布局BaseMultiItemQuickAdapter
数据源类型需要继承自MultiItemEntity,MultiItemEntity是一个接口
原理
BaseQuickAdapter实例化第一步当然是调用我们的构造方法:
public BaseQuickAdapter(int layoutResId, List data) {
this.mData = data == null ? new ArrayList() : data;
if (layoutResId != 0) {
this.mLayoutResId = layoutResId;
}
}
public BaseQuickAdapter(List data) {
this(0, data);
}
public BaseQuickAdapter(int layoutResId) {
this(layoutResId, null);
}
getItemViewType
@Override
public int getItemViewType(int position) {
if (getEmptyViewCount() == 1) {
boolean header = mHeadAndEmptyEnable && getHeaderLayoutCount() != 0;
switch (position) {
case 0:
if (header) {
return HEADER_VIEW;
} else {
return EMPTY_VIEW;
}
case 1:
if (header) {
return EMPTY_VIEW;
} else {
return FOOTER_VIEW;
}
case 2:
return FOOTER_VIEW;
default:
return EMPTY_VIEW;
}
}
//当RecyclerView在渲染一个新的itemView时,就会判断是不是需要调用加载更多回调,需要就调用
autoLoadMore(position);
int numHeaders = getHeaderLayoutCount();
if (position < numHeaders) {
return HEADER_VIEW;
} else {
int adjPosition = position - numHeaders;
int adapterCount = mData.size();
if (adjPosition < adapterCount) {
return getDefItemViewType(adjPosition);
} else {
adjPosition = adjPosition - adapterCount;
int numFooters = getFooterLayoutCount();
if (adjPosition < numFooters) {
return FOOTER_VIEW;
} else {
return LOADING_VIEW;
}
}
}
}
根据我们data的index值以及我们是否开启空视图之类的数据来决定在onCreateViewHolder中应该返回什么类型的viewHolder。
onCreateViewHolder
@Override
public K onCreateViewHolder(ViewGroup parent, int viewType) {
K baseViewHolder = null;
this.mContext = parent.getContext();
this.mLayoutInflater = LayoutInflater.from(mContext);
switch (viewType) {
case LOADING_VIEW:
baseViewHolder = getLoadingView(parent);
break;
case HEADER_VIEW:
baseViewHolder = createBaseViewHolder(mHeaderLayout);
break;
case EMPTY_VIEW:
baseViewHolder = createBaseViewHolder(mEmptyLayout);
break;
case FOOTER_VIEW:
baseViewHolder = createBaseViewHolder(mFooterLayout);
break;
default:
//创建BaseViewHolder或者其拓展的类
baseViewHolder = onCreateDefViewHolder(parent, viewType);
bindViewClickListener(baseViewHolder);
}
baseViewHolder.setAdapter(this);
return baseViewHolder;
}
protected K createBaseViewHolder(ViewGroup parent, int layoutResId) {
return createBaseViewHolder(getItemView(layoutResId, parent));
}
/**
* if you want to use subclass of BaseViewHolder in the adapter,
* you must override the method to create new ViewHolder.
*
* @param view view
* @return new ViewHolder
*/
protected K createBaseViewHolder(View view) {
Class temp = getClass();
Class z = null;
while (z == null && null != temp) {
z = getInstancedGenericKClass(temp);
temp = temp.getSuperclass();
}
K k = createGenericKInstance(z, view);
return null != k ? k : (K) new BaseViewHolder(view);
}
/**
* try to create Generic K instance
*
* @param z
* @param view
* @return
*/
private K createGenericKInstance(Class z, View view) {
try {
Constructor constructor;
String buffer = Modifier.toString(z.getModifiers());
String className = z.getName();
// inner and unstatic class
if (className.contains("$") && !buffer.contains("static")) {
constructor = z.getDeclaredConstructor(getClass(), View.class);
return (K) constructor.newInstance(this, view);
} else {
constructor = z.getDeclaredConstructor(View.class);
return (K) constructor.newInstance(view);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
getLayoutPosition
注意这里使用了ViewHolder的getLayoutPosition方法,此方法返回的pos值与onBindViewHolder方法传入的position值有可能不同。
注:获取的位置和用户目前在屏幕上看到的是一致的.
onBindViewHolder
/**
* To bind different types of holder and solve different the bind events
*
* @param holder
* @param positions
* @see #getDefItemViewType(int)
*/
@Override
public void onBindViewHolder(K holder, int positions) {
int viewType = holder.getItemViewType();
switch (viewType) {
case 0:
convert(holder, mData.get(holder.getLayoutPosition() - getHeaderLayoutCount()));
break;
case LOADING_VIEW:
mLoadMoreView.convert(holder);
break;
case HEADER_VIEW:
break;
case EMPTY_VIEW:
break;
case FOOTER_VIEW:
break;
default:
convert(holder, mData.get(holder.getLayoutPosition() - getHeaderLayoutCount()));
break;
}
}
添加Header和Footer
默认的recycleview是没有addheaderView和addfooterview的,需要自定义
添加header(footer原理一样)
//添加Header对应的View
View view = getLayoutInflater().inflate(R.layout.head_view, (ViewGroup) mRecyclerView.getParent(), false);
//添加Header对应的点击事件
view.setOnClickListener(listener);
//调用BaseQuickAdapter
headerAndFooterAdapter.addHeaderView(headerView);
//(0,header.child)属于正常,其他情况下默认添加到head末尾
public int addHeaderView(View header) {
return addHeaderView(header, -1);
}
public int addHeaderView(View header, int index) {
return addHeaderView(header, index, LinearLayout.VERTICAL);
}
public int addHeaderView(View header, int index, int orientation) {
//如果没有header的话就新增一个HeaderLayout
if (mHeaderLayout == null) {
mHeaderLayout = new LinearLayout(header.getContext());
if (orientation == LinearLayout.VERTICAL) {
mHeaderLayout.setOrientation(LinearLayout.VERTICAL);
mHeaderLayout.setLayoutParams(new LayoutParams(MATCH_PARENT, WRAP_CONTENT));
} else {
mHeaderLayout.setOrientation(LinearLayout.HORIZONTAL);
mHeaderLayout.setLayoutParams(new LayoutParams(WRAP_CONTENT, MATCH_PARENT));
}
}
//获取mHeaderLayout中的header个数
final int childCount = mHeaderLayout.getChildCount();
//如果index的小于0的话,表示插入在最前面index置为view的个数
//如果index大于childcount,index置为子view的个数
//校验index数据的正确性(0,index)往中间插入
if (index < 0 || index > childCount) {
index = childCount;
}
//将数据插入到headerlayout中
mHeaderLayout.addView(header, index);
//如果有一个header的话
if (mHeaderLayout.getChildCount() == 1) {
//获取新增的header的位置position为0
int position = getHeaderViewPosition();
if (position != -1) {
//通知插入一个数据位置0处
notifyItemInserted(position);
}
}
return index;
}
加载更多
使用
pullToRefreshAdapter.setOnLoadMoreListener(this, mRecyclerView);
pullToRefreshAdapter.setLoadMoreView(new CustomLoadMoreView());
pullToRefreshAdapter.setEnableLoadMore(true);
然后在网络请求,
请求成功的话调用loadMoreComplete()
将数据加载到RecycleView中去
请求失败的话调用loadMoreFail()
@Override
public void onLoadMoreRequested() {
//加载更多的请求
mSwipeRefreshLayout.setEnabled(false);
if (pullToRefreshAdapter.getData().size() < PAGE_SIZE) {
pullToRefreshAdapter.loadMoreEnd(true);
} else {
if (mCurrentCounter >= TOTAL_COUNTER) {
pullToRefreshAdapter.loadMoreEnd(mLoadMoreEndGone);//true is gone,false is visible
} else {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
pullToRefreshAdapter.addData(DataServer.getSampleData(PAGE_SIZE));
mCurrentCounter = pullToRefreshAdapter.getData().size();
pullToRefreshAdapter.loadMoreComplete();
mSwipeRefreshLayout.setEnabled(true);
}
},1000);
}
}
}
原理分析
添加loadermoreview项
/**
* Set the enabled state of load more.
*
* @param enable True if load more is enabled, false otherwise.
*/
public void setEnableLoadMore(boolean enable) {
int oldLoadMoreCount = getLoadMoreViewCount();
mLoadMoreEnable = enable;
int newLoadMoreCount = getLoadMoreViewCount();
//oldLoadMoreCount 代表在改变这个开关时我们是否处于显示上拉加载的view的状态,1表示处于该状态。
//newLoadMoreCount 代表我们当前是否可以开启上拉加载功能,同样,1表示可以。
if (oldLoadMoreCount == 1) {
//加入当前处于显示加载更多view的状态,移除加载更多view。
if (newLoadMoreCount == 0) {
notifyItemRemoved(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
}
} else {
//开启上拉加载
if (newLoadMoreCount == 1) {
mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT);
notifyItemInserted(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
}
}
}
当绘制view的时候当绘制到底部的时候会添加回调
显示LoadingView
private void autoLoadMore(int position) {
//只有开启了上拉加载且loadMoreView没有gone且data.size>0 ,且设置了mRequestLoadMoreListener时返回1
if (getLoadMoreViewCount() == 0) {
return;
}
/*
理解起来大概是这样的,mAutoLoadMoreSize是标识开启自动加载更多的一个数量阀值。这个返回很巧妙。
假设你的data.size =20 ,mAutoLoadMoreSize =10,当前position=9, 按照理解,这个pisition=9是个临界值,因为我们设置了剩余数量<10个时自动加载更多,此时计算9<20-10,position等于9,说明后面还有10个数据没渲染,当position=10时(未加载数据还剩9个,此时应该预加载更多),10<20-10,不成立,代码继续往下走,
执行
*/
if (position < getItemCount() - mAutoLoadMoreSize) {
return;
}
//当你快速上滑时,由于position>=10后满足条件,执行加载更多的回调,position=11时也会执行,以此类推,那么你将收到多次加载更多的回调。所以我们需要判断此时是否当前的加载更能多回调已完成,保证每次到达阀值后只调用一次加载更多回调方法。
if (mLoadMoreView.getLoadMoreStatus() != LoadMoreView.STATUS_DEFAULT) {
return;
}
mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_LOADING);
if (!mLoading) {
mLoading = true;
if (getRecyclerView() != null) {
getRecyclerView().post(new Runnable() {
@Override
public void run() {
mRequestLoadMoreListener.onLoadMoreRequested();
}
});
} else {
mRequestLoadMoreListener.onLoadMoreRequested();
}
}
}
加载失败调用,可能你有需求在加载失败后要显示一个加载失败的view提示用户,而不是直接关闭loadMoreView。此时你可以调用该方法。
/**
* Refresh failed
*/
public void loadMoreFail() {
if (getLoadMoreViewCount() == 0) {
return;
}
mLoading = false;
mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_FAIL);
notifyItemChanged(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
}
/**
* Refresh end, no more data
*
* @param gone if true gone the load more view
*/
public void loadMoreEnd(boolean gone) {
if (getLoadMoreViewCount() == 0) {
return;
}
mLoading = false;
mNextLoadEnable = false;
mLoadMoreView.setLoadMoreEndGone(gone);
if (gone) {
notifyItemRemoved(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
} else {
mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_END);
notifyItemChanged(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
}
}
EmptyView设置
调用setEmptyView()
源码
public void setEmptyView(View emptyView) {
boolean insert = false;
if (mEmptyLayout == null) {
mEmptyLayout = new FrameLayout(emptyView.getContext());
final LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
final ViewGroup.LayoutParams lp = emptyView.getLayoutParams();
if (lp != null) {
layoutParams.width = lp.width;
layoutParams.height = lp.height;
}
mEmptyLayout.setLayoutParams(layoutParams);
insert = true;
}
mEmptyLayout.removeAllViews();
mEmptyLayout.addView(emptyView);
mIsUseEmpty = true;
if (insert) {
if (getEmptyViewCount() == 1) {
int position = 0;
if (mHeadAndEmptyEnable && getHeaderLayoutCount() != 0) {
position++;
}
notifyItemInserted(position);
}
}
}
mEmptyLayout赋值
应该只有空数据(EmptyView,Header,Footer等等)
会执行getItemViewType来显示EmptyView
if (getEmptyViewCount() == 1) {
boolean header = mHeadAndEmptyEnable && getHeaderLayoutCount() != 0;
switch (position) {
case 0:
if (header) {
return HEADER_VIEW;
} else {
return EMPTY_VIEW;
}
case 1:
if (header) {
return EMPTY_VIEW;
} else {
return FOOTER_VIEW;
}
case 2:
return FOOTER_VIEW;
default:
return EMPTY_VIEW;
}
}
当有数据的时候
getEmptyViewCount()就为0,不会显示Empty数据
BaseMultiItemQuickAdapter多布局实现
使用
实体必须继承自MultiItemEntity
然后添加类型
addItemType(MultipleItem.TEXT, R.layout.item_text_view);
addItemType(MultipleItem.IMG, R.layout.item_image_view);
addItemType(MultipleItem.IMG_TEXT, R.layout.item_img_text_view);
在getItemViewType中会
protected int getDefItemViewType(int position) {
if (mMultiTypeDelegate != null) {
return mMultiTypeDelegate.getDefItemViewType(mData, position);
}
return super.getItemViewType(position);
}
返回类型type
在onCreateViewHolder中会调用
protected K onCreateDefViewHolder(ViewGroup parent, int viewType) {
int layoutId = mLayoutResId;
if (mMultiTypeDelegate != null) {
layoutId = mMultiTypeDelegate.getLayoutId(viewType);
}
return createBaseViewHolder(parent, layoutId);
}
根据viewtype获取到不同的layout
使用的是SparseArray
protected K createBaseViewHolder(ViewGroup parent, int layoutResId) {
return createBaseViewHolder(getItemView(layoutResId, parent));
}
点击事件
SimpleClickListener实现
注:点击事件的实现有多重方式,如:
1,使用 RecyclerView提供的 addOnItemTouchListener()实现
2,创建 ItemView时添加点击事件监听
3,在 ItemView attach RecyclerView时实现
根据SDK中的解释,在Recyclerview 进行添加、移除item等操作时,position位置可能会变化,而所有的adapter的刷新并不总是及时的,只有这个方法返回的才是当前item经过一些变换后所处的真正位置。
使用
adapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
@Override
public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
Log.d(TAG, "onItemClick: ");
Toast.makeText(ItemClickActivity.this, "onItemClick" + position, Toast.LENGTH_SHORT).show();
}
});
源码
/**
* Interface definition for a callback to be invoked when an itemchild in this
* view has been clicked
*/
public interface OnItemChildClickListener {
/**
* callback method to be invoked when an item in this view has been
* click and held
*
* @param view The view whihin the ItemView that was clicked
* @param position The position of the view int the adapter
*/
void onItemChildClick(BaseQuickAdapter adapter, View view, int position);
}
/**
* Interface definition for a callback to be invoked when an childView in this
* view has been clicked and held.
*/
public interface OnItemChildLongClickListener {
/**
* callback method to be invoked when an item in this view has been
* click and held
*
* @param view The childView whihin the itemView that was clicked and held.
* @param position The position of the view int the adapter
* @return true if the callback consumed the long click ,false otherwise
*/
boolean onItemChildLongClick(BaseQuickAdapter adapter, View view, int position);
}
/**
* Interface definition for a callback to be invoked when an item in this
* view has been clicked and held.
*/
public interface OnItemLongClickListener {
/**
* callback method to be invoked when an item in this view has been
* click and held
*
* @param adapter the adpater
* @param view The view whihin the RecyclerView that was clicked and held.
* @param position The position of the view int the adapter
* @return true if the callback consumed the long click ,false otherwise
*/
boolean onItemLongClick(BaseQuickAdapter adapter, View view, int position);
}
/**
* Interface definition for a callback to be invoked when an item in this
* RecyclerView itemView has been clicked.
*/
public interface OnItemClickListener {
/**
* Callback method to be invoked when an item in this RecyclerView has
* been clicked.
*
* @param adapter the adpater
* @param view The itemView within the RecyclerView that was clicked (this
* will be a view provided by the adapter)
* @param position The position of the view in the adapter.
*/
void onItemClick(BaseQuickAdapter adapter, View view, int position);
}
/**
* Register a callback to be invoked when an item in this RecyclerView has
* been clicked.
*
* @param listener The callback that will be invoked.
*/
public void setOnItemClickListener(@Nullable OnItemClickListener listener) {
mOnItemClickListener = listener;
}
/**
* Register a callback to be invoked when an itemchild in View has
* been clicked
*
* @param listener The callback that will run
*/
public void setOnItemChildClickListener(OnItemChildClickListener listener) {
mOnItemChildClickListener = listener;
}
/**
* Register a callback to be invoked when an item in this RecyclerView has
* been long clicked and held
*
* @param listener The callback that will run
*/
public void setOnItemLongClickListener(OnItemLongClickListener listener) {
mOnItemLongClickListener = listener;
}
/**
* Register a callback to be invoked when an itemchild in this View has
* been long clicked and held
*
* @param listener The callback that will run
*/
public void setOnItemChildLongClickListener(OnItemChildLongClickListener listener) {
mOnItemChildLongClickListener = listener;
}
/**
* @return The callback to be invoked with an item in this RecyclerView has
* been long clicked and held, or null id no callback as been set.
*/
public final OnItemLongClickListener getOnItemLongClickListener() {
return mOnItemLongClickListener;
}
/**
* @return The callback to be invoked with an item in this RecyclerView has
* been clicked and held, or null id no callback as been set.
*/
public final OnItemClickListener getOnItemClickListener() {
return mOnItemClickListener;
}
/**
* @return The callback to be invoked with an itemchild in this RecyclerView has
* been clicked, or null id no callback has been set.
*/
@Nullable
public final OnItemChildClickListener getOnItemChildClickListener() {
return mOnItemChildClickListener;
}
/**
* @return The callback to be invoked with an itemChild in this RecyclerView has
* been long clicked, or null id no callback has been set.
*/
@Nullable
public final OnItemChildLongClickListener getmOnItemChildLongClickListener() {
return mOnItemChildLongClickListener;
}
设置了4个接口的回调
在onCreateViewHolder方法中回调了
bindViewClickListener(baseViewHolder);
设置单击和回调事件
private void bindViewClickListener(final BaseViewHolder baseViewHolder) {
if (baseViewHolder == null) {
return;
}
final View view = baseViewHolder.getConvertView();
if (view == null) {
return;
}
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (getOnItemClickListener() != null && baseViewHolder != null) {
getOnItemClickListener().onItemClick(BaseQuickAdapter.this, v, baseViewHolder.getLayoutPosition() - getHeaderLayoutCount());
}
}
});
view.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (getOnItemLongClickListener() != null && baseViewHolder != null) {
return getOnItemLongClickListener().onItemLongClick(BaseQuickAdapter.this, v, baseViewHolder.getLayoutPosition() - getHeaderLayoutCount());
}
return false;
}
});
}