1、RecyclerView是什么?
根据Googl给出的说明:“能够在有限的窗口中展示大数据集合的灵活视图”
RecyclerView看上去和我们一直用的ListVIew可以说很是一样,但是我们为什么还要用RecyclerView呢?
首先我们先来看一个场景,由于尺寸限制,用户的设备不能以此性展示所有条目,用户需要上下滚动查看会更多的的条目。滚出可见区域的条目将被回收,并在下一条目可见的时候被复用。
在这个场景可以知道,RecyclertView的缓存可以更大的减少内存的开销和CPU的计算,这意味着我们不用每次都创建新的条目,这样就大大的减少了内存开销和CPU的计算。,有效的降低屏幕的卡顿,保证滑动的顺滑。
说了半天你们会说还是没有说明白和LIstView的区别,毕竟我们用ListView很长一段时间了,其实在应用RecyclerView的时候,RecyclerView只管回收和复用View,其他的我们自己去设置。可以根据开发者的需要去设置任何样式的的动画和布局View,可以很轻松的实现LsitView和GirdView,瀑布流的效果。
2、引入RecyclerView
我们在应用RecyclerView的时候,需要添加依赖。
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:recyclerview-v7:25.3.1'
在添加依赖的时候这两个依赖包的版本号要统一,否则会报错。
3、在布局文件中应用RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
4、使用RecyclerView
我们先来了解一下RecyclerView中几个重要的类,都是内部类
Class |
功能 |
Adapter |
处理数据集合并负责绑定数据 |
ViewHolder |
持有所有的用于绑定数据或者需要操作的View |
LayoutManager |
负责摆放视图等相关操作 |
ItemDecoration |
负责绘制Item附近的分割线 |
ItemAnimator |
为Item的一般操作添加动画效果 |
4.1RecyclerView.ViewHolder
这里的ViewHolder和ListVIew里面的ViewHolder差不多是一样的,但是在ListView中,并没有强制我们使用ViewHolder,但是在RecyclerView中,Google强制我们必须使用ViewHolder这种模式。(不了解ViewHolder可以去查一下,网上有很多说明,面试重点)
在RecycleView中的ViewHolder子类可以通过访问公共成员itemView来访问ViewHolder的根视图,所以不需要再ViewHolder子类中存储。
public static class MyViewHolder extends RecyclerView.ViewHolder {
TextView tv;
public MyViewHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.tv);
}
}
4.2、Adapter
adapter有两个功能。一.根据不同Viewtype创建与之相应的Item-Layout,二.访问数据集合并将数据绑定到正确的View上。在实现这两个方面的时候,需要我们重写三个方法:
。public VH onCreateViewHolder(ViewGroup parent,int ViewType)创建Item视图,并返回相应的ViewHolder
。public void onBindViewHolder(VH holder,int position)绑定数据到正确的Item视图上
。public int getItemCount()返回该Adapter所持有的item数量
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
MyViewHolder holder = new MyViewHolder(LayoutInflater.from(context)
.inflate(R.layout.item_recycler_view, parent, false));
return holder;
}
@Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
holder.tv.setText(mDatas.get(position));
}
@Override
public int getItemCount() {
return mDatas.size();
}
4.3、RecyclerView.LayoutManager
LayoutManager的职责是摆放Item的位置,并且负责决定何时回收和重用item。他有一个默认的实现:LinearLayoutManager,他可以用于垂直和水平列表。RecycleVIew.LayoutManager是一个抽象类,RecycleView提供了三个实现类:
1、LinearlayoutManager现行管理器,支持横向,纵向。
2、GridLayoutManager网格布局管理器
3、StaggeredGridLayoutManager瀑布流式布局管理器
4.3.1LinearlayoutManager
LinearlayoutManager是LayoutManager的默认实现。可以使用这个类来创建垂直或水平列表
设置纵向布局
LinearLayoutManager layoutManager= new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
设置横向布局
LinearLayoutManager layoutManager= new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(layoutManager);
在LinearLayoutManager中还有一些很实用的API:
。findFirstVisibleItemPosition()返回当前第一个可见的Item的position
。findLastVisibleItemPosition()返回当前最后一个可见的Item的position
。findFirstCompletelyVisibleItemPosition()返回当前的第一个完全可见的Item的position
。findLastCompletelyVisibleItemPosition()返回当前最后一个完全可见Item的position
剩下的两个RecycleView的实现类就不多说了,大家可以查看官方文档,很简单。
4.4、RecyclerView.ItemDecoration
设置:
// 给纵向显示RecyclerView设置分割线
recyclerView.addItemDecoration(new DividerItemDecoration(activity, DividerItemDecoration.VERTICAL));
// 给横向显示RecyclerView设置分割线
recyclerView.addItemDecoration(new DividerItemDecoration(activity,DividerItemDecoration.HORIZONTAL));
当然,我们也可以对RecyclerView设置多个ItemDecoration,列表展示的时候会遍历所有的ItemDecoration并调用里面的绘制方法,对Item进行装饰。
RecyclerView.ItemDecoration也是一个抽象类,重写他的三个方法来实现Item之间的偏移量或者装饰效果:
。public void onDraw(Canvas c,RecyclerView parent)装饰的绘制在Item条目绘制之前调用,所以这有可能被Item的内容所遮挡
。public void onDrawOver(Canvas c,RecyclerView parent)装饰的绘制在Item条目绘制之后调用,因此装饰奖浮于Item之上
。public void getItemOffsets(Rect outRect,int itemPositon,RecyclerView parent)和padding或者margin类似,LayoutManager在测量阶段会调用这个方法,计算出每一个Item的正确尺寸并设置偏移量。
4.5、RecyclerView.ItemAnimator
ItemAnimator能够帮助Item实现独立的动画。我们可以通过以下代码为Item增加动画效果:
recyclerView.setItemAnimator(new DefaultItemAnimator());
在之前我们开发过程中,表中的集合发生改变的时候,我们通过调用.notifyDataSetChanged()来刷新列表,这样做会触发列表重绘,所以并不会出现任何动画效果,因此需要调用一些以notifyItem*()最为作为前缀的特殊方法。
。public final void notifyItemInserted(int position)向指定位置插入Item
。public final void notifyItemRemoved(int position)移除指定位置的Item
。public final void notifyItemChanged(int position)更新指定位置Item
例:
btnAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
adapter.addData(1);
}
});
btnRemove.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
adapter.removeData(2);
}
});
在适配器中添加两个方法:
public void addData(int position) {
mDatas.add(position, "新添加的");
mHeights.add( (int) (100 + Math.random() * 300));
notifyItemInserted(position);
}
public void removeData(int position) {
mDatas.remove(position);
notifyItemRemoved(position);
}
下面给大家上一下全部代码:
public class MainActivity extends Activity {
private Activity activity;
private Button btnAdd,btnRemove;
private RecyclerView recyclerView;
private RecyclerAdapter adapter;
private List mDatas = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
activity = this;
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
btnAdd = (Button) findViewById(R.id.btn_add);
btnRemove = (Button) findViewById(R.id.btn_remove);
initDatas();
adapter = new RecyclerAdapter(activity,mDatas);
// 设置RecyclerView布局方式为纵向布局
// LinearLayoutManager layoutManager= new LinearLayoutManager(this);
// layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
// recyclerView.setLayoutManager(layoutManager);
// 设置RecyclerView布局方式为横向布局
// LinearLayoutManager layoutManager= new LinearLayoutManager(this);
// layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
// recyclerView.setLayoutManager(layoutManager);
// 设置纵向的4列的表格布局
// GridLayoutManager layoutManager = new GridLayoutManager(this,4,GridLayoutManager.VERTICAL,false);
// recyclerView.setLayoutManager(layoutManager);
// recyclerView.addItemDecoration(new DividerGridItemDecoration(activity));l
// 设置横向的3行的表格布局
// GridLayoutManager layoutManager = new GridLayoutManager(this,3,GridLayoutManager.HORIZONTAL,false);
// recyclerView.setLayoutManager(layoutManager);
// 给纵向显示RecyclerView设置分割线
// recyclerView.addItemDecoration(new DividerItemDecoration(activity, DividerItemDecoration.VERTICAL));
// 给横向显示RecyclerView设置分割线
// recyclerView.addItemDecoration(new DividerItemDecoration(activity,DividerItemDecoration.HORIZONTAL));
// 设置纵向的瀑布流布局
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(4,StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
// 给RecyclerView设置默认动画效果
recyclerView.setItemAnimator(new DefaultItemAnimator());
adapter.setLitener(new RecyclerAdapter.OnItemClickLitener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(activity,"第"+(position+1)+"项被点击了",Toast.LENGTH_SHORT).show();
}
@Override
public void onItemLongClick(View view, int position) {
Toast.makeText(activity,"第"+(position+1)+"项被长按了",Toast.LENGTH_SHORT).show();
}
});
recyclerView.setAdapter(adapter);
btnAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
adapter.addData(1);
}
});
btnRemove.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
adapter.removeData(2);
}
});
}
private void initDatas(){
for (int i = 0;i<50;i++){
mDatas.add(String.valueOf(i+1));
}
}
}
public class RecyclerAdapter extends RecyclerView.Adapter {
private Context context;
private List mDatas;
private List mHeights;
public RecyclerAdapter(Context context, List mDatas) {
this.context = context;
this.mDatas = mDatas;
mHeights = new ArrayList();
for (int i = 0; i < mDatas.size(); i++) {
mHeights.add((int) (100 + Math.random() * 300));
}
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
MyViewHolder holder = new MyViewHolder(LayoutInflater.from(context)
.inflate(R.layout.item_recycler_view, parent, false));
return holder;
}
@Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
ViewGroup.LayoutParams lp = holder.tv.getLayoutParams();
lp.height = mHeights.get(position);
holder.tv.setLayoutParams(lp);
holder.tv.setText(mDatas.get(position));
// 如果设置了回调,则响应点击事件
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (litener != null) {
litener.onItemClick(v, position);
}
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (litener != null) {
litener.onItemLongClick(v, position);
}
return false;
}
});
}
@Override
public int getItemCount() {
return mDatas == null ? 0 : mDatas.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder {
TextView tv;
public MyViewHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.tv);
}
}
public void addData(int position) {
mDatas.add(position, "新添加的");
mHeights.add( (int) (100 + Math.random() * 300));
notifyItemInserted(position);
}
public void removeData(int position) {
mDatas.remove(position);
notifyItemRemoved(position);
}
public interface OnItemClickLitener {
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
private OnItemClickLitener litener;
public void setLitener(OnItemClickLitener litener) {
this.litener = litener;
}
}
public class DividerGridItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
private Drawable mDivider;
public DividerGridItemDecoration(Context context) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
}
@Override
public void onDraw(Canvas c, RecyclerView parent, State state) {
drawHorizontal(c, parent);
drawVertical(c, parent);
}
private int getSpanCount(RecyclerView parent) {
// 列数
int spanCount = -1;
LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
spanCount = ((StaggeredGridLayoutManager) layoutManager)
.getSpanCount();
}
return spanCount;
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getLeft() - params.leftMargin;
final int right = child.getRight() + params.rightMargin
+ mDivider.getIntrinsicWidth();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawVertical(Canvas c, RecyclerView parent) {
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getTop() - params.topMargin;
final int bottom = child.getBottom() + params.bottomMargin;
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicWidth();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
private boolean isLastColum(RecyclerView parent, int pos, int spanCount,
int childCount) {
LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边
{
return true;
}
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
int orientation = ((StaggeredGridLayoutManager) layoutManager)
.getOrientation();
if (orientation == StaggeredGridLayoutManager.VERTICAL) {
if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边
{
return true;
}
} else {
childCount = childCount - childCount % spanCount;
if (pos >= childCount)// 如果是最后一列,则不需要绘制右边
return true;
}
}
return false;
}
private boolean isLastRaw(RecyclerView parent, int pos, int spanCount,
int childCount) {
LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
childCount = childCount - childCount % spanCount;
if (pos >= childCount)// 如果是最后一行,则不需要绘制底部
return true;
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
int orientation = ((StaggeredGridLayoutManager) layoutManager)
.getOrientation();
// StaggeredGridLayoutManager 且纵向滚动
if (orientation == StaggeredGridLayoutManager.VERTICAL) {
childCount = childCount - childCount % spanCount;
// 如果是最后一行,则不需要绘制底部
if (pos >= childCount)
return true;
} else
// StaggeredGridLayoutManager 且横向滚动
{
// 如果是最后一行,则不需要绘制底部
if ((pos + 1) % spanCount == 0) {
return true;
}
}
}
return false;
}
@Override
public void getItemOffsets(Rect outRect, int itemPosition,
RecyclerView parent) {
int spanCount = getSpanCount(parent);
int childCount = parent.getAdapter().getItemCount();
if (isLastRaw(parent, itemPosition, spanCount, childCount))// 如果是最后一行,则不需要绘制底部
{
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
} else if (isLastColum(parent, itemPosition, spanCount, childCount))// 如果是最后一列,则不需要绘制右边
{
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(),
mDivider.getIntrinsicHeight());
}
}
}