RecyclerView是什么
从Android 5.0开始,谷歌公司推出了一个用于大量数据展示的新控件RecylerView,可以用来代替传统的ListView,更加强大和灵活。RecyclerView的官方定义如下:
A flexible view for providing a limited window into a large data set.
RecyclerView是support-v7包中的新组件,是一个强大的滑动组件,与经典的ListView相比,同样拥有item回收复用的功能。
RecyclerView的优点
- RecyclerView封装了viewholder的回收复用,也就是说RecyclerView标准化了ViewHolder,编写Adapter面向的是ViewHolder而不再是View了,复用的逻辑被封装了,写起来更加简单。
直接省去了listview中convertView.setTag(holder)和convertView.getTag()这些繁琐的步骤。 - 提供了一种插拔式的体验,高度的解耦,异常的灵活,针对一个Item的显示RecyclerView专门抽取出了相应的类,来控制Item的显示,使其的扩展性非常强。
- 设置布局管理器以控制Item的布局方式,横向、竖向以及瀑布流方式
例如:你想控制横向或者纵向滑动列表效果可以通过LinearLayoutManager这个类来进行控制(与GridView效果对应的是GridLayoutManager,与瀑布流对应的还StaggeredGridLayoutManager等)。也就是说RecyclerView不再拘泥于ListView的线性展示方式,它也可以实现GridView的效果等多种效果。 - 可设置Item的间隔样式(可绘制)
通过继承RecyclerView的ItemDecoration这个类,然后针对自己的业务需求去书写代码。 - 可以控制Item增删的动画,可以通过ItemAnimator这个类进行控制,当然针对增删的动画,RecyclerView有其自己默认的实现。
创建实例
在build.gradle文件中引入该类:
compile 'com.android.support:recycleview-v7:25.0.1'
private RecyclerView mRecyclerView;
private RecyclerAdapter mAdapter;
LinearLayoutManager layoutManager = new LinearLayoutManager(this );
layoutManager.setOrientation(OrientationHelper.VERTICAL); //设置为垂直布局,这也是默认的
mRecyclerView.setLayoutManager(layoutManager);//设置布局管理器
mAdapter = new RecyclerAdapter(this, dataBeanList);//创建适配器
mRecyclerView.setAdapter(mAdapter);//设置Adapter
mRecyclerView.setItemAnimator();//设置增加或删除条目的动画
mRecyclerView.addItemDecoration();//设置分隔线
//滚动监听
mAdapter.setOnScrollListener(new RecyclerAdapter.OnScrollListener() {
@Override
public void scrollTo(int pos) {
mRecyclerView.scrollToPosition(pos);
}
});
布局
<android.support.v7.widget.RecyclerView
android:id="@+id/recycle_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:paddingTop="5dp"
android:scrollbars="vertical"/>
创建适配器
标准实现步骤如下:
① 创建Adapter:创建一个继承RecyclerView.Adapter<VH>
的Adapter类(VH是ViewHolder的类名)
② 创建ViewHolder:在Adapter中创建一个继承RecyclerView.ViewHolder
的静态内部类,记为VH。
ViewHolder的实现和ListView的ViewHolder实现几乎一样。
③ 在Adapter中实现4个方法:
1.onCreateViewHolder()
这个方法主要生成为每个Item inflater出一个View,但是该方法返回的是一个ViewHolder。该方法把View直接封装在ViewHolder中,然后我们面向的是ViewHolder这个实例,当然这个ViewHolder需要我们自己去编写。
需要注意的是在onCreateViewHolder()
中,映射Layout必须为
view = mInflater.inflate(R.layout.recycleview_item_parent, parent, false);
不能为:view = mInflater.inflate(R.layout.recycleview_item_parent, null);
2.onBindViewHolder()
这个方法主要用于适配渲染数据到View中。方法提供给你viewHolder而不是原来的convertView。
3.getItemCount()
这个方法就类似于BaseAdapter的getCount方法了,即总共有多少个条目。
4.getItemViewType(int position)
这个方法不是必须实现,前三种必须实现,此方法作用可以根据需求自定义类型在onCreateViewHolder和onBindViewHolder做相应处理。如BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType),viewtype即是getItemViewType方法的返回值,onBindViewHolder(BaseViewHolder holder, int position)这个方法可以通过getItemViewType(position)的得到类型并作相应处理。
可以看出,RecyclerView将ListView中getView()
的功能拆分成了onCreateViewHolder()
和onBindViewHolder()
。
RecyclerView.Adapter<VH extend ViewHolder>代码实现如下:
public class RecyclerAdapter extends RecyclerView.Adapter<BaseViewHolder>{
private Context context;
private List<DataBean> dataBeanList;
private LayoutInflater mInflater;
private OnScrollListener mOnScrollListener;
public RecyclerAdapter(Context context, List<DataBean> dataBeanList) {
this.context = context;
this.dataBeanList = dataBeanList;
this.mInflater = LayoutInflater.from(context);
}
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = null;
switch (viewType) {
case DataBean.PARENT_ITEM:
view = mInflater.inflate(R.layout.recycleview_item_parent, parent, false);
return new ParentViewHolder(context, view);
case DataBean.CHILD_ITEM:
view = mInflater.inflate(R.layout.recycleview_item_child, parent, false);
return new ChildViewHolder(context, view);
default:
view = mInflater.inflate(R.layout.recycleview_item_parent, parent, false);
return new ParentViewHolder(context, view);
}
}
/**
* 根据不同的类型绑定View
*
* @param holder
* @param position
*/
@Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
switch (getItemViewType(position)) {
case DataBean.PARENT_ITEM:
ParentViewHolder parentViewHolder = (ParentViewHolder) holder;
parentViewHolder.bindView(dataBeanList.get(position), position, itemClickListener);
break;
case DataBean.CHILD_ITEM:
ChildViewHolder childViewHolder = (ChildViewHolder) holder;
childViewHolder.bindView(dataBeanList.get(position), position);
break;
}
}
@Override
public int getItemCount() {
return dataBeanList.size();
}
@Override
public int getItemViewType(int position) {
return dataBeanList.get(position).getType();
}
private ItemClickListener itemClickListener = new ItemClickListener() {
@Override
public void onExpandChildren(DataBean bean) {
int position = getCurrentPosition(bean.getID());//确定当前点击的item位置
DataBean children = getChildDataBean(bean);//获取要展示的子布局数据对象,注意区分onHideChildren方法中的getChildBean()。
if (children == null) {
return;
}
add(children, position + 1);//在当前的item下方插入
if (position == dataBeanList.size() - 2 && mOnScrollListener != null) { //如果点击的item为最后一个
mOnScrollListener.scrollTo(position + 1);//向下滚动,使子布局能够完全展示
}
}
@Override
public void onHideChildren(DataBean bean) {
int position = getCurrentPosition(bean.getID());//确定当前点击的item位置
DataBean children = bean.getChildBean();//获取子布局对象
if (children == null) {
return;
}
remove(position + 1);//删除
if (mOnScrollListener != null) {
mOnScrollListener.scrollTo(position);
}
}
};
/**
* 在父布局下方插入一条数据
*
* @param bean
* @param position
*/
public void add(DataBean bean, int position) {
dataBeanList.add(position, bean);
notifyItemInserted(position);
}
/**
* 移除子布局数据
*
* @param position
*/
protected void remove(int position) {
dataBeanList.remove(position);
notifyItemRemoved(position);
}
/**
* 确定当前点击的item位置并返回
*
* @param uuid
* @return
*/
protected int getCurrentPosition(String uuid) {
for (int i = 0; i < dataBeanList.size(); i++) {
if (uuid.equalsIgnoreCase(dataBeanList.get(i).getID())) {
return i;
}
}
return -1;
}
/**
* 封装子布局数据对象并返回
* 注意,此处只是重新封装一个DataBean对象,为了标注Type为子布局数据,进而展开,展示数据
* 要和onHideChildren方法里的getChildBean()区分开来
*
* @param bean
* @return
*/
private DataBean getChildDataBean(DataBean bean) {
DataBean child = new DataBean();
child.setType(1);
child.setParentLeftTxt(bean.getParentLeftTxt());
child.setChildLeftTxt(bean.getChildLeftTxt());
child.setChildRightTxt(bean.getChildRightTxt());
child.setListdata(bean.getListdata());
return child;
}
/**
* 滚动监听接口
*/
public interface OnScrollListener {
void scrollTo(int pos);
}
public void setOnScrollListener(OnScrollListener onScrollListener) {
this.mOnScrollListener = onScrollListener;
}
}
BaseViewHolder
public class BaseViewHolder extends RecyclerView.ViewHolder {
public BaseViewHolder(View itemView) {
super(itemView);
}