一、RecyclerView是什么
RecyclerView从2014年发布到现在已经很长时间了,使用已经相当普遍。它是support.v7包中的控件,谷歌在高级版本提出一个新的替代ListView、GridView的控件,同样拥有item回收复用的功能。
二、RecyclerView的优点
1.自带了性能优化:ViewHolder
RecylerView封装了viewholder的回收复用,也就是说RecylerView标准化了ViewHolder,编写Adapter面向的是ViewHolder而不再是View了,复用的 逻辑被封装了,写起来更加简单。
2.高度解耦
提供了一种插拔式的体验,高度的解耦,异常的灵活,针对一个Item的显示RecylerView专门抽取出了相应的类,来控制Item的显示,使其的扩展性非常强。例如:你想控制横向或者纵向滑动列表效果可以通过LinearLayoutManager这个类来进行控制(与GridView效果对应的是GridLayoutManager,与瀑布流对应的还有StaggeredGridLayoutManager等),也就是说RecylerView不再拘泥于ListView的线性展示方式,它也可以实现GridView的效果等多种效果。你想控制Item的分隔线,可以通过继承RecylerView的ItemDecoration这个类,然后针对自己的业务需求去抒写代码。
3.动画效果
可以控制Item增删的动画,可以通过ItemAnimator这个类进行控制,当然针对增删的动画,RecylerView有其自己默认的实现。
三、缺点:
1.RecyclerView没有条目点击事件,需要自己写。
2.需要重写addView
四、简单使用
1.布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="${relativePackage}.${activityClass}" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_menu" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recyler_menu"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
2.Activity使用
list = JsonUtil.analyMenu(FileUtil.readAssetFile(this));
adapter = new MyRecyclerAdapter(list);
//recyclerView的样式全部由LayoutManager来控制,实现低耦合
//LayoutManager布局摆放管理器(线性摆放、瀑布流),具体可见源码中的构造方法
recyler_menu.setLayoutManager(new LinearLayoutManager(this));//默认垂直
recyler_menu.setAdapter(adapter);
其中RecyclerView必须要设置LayoutManager,他是布局管理器,可以设置滑动的方向,每一行摆放的数量(GridView的效果)G,这些是设置详细可参考LayoutManager的源码。
3.Adapter:
public class MyRecyclerAdapter extends
RecyclerView.Adapter<MyRecyclerAdapter.MyViewHolder> {
private List<MenuData> list;
public MyRecyclerAdapter(List<MenuData> list) {
// TODO Auto-generated constructor stub
this.list = list;
}
class MyViewHolder extends RecyclerView.ViewHolder {
TextView tv;
public MyViewHolder(View view) {
super(view);
tv = (TextView) view.findViewById(android.R.id.text1);
}
}
@Override
public int getItemCount() {
// TODO Auto-generated method stub
return list.size();
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
// 绑定数据
holder.tv.setText(list.get(position).getModuleName());
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int arg1) {
// 创建ViewHolder
MyViewHolder holder = new MyViewHolder(View.inflate(
viewGroup.getContext(), android.R.layout.simple_list_item_1,
null));
return holder;
}
}
4.点击事件:
RecyclerView的点击事件(点击、长按)需要通过接口回调实现,在Adapter中定义接口:
public interface OnItemClickListener{
void onItemClick(View view, int position);
}
public void setOnItemClickListener(OnItemClickListener listener){
this.mOnItemClickListener = listener;
}
并在view的点击事件中回调给activity:
@Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
// 绑定数据
holder.tv.setText(list.get(position).getModuleName());
if(mOnItemClickListener!=null){
holder.itemView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mOnItemClickListener.onItemClick(v, position);
}
});
}
}
adapter.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Intent intent=new Intent();
Class deClass = null;
try {
String className=list.get(position).getClassName();
deClass = Class.forName(className);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
intent.setClass(MainActivity.this, deClass);
startActivity(intent);
}
});
五、扩展:
1.封装一个简单的BaseRecyclerAdapter。RecyclerView的通用适配器:
http://blog.csdn.net/lmj623565791/article/details/51118836/
2.解决position可能错位的问题。
3.设置条目长按事件
4.开源的下拉刷新等控件
5.瀑布流
六.RecyclerView的分割线
RecyclerView没有默认的分割线,需要自己绘制。
RecyclerView.ItemDecoration
1)线性的分割线
2)网格的分割线
(1)可以通过修改Theme.Appcompa主题样式里面的android:listSelector或者 android:listDivider属性
达到改变间隔线的大小和颜色哦!(自己尝试下)
<style name="AppTheme" parent="AppBaseTheme">
<item name="android:listDivider">@drawable/item_divider</item>
</style>
(2)写一个条目装饰类,继承RecyclerView.ItemDecoration
decor=new DividerItemDecoration(this, LinearLayoutManager.HORIZONTAL);
recylerview.addItemDecoration(decor);
重写ItemDecoration:
public class DividerItemDecoration extends ItemDecoration {
private int mOrientation = LinearLayoutManager.VERTICAL;
private Drawable mDivider;
private int[] attrs= new int[]{
android.R.attr.listDivider
};
public DividerItemDecoration(Context context, int orientation) {
TypedArray typedArray = context.obtainStyledAttributes(attrs);
mDivider = typedArray.getDrawable(0);
typedArray.recycle();
setOrientation(orientation);
}
public void setOrientation(int orientation){
if(orientation!=LinearLayoutManager.HORIZONTAL&&orientation!=LinearLayoutManager.VERTICAL){
throw new IllegalArgumentException("哥们,逗我ma?非水平和线性的枚举类型");
}
this.mOrientation = orientation;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, State state) {
//2。调用这个绘制方法, RecyclerView会毁掉该绘制方法,需要你自己去绘制条目的间隔线
if(mOrientation == LinearLayoutManager.VERTICAL){//垂直
drawVertical(c,parent);
}else{//水平
drawHorizontal(c,parent);
}
super.onDraw(c, parent, state);
}
private void drawHorizontal(Canvas c, RecyclerView parent) {
int top = parent.getPaddingTop();
int bottom = parent.getHeight() - parent.getPaddingBottom();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount ; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (LayoutParams) child.getLayoutParams();
int left = child.getRight() + params.rightMargin + Math.round(ViewCompat.getTranslationX(child));
int right = left + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top , right, bottom);
mDivider.draw(c);
}
}
private void drawVertical(Canvas c, RecyclerView parent) {
// 画水平线
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount ; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (LayoutParams) child.getLayoutParams();
int top = child.getBottom() + params.bottomMargin + Math.round(ViewCompat.getTranslationY(child));
int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top , right, bottom);
mDivider.draw(c);
}
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
State state) {
//1.调用此方法(首先会先获取条目之间的间隙宽度---Rect矩形区域)
// 获得条目的偏移量(所有的条目都回调用一次该方法)
if(mOrientation == LinearLayoutManager.VERTICAL){//垂直
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
}else{//水平
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0 );
}
}
}
七.RecycleView添加头部和底部
ListView.addHeadView();
ListView.addFooterView();
RecyclerView没有这样的方法,需要自己解决
所以我们通过看ListView的源码学习如何解决这个问题!!
思路
通过偷天换日,其实就是改造RecyclerView和适配器:
(1)给RecyclerView提供添加头部和底部view的方法
public class WrapRecyclerView extends RecyclerView{
private ArrayList<View> mHeaderViewInfos = new ArrayList<View>();
private ArrayList<View> mFooterViewInfos = new ArrayList<View>();
private Adapter mAdapter;
public WrapRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void addHeaderView(View v) {
mHeaderViewInfos.add(v);
// Wrap the adapter if it wasn't already wrapped.
if (mAdapter != null) {
if (!(mAdapter instanceof HeaderViewRecyclerAdapter)) {
mAdapter = new HeaderViewRecyclerAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
}
}
}
public void addFooterView(View v) {
mFooterViewInfos.add(v);
// Wrap the adapter if it wasn't already wrapped.
if (mAdapter != null) {
if (!(mAdapter instanceof HeaderViewRecyclerAdapter)) {
mAdapter = new HeaderViewRecyclerAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
}
}
}
@Override
public void setAdapter(Adapter adapter) {
if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = new HeaderViewRecyclerAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
mAdapter = adapter;
}
super.setAdapter(mAdapter);
}
}
(2)适配器中onCreateViewHolder和getItemViewType、onBindViewHolder进行类型判断并处理。
public class HeaderViewRecyclerAdapter extends Adapter {
private Adapter mAdapter;
ArrayList<View> mHeaderViewInfos;
ArrayList<View> mFooterViewInfos;
public HeaderViewRecyclerAdapter(ArrayList<View> headerViewInfos,
ArrayList<View> footerViewInfos, Adapter adapter) {
mAdapter = adapter;
if (headerViewInfos == null) {
mHeaderViewInfos = new ArrayList<View>();
} else {
mHeaderViewInfos = headerViewInfos;
}
if (footerViewInfos == null) {
mFooterViewInfos = new ArrayList<View>();
} else {
mFooterViewInfos = footerViewInfos;
}
}
@Override
public int getItemCount() {
if (mAdapter != null) {
return getFootersCount() + getHeadersCount() + mAdapter.getItemCount();
} else {
return getFootersCount() + getHeadersCount();
}
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
//也要划分三个区域
int numHeaders = getHeadersCount();
if (position < numHeaders) {//是头部
return ;
}
//adapter body
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mAdapter != null) {
adapterCount = mAdapter.getItemCount();
if (adjPosition < adapterCount) {
mAdapter.onBindViewHolder(holder, adjPosition);
return ;
}
}
//footer
}
@Override
public int getItemViewType(int position) {
//判断当前条目是什么类型的---决定渲染什么视图给什么数据
int numHeaders = getHeadersCount();
if (position < numHeaders) {//是头部
return RecyclerView.INVALID_TYPE;
}
//正常条目部分
// Adapter
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mAdapter != null) {
adapterCount = mAdapter.getItemCount();
if (adjPosition < adapterCount) {
return mAdapter.getItemViewType(adjPosition);
}
}
//footer部分
return RecyclerView.INVALID_TYPE-1;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//header
if(viewType==RecyclerView.INVALID_TYPE){
return new HeaderViewHolder(mHeaderViewInfos.get(0));
}else if(viewType==RecyclerView.INVALID_TYPE-1){//footer
return new HeaderViewHolder(mFooterViewInfos.get(0));
}
// Footer (off-limits positions will throw an IndexOutOfBoundsException)
return mAdapter.onCreateViewHolder(parent, viewType);
}
public int getHeadersCount() {
return mHeaderViewInfos.size();
}
public int getFootersCount() {
return mFooterViewInfos.size();
}
private static class HeaderViewHolder extends ViewHolder{
public HeaderViewHolder(View view) {
super(view);
}
}
}
八.RecycleView交互动画
ItemTouchHelper
回调接口的三个方法:退拽、侧滑、判断事件(通过十六进制来进行与或运算判断状态)
可参考:http://blog.csdn.net/liaoinstan/article/details/51200618
//条目触摸帮助类
ItemTouchHelper.Callback callback = new MyItemTouchHelperCallback(adapter);
itemTouchHelper = new ItemTouchHelper(callback);
itemTouchHelper.attachToRecyclerView(recyclerView);
重写Callback:
public class MyItemTouchHelperCallback extends Callback {
private ItemTouchMoveListener moveListener;
public MyItemTouchHelperCallback(ItemTouchMoveListener moveListener) {
this.moveListener = moveListener;
}
//Callback回调监听时先调用的,用来判断当前是什么动作,比如判断方向(意思就是我要监听哪个方向的拖动)
@Override
public int getMovementFlags(RecyclerView recyclerView, ViewHolder holder) {
//方向:up,down,left,right
//常量:
int up = ItemTouchHelper.UP;//1 0x0001
int down = ItemTouchHelper.DOWN;//2 0x0010
// ItemTouchHelper.LEFT
// ItemTouchHelper.RIGHT
//我要监听的拖拽方向是哪两个方向。
int dragFlags = ItemTouchHelper.UP|ItemTouchHelper.DOWN;
//我要监听的swipe侧滑方向是哪个方向
// int swipeFlags = 0;
int swipeFlags = ItemTouchHelper.LEFT|ItemTouchHelper.RIGHT;
int flags = makeMovementFlags(dragFlags, swipeFlags);
return flags;
}
@Override
public boolean isLongPressDragEnabled() {
// 是否允许长按拖拽效果
return true;
}
//当移动的时候回调的方法--拖拽
@Override
public boolean onMove(RecyclerView recyclerView, ViewHolder srcHolder, ViewHolder targetHolder) {
if(srcHolder.getItemViewType()!=targetHolder.getItemViewType()){
return false;
}
// 在拖拽的过程当中不断地调用adapter.notifyItemMoved(from,to);
boolean result = moveListener.onItemMove(srcHolder.getAdapterPosition(), targetHolder.getAdapterPosition());
return result;
}
//侧滑的时候回调的
@Override
public void onSwiped(ViewHolder holder, int arg1) {
// 监听侧滑,1.删除数据;2.调用adapter.notifyItemRemove(position)
moveListener.onItemRemove(holder.getAdapterPosition());
}
@Override
public void onSelectedChanged(ViewHolder viewHolder, int actionState) {
//判断选中状态
if(actionState!=ItemTouchHelper.ACTION_STATE_IDLE){
viewHolder.itemView.setBackgroundColor(viewHolder.itemView.getContext().getResources().getColor(R.color.colorPrimary_pink));
}
super.onSelectedChanged(viewHolder, actionState);
}
@Override
public void clearView(RecyclerView recyclerView, ViewHolder viewHolder) {
// 恢复
viewHolder.itemView.setBackgroundColor(Color.WHITE);
super.clearView(recyclerView, viewHolder);
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView,
ViewHolder viewHolder, float dX, float dY, int actionState,
boolean isCurrentlyActive) {
//dX:水平方向移动的增量(负:往左;正:往右)范围:0~View.getWidth 0~1
if(actionState==ItemTouchHelper.ACTION_STATE_SWIPE){
//透明度动画
float alpha = 1-Math.abs(dX)/viewHolder.itemView.getWidth();
viewHolder.itemView.setAlpha(alpha);//1~0
viewHolder.itemView.setScaleX(alpha);//1~0
viewHolder.itemView.setScaleY(alpha);//1~0
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState,
isCurrentlyActive);
}
}