RecyclerView的使用
2016-01-22 17:29
253 查看
RecyclerView的基本知识
适配器
示例代码:public class GalleryAdapter extends RecyclerView.Adapter<GalleryAdapter.ViewHolder> { private LayoutInflater mInflater; private List<Integer> mDatas; public GalleryAdapter(Context context, List<Integer> datats) { mInflater = LayoutInflater.from(context); mDatas = datats; } public static class ViewHolder extends RecyclerView.ViewHolder { public ViewHolder(View arg0) { super(arg0); } ImageView mImg; TextView mTxt; } @Override public int getItemCount() { return mDatas.size(); } /** * 创建ViewHolder */ @Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { View view = mInflater.inflate(R.layout.activity_index_gallery_item, viewGroup, false); ViewHolder viewHolder = new ViewHolder(view); viewHolder.mImg = (ImageView) view .findViewById(R.id.id_index_gallery_item_image); return viewHolder; } /** * 设置值 */ @Override public void onBindViewHolder(final ViewHolder viewHolder, final int i) { viewHolder.mImg.setImageResource(mDatas.get(i)); } }
可以看到数据适配器与BaseAdapter比较发生了相当大的变化,主要有3个方法:
getItemCount 这个不用说,获取总的条目数
onCreateViewHolder 创建ViewHolder
onBindViewHolder 将数据绑定至ViewHolder
可见,RecyclerView对ViewHolder也进行了一定的封装,但是如果你仔细观察,你会发出一个疑问,ListView里面有个getView返回View为Item的布局,那么这个Item的样子在哪控制?
其实是这样的,我们创建的ViewHolder必须继承RecyclerView.ViewHolder,这个RecyclerView.ViewHolder的构造时必须传入一个View,这个View相当于我们ListView getView中的convertView (即:我们需要inflate的item布局需要传入)。
还有一点,ListView中convertView是复用的,在RecyclerView中,是把ViewHolder作为缓存的单位了,然后convertView作为ViewHolder的成员变量保持在ViewHolder中,也就是说,假设没有屏幕显示10个条目,则会创建10个ViewHolder缓存起来,每次复用的是ViewHolder,所以他把getView这个方法变为了onCreateViewHolder。有兴趣的自己打印下Log,测试下。
使用
在使用前必须倒入recyclerView V7包。布局:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".gallery.MainActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/id_recyclerview_horizontal" android:layout_width="match_parent" android:layout_height="120dp" android:background="#FF0000" android:scrollbars="none" ></android.support.v7.widget.RecyclerView> </RelativeLayout>
其item:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/textview" android:layout_width="match_parent" android:layout_height="50dp" android:layout_centerInParent="true" android:text="1" android:gravity="center" android:background="#2200ff00" /> </RelativeLayout>
在Activity中:
//建立适配器 adapter = new ListAdapter(getLayoutInflater(),mDates); LinearLayoutManager manager = new LinearLayoutManager(this); //设置布局管理器 recyclerView.setLayoutManager(manager); //添加分割线 recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST)); //设置Item增加、移除动画 recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.setAdapter(adapter);
设置布局管理器
RecyclerView.LayoutManager吧,这是一个抽象类,好在系统提供了3个实现类:LinearLayoutManager 现行管理器,支持横向、纵向。
LinearLayoutManager manager = new LinearLayoutManager(this); linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);//选择方向
GridLayoutManager 网格布局管理器
GridLayoutManager manager = new GridLayoutManager(this,4);
StaggeredGridLayoutManager 瀑布就式布局管理器
StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);
根据不同的样式选择不同的LayoutManager。
添加分割线
我们可以通过该方法添加分割线:mRecyclerView.addItemDecoration()
该方法的参数为RecyclerView.ItemDecoration,该类为抽象类,官方目前并没有提供默认的实现类.
网上大神实现了两个实现类。
DividerItemDecoration类用于LinearLayoutManager 的分割线:
package com.example.myrecyclerview.list; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; /** * Created by 陈猛 on 2016/1/22. */ public class DividerItemDecoration extends RecyclerView.ItemDecoration { private static final int[] ATTRS = new int[]{ android.R.attr.listDivider }; public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL; public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL; private Drawable mDivider; private int mOrientation; public DividerItemDecoration(Context context, int orientation) { final TypedArray a = context.obtainStyledAttributes(ATTRS); mDivider = a.getDrawable(0); a.recycle(); setOrientation(orientation); } public void setOrientation(int orientation) { if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) { throw new IllegalArgumentException("invalid orientation"); } mOrientation = orientation; } @Override public void onDraw(Canvas c, RecyclerView parent) { // Log.v("recyclerview - itemdecoration", "onDraw()"); if (mOrientation == VERTICAL_LIST) { drawVertical(c, parent); } else { drawHorizontal(c, parent); } } public void drawVertical(Canvas c, RecyclerView parent) { final int left = parent.getPaddingLeft(); final int right = parent.getWidth() - parent.getPaddingRight(); final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext()); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int top = child.getBottom() + params.bottomMargin; final int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } public void drawHorizontal(Canvas c, RecyclerView parent) { final int top = parent.getPaddingTop(); final int bottom = parent.getHeight() - parent.getPaddingBottom(); 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 left = child.getRight() + params.rightMargin; final int right = left + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } @Override public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { if (mOrientation == VERTICAL_LIST) { outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); } else { outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); } } }
该实现类可以看到通过读取系统主题中的 android.R.attr.listDivider作为Item间的分割线,并且支持横向和纵向。
获取到listDivider以后,该属性的值是个Drawable,在getItemOffsets中,outRect去设置了绘制的范围。onDraw中实现了真正的绘制。
我们在原来的代码中添加一句:
mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));
该分割线是系统默认的,你可以在theme.xml中找到该属性的使用情况。那么,使用系统的listDivider有什么好处呢?就是方便我们去随意的改变,该属性我们可以直接声明在:
<!-- Application theme. --> <style name="AppTheme" parent="AppBaseTheme"> <item name="android:listDivider">@drawable/divider_bg</item> </style>
然后自己写个drawable即可,下面我们换一种分隔符:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > <gradient android:centerColor="#ff00ff00" android:endColor="#ff0000ff" android:startColor="#ffff0000" android:type="linear" /> <size android:height="4dp"android:width="1dp"/> </shape>
DividerGridItemDecoration为后两者用的:
package com.example.myrecyclerview.grid; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import android.view.View; /** * Created by 陈猛 on 2016/1/22. */ 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, RecyclerView.State state) { drawHorizontal(c, parent); drawVertical(c, parent); } private int getSpanCount(RecyclerView parent) { // 列数 int spanCount = -1; RecyclerView.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) { RecyclerView.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) { RecyclerView.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()); } } }
设置Item增加、移除动画
temAnimator也是一个抽象类,好在系统为我们提供了一种默认的实现类,期待系统多添加些默认的实现。
借助默认的实现,当Item添加和移除的时候,添加动画效果很简单:
// 设置item动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
注意,这里更新数据集不是用adapter.notifyDataSetChanged()而是
notifyItemInserted(position)与notifyItemRemoved(position)
否则没有动画效果。
上述为adapter中添加了两个方法:
public void addData(int position) { mDatas.add(position, "Insert One"); notifyItemInserted(position); } public void removeData(int position) { mDatas.remove(position); notifyItemRemoved(position); }
点击事件
RecyclerView并没有提供点击事件,我们可以添加一个回调函数在adapter中:public interface OnItemClickListener{ void OnClick(View view,int position); } private OnItemClickListener onItemClickListener; public void setOnItemClickListener(OnItemClickListener onItemClickListener) { this.onItemClickListener = onItemClickListener; } public void onBindViewHolder(ViewHolder holder, final int position) { holder.tv.setText(mDate.get(position)); //为每一个itemView设置点击事件 holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if(onItemClickListener!=null){ onItemClickListener.OnClick(view,position); } } }); }
到此为止其基本的设置就介绍完了,下面的是mode:
List:
ListActivity:package com.example.myrecyclerview.list;
import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import com.example.myrecyclerview.R;
import java.util.ArrayList;
import java.util.List;
/**
* Created by 陈猛 on 2016/1/21.
*/
public class ListActivity extends Activity{
private RecyclerView recyclerView;
private ListAdapter adapter;
private List<String> mDates;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list_activity);
recyclerView = (RecyclerView) findViewById(R.id.list_recyclerview);
initData();
//建立适配器 adapter = new ListAdapter(getLayoutInflater(),mDates); LinearLayoutManager manager = new LinearLayoutManager(this); //设置布局管理器 recyclerView.setLayoutManager(manager); //添加分割线 recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST)); //设置Item增加、移除动画 recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.setAdapter(adapter);
}
protected void initData()
{
mDates = new ArrayList<String>();
for (int i = 'A'; i < 'z'; i++)
{
mDates.add("" + (char) i);
}
}
}
ListAdapter:
package com.example.myrecyclerview.list; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.Adapter; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; import com.example.myrecyclerview.R; import java.util.List; /** * Created by 陈猛 on 2016/1/21. */ public class ListAdapter extends RecyclerView.Adapter<ListAdapter.ViewHolder> { private LayoutInflater inflater; private List<String> mDate; public ListAdapter(LayoutInflater inflater, List<String> mDate) { this.inflater = inflater; this.mDate = mDate; } public interface OnItemClickListener{ void OnClick(View view,int position); } private OnItemClickListener onItemClickListener; public void setOnItemClickListener(OnItemClickListener onItemClickListener) { this.onItemClickListener = onItemClickListener; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = inflater.inflate(R.layout.list_item,parent,false); ViewHolder viewHolder = new ViewHolder(view); return viewHolder; } @Override public void onBindViewHolder(ViewHolder holder, final int position) { holder.tv.setText(mDate.get(position)); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if(onItemClickListener!=null){ onItemClickListener.OnClick(view,position); } } }); } @Override public int getItemCount() { return mDate.size(); } public class ViewHolder extends RecyclerView.ViewHolder{ TextView tv; public ViewHolder(View itemView) { super(itemView); tv= (TextView) itemView.findViewById(R.id.textview); } } //增加item public void addData(int position) { mDate.add(position, "Insert One"); notifyItemInserted(position); } //删除item public void removeData(int position) { mDate.remove(position); notifyItemRemoved(position); } }
其分割线的代码已经在上面展示过这里省略。
效果如下:
Grid:
GridActivity:package com.example.myrecyclerview.grid; import android.app.Activity; import android.os.Bundle; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; import com.example.myrecyclerview.R; import com.example.myrecyclerview.list.DividerItemDecoration; import com.example.myrecyclerview.list.ListAdapter; import java.util.ArrayList; import java.util.List; /** * Created by 陈猛 on 2016/1/22. */ public class GridActivity extends Activity { private RecyclerView recyclerView; private ListAdapter adapter; private List<String> mDates; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.list_activity); recyclerView = (RecyclerView) findViewById(R.id.list_recyclerview); initData(); adapter = new ListAdapter(getLayoutInflater(),mDates); GridLayoutManager manager = new GridLayoutManager(this,4); recyclerView.setLayoutManager(manager); recyclerView.addItemDecoration(new DividerGridItemDecoration(this)); recyclerView.setAdapter(adapter); recyclerView.setItemAnimator(new DefaultItemAnimator()); adapter.setOnItemClickListener(new ListAdapter.OnItemClickListener() { @Override public void OnClick(View view, int position) { if(position%2==0){ adapter.addData(position); }else { adapter.removeData(position); } } }); } protected void initData() { mDates = new ArrayList<String>(); for (int i = 'A'; i < 'z'; i++) { mDates.add("" + (char) i); } } }
效果图:
相关文章推荐
- 前端路上的旅行
- roscpp
- Animator Override Controller学习及性能测试
- tomat修改默认项目路径
- cocos中的Box2d物理引擎
- mysql+spring+mybatis实现数据库读写分离
- HDU 1074 Doing Homework(状态压缩dp)
- ViewPager 详解(三)---PagerTabStrip与PagerTitleStrip添加标题栏的异同
- mysql kill process解决死锁
- java多线程
- LeetCode 260:Single Number III
- 刚踏上管理岗位,如何短时间树立威信,快速提高领导力?
- 字符设备驱动程序的设计
- (安全系列一)Android Apk反编译得到Java源代码
- 解决有java命令,没有javac命令
- 数据结构-单链表的java实现
- Matlab—Matlab操作mysql数据库
- 正向代理 & 反向代理
- 玩转swift(二)
- JSP SHELL有关的whoami漏洞