Android-RecyclerView
众所周知,RecyclerView是Google公司推出的V7包中的一个重要的控件,非常方便,可以替代现有的ListView和Gridview等控件,它功能很强大,灵活性好,扩展性强,还自带VIewHolder,不再需要自己去写,这点非常方便,缺点也是有的,那就是比较原始,因为灵活性好,所以封装的比较浅,原汁原味,所以它不具备点击事件和长按事件等。
下面先来看看Recycler的简单的使用:
直接上代码:
public class MainActivity extends AppCompatActivity { private RecyclerView rv;
private List<ItemEntity> rvList; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rvList = initRvList();
rv = (RecyclerView) findViewById(R.id.rv);
//默认的设置一个纵向的LinearLayoutManage,这一步,不能忘,很重要
rv.setLayoutManager(new LinearLayoutManager(this));
rv.setAdapter(new RecyclerViewAdapter(this, rvList));
} public class ItemEntity {
private int id;
private String name; public ItemEntity(int ids, String names) {
this.id = ids;
this.name = names;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
} private List<ItemEntity> initRvList() {
List<ItemEntity> ll = new ArrayList<ItemEntity>();
ll.add(new ItemEntity(1, "张三"));
ll.add(new ItemEntity(2, "李四"));
ll.add(new ItemEntity(3, "王五"));
ll.add(new ItemEntity(4, "赵六"));
ll.add(new ItemEntity(5, "钱七"));
ll.add(new ItemEntity(6, "孙八"));
ll.add(new ItemEntity(7, "吴九"));
ll.add(new ItemEntity(8, "周十"));
return ll;
} public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.RecyclerViewViewHolder> { private Context mContext;
private List<ItemEntity> mList; //构造方法,提供context上下文和RexyclerView的数据源
public RecyclerViewAdapter(Context context, List<ItemEntity> list) {
this.mContext = context;
this.mList = list;
} @Override
public RecyclerViewAdapter.RecyclerViewViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//通过一个itemView创建一个ViewHolder
View itemViews = getLayoutInflater().inflate(R.layout.item_recyclerview,parent, false);
return new RecyclerViewViewHolder(itemViews);
} @Override
public void onBindViewHolder(RecyclerViewViewHolder holder, int position) {
//这里为item项的每个一个控件赋值
holder.id.setText(String.valueOf(mList.get(position).getId()));
holder.name.setText(mList.get(position).getName());
} @Override
public int getItemCount() {
//返回值item的总数
return mList == null ? 0 : mList.size();
} public class RecyclerViewViewHolder extends RecyclerView.ViewHolder { public TextView id;
public TextView name; public RecyclerViewViewHolder(View itemView) {
super(itemView);
id = (TextView) itemView.findViewById(R.id.id);
name = (TextView) itemView.findViewById(R.id.name);
}
}
}
}
这里附上itemView的布局文件的代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"> <TextView
android:id="@+id/id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="#f80"
android:textSize="13sp" /> <TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:gravity="center"
android:textColor="#08f"
android:textSize="16sp" /> </LinearLayout>
整个示例就这样了,写的比较简单,仅供各位参考。
温馨提醒一句,因为之前习惯使用ListView,所以,刚开始使用RecyclerView的时候总是忘了设置LayoutManage,这样出来的结果是一片空白,一脸懵逼,自此,每次都要去检查一下有没有LayoutManage。哈哈。
上面的示例是一个纵向的线性的LayoutManage,如果要搞成横向的或者Grid的直接设置另外一个LayoutManage就可以了,还可以很方便的实现瀑布流布局。
//设置一个LinearLayoutManager
rv.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
//设置一个GridLayoutManager
rv.setLayoutManager(new GridLayoutManager(this, 3));
//设置一个纵向的,有3列的瀑布流的布局
rv.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL));
最简单的示例先写到这里,后续更新。哈哈。2016-12-28 14:56
示例看完,仔细的看一下代码,其实也很繁琐的,而且实际使用中还有诸多的不方便,例如没有getItem()等方法。所以,我们对这进行做一些封装。使得我们使用的时候更加方便,也让代码变得更加简洁。
public abstract class BaseAdapter<T> extends RecyclerView.Adapter<BaseViewHolder<T>> { private List<T> list;
private Context mContext; public BaseAdapter(List<T> list, Context mContext) {
this.list = list;
this.mContext = mContext;
} @Override
public BaseViewHolder<T> onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = (LayoutInflater)
mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
return onViewHolderCreate(layoutInflater, parent, viewType);
} @Override
public void onBindViewHolder(BaseViewHolder<T> holder, int position) {
holder.onBind(list.get(position), position);
} @Override
public int getItemCount() {
return list == null ? 0 : list.size();
} public void setList(List<T> list) {
this.list = list;
} public T getItem(int pos) {
return list.get(pos);
} abstract BaseViewHolder<T> onViewHolderCreate(LayoutInflater layoutInflater, ViewGroup parent, int viewType);
}
上面就是Adapter的封装。下面来看看ViewHolder的封装。
public abstract class BaseViewHolder<T> extends RecyclerView.ViewHolder { public BaseViewHolder(View itemView) {
super(itemView);
} public abstract void onBind(T t,int position);
}
最后来看看是怎么使用我们已经封装好的Adapter和ViewHolder。
public class Adapters extends BaseAdapter<ItemEntity> { public Adapters(List<ItemEntity> list, Context mContext) {
super(list, mContext);
} @Override
BaseViewHolder<ItemEntity> onViewHolderCreate(LayoutInflater layoutInflater, ViewGroup parent, int viewType) {
View view = layoutInflater.inflate(R.layout.item_recyclerview, parent, false);
return new ViewHolders(view);
}
} public class ViewHolders extends BaseViewHolder<ItemEntity> { private TextView id;
private TextView name; public ViewHolders(View itemView) {
super(itemView);
id = (TextView) itemView.findViewById(R.id.id);
name = (TextView) itemView.findViewById(R.id.name);
} @Override
public void onBind(ItemEntity itemEntity, int position) {
id.setText(String.valueOf(itemEntity.getId()));
name.setText(itemEntity.getName());
}
}
现在来看看我们封装的Adapter和VIewHolder,先来看简单的ViewHolder,ViewHolder中很简单,只有一个构造方法和一个抽象的onBind() 这有啥作用呢?其实很简单,看最后使用的时候,使用的时候,onBind() 可以直接写itemView的控件的赋值就可以了,简单明了。再来看看Adapter,Adapter也很简单,第一个是构造方法,中间三个是复写父类的,接下来的两个是自己加的,方便以后Adapter里面操作一些和业务相关的一些东西,别问为什么,只是这两个方法在以后的日子里,用上的概率会比较大。现在来看看onCreateViewHolder() 先不要看封装的,先看看最前面的那个小示例,示例里面的onCreateViewHolder里面做的是直接返回一个ViewHolder对象,而new一个ViewHolder需要一个itemView,而itenView需要inflater一个Layout来得到一个View,这,明显不对,为啥呢?因为我们这是封装,而不是实际的使用,实际使用的时候才涉及到这一块,所以,对于未知的东西,我们可以写一个抽象的方法来返回,而inflater一个view需要的一些东西我们可以事先做好,例如获取一个inflater就可以在这里做,然后直接在使用的时候调用就可以了,所以这里直接调用一个抽象的方法来获取ViewHolder.再来看看OnBindViewHolder(),这里我们直接调用ViewHolder的那个抽象的onBind(),看看最前面的那个小示例,示例里面,我们做的是给itemView的每一个控件赋值,现在这样封装,可以将Adapter里面做的操作转到ViewHolder,这样分工更加明确,看看最后的使用就知道了,Adapter里面只有几行代码,只需要在onViewHolder()方法里面操作即可,最后看看ViewHolder,所有涉及到的控件的findView,赋值都在ViewHolder里面进行。这样分工就明确了,代码也一眼看懂。里面要给itemView的每一个控件添加点击事件也很方便,如果要给整个的ItemView添加点击事件可以直接使用RecyclerView的ViewHolder里面的itemView既可(itemView.setOnClickListener)。
有关于Adapter和ViewHolder的封装写到这里。后续更新,2016-12-28 18:01
RecyclerView.ItemDecoration
RecyclerView 是不带分割线的,所以分割线要自己写,而分割线实现起来也是比较简单的,最傻瓜式的就是在布局文件中预留出来。然后再最后一个的时候隐藏掉,就这个简单,但是,如果遇到比较复杂的就难搞了,像gridview一样,要考虑横竖的情况就比较麻烦了。但不用怕,官方有一个现成的抽象类,你只需要继承这个类,实现相应的方法即可。哈哈,那就是ItemDecoration。ItemDecoration中有三个重要的方法,onDraw,onDrawOver,getItemOffsets,其中,onDraw和onDrawOver只需要实现一个即可,看名字也知道啥意思了,一个是在draw item 之前调用,一个是在之后调用,所以只需要实现一个即可,一般实现onDraw比较多。getItemOffsets,这个方法其实就是计算分割线的偏移量。说白了就是执行分割线的大小。
public class LinearManageItemDecoracition extends RecyclerView.ItemDecoration { public int mOrientation = LinearLayoutManager.VERTICAL;
public Drawable dividerLineHorizontal = null;
public Drawable dividerLineVertical = null;
public ColorDrawable cd;
private Paint paint; public LinearManageItemDecoracition(Context context, int orientation) {
if (orientation == LinearLayoutManager.VERTICAL || orientation == LinearLayoutManager.HORIZONTAL)
mOrientation = orientation;
else
throw new RuntimeException("orientation is Incorrect");
dividerLineHorizontal = context.getResources().getDrawable(R.drawable.divider_drawable_horizontal);
dividerLineVertical = context.getResources().getDrawable(R.drawable.divider_drawable_vertical);
//纯色可以不用Drawable,直接用颜色
cd = new ColorDrawable(Color.parseColor("#FF8800"));
paint = new Paint();
paint.setColor(Color.BLUE);
} @Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
// drawMethod1(c, parent, state);
// drawMethod2(c, parent, state);
drawMethod3(c, parent, state);
} @Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
} @Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
outRect.left = 0;
outRect.top = 0;
if (mOrientation == LinearLayoutManager.VERTICAL) {
outRect.right = 0;
if (dividerLineHorizontal != null) {
outRect.bottom = dividerLineHorizontal.getIntrinsicHeight();
} else {
outRect.bottom = 5;
}
} else if (mOrientation == LinearLayoutManager.HORIZONTAL) {
outRect.bottom = 0;
if (dividerLineVertical != null) {
outRect.right = dividerLineVertical.getIntrinsicWidth();
} else {
outRect.right = 5;
}
}
} public void drawMethod1(Canvas c, RecyclerView parent, RecyclerView.State state) {
int childCount = parent.getChildCount();
if (mOrientation == LinearLayoutManager.VERTICAL) {
for (int pos = 0; pos < childCount - 1; pos++) {
View view = parent.getChildAt(pos);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
int left = view.getLeft() - layoutParams.leftMargin;
int right = view.getRight() + layoutParams.rightMargin;
int top = view.getBottom() + layoutParams.bottomMargin;
int bottom = top + dividerLineHorizontal.getIntrinsicHeight();
dividerLineHorizontal.setBounds(left, top, right, bottom);
dividerLineHorizontal.draw(c);
} } else if (mOrientation == LinearLayoutManager.HORIZONTAL) {
int top = parent.getPaddingTop();
int bottom = parent.getHeight() - parent.getPaddingBottom();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int left = child.getRight() + params.rightMargin;
int right = left + dividerLineVertical.getIntrinsicWidth();
dividerLineVertical.setBounds(left, top, right, bottom);
dividerLineVertical.draw(c);
}
}
} public void drawMethod2(Canvas c, RecyclerView parent, RecyclerView.State state) {
int childCount = parent.getChildCount();
int dp1 = dpToPx(1);
if (mOrientation == LinearLayoutManager.VERTICAL) {
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
for (int index = 0; index < childCount - 1; index++) {
View childView = parent.getChildAt(index);
RecyclerView.LayoutParams childViewParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
//childViewParams.bottomMargin->画在childview下面,自然要加上margin
//ViewCompat.getTranslationY(childView) ,纵向的平移,在动画的时候用得上,如果item没有动画则可有可无
int top = childView.getBottom() + childViewParams.bottomMargin + Math.round(ViewCompat.getTranslationY(childView));
int bottom = top + dp1;
c.drawRect(left, top, right, bottom, paint);
}
} else if (mOrientation == LinearLayoutManager.HORIZONTAL) {
int top = parent.getPaddingTop();
int bottom = top + parent.getHeight();
for (int index = 0; index < childCount - 1; index++) {
View childView = parent.getChildAt(index);
RecyclerView.LayoutParams childViewParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
int left = childView.getRight() + childViewParams.rightMargin;
int right = left + dp1;
c.drawRect(left, top, right, bottom, paint);
}
}
} public void drawMethod3(Canvas c, RecyclerView parent, RecyclerView.State state) {
int childCount = parent.getChildCount();
if (mOrientation == LinearLayoutManager.VERTICAL) {
for (int pos = 0; pos < childCount - 1; pos++) {
View view = parent.getChildAt(pos);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
int left = view.getLeft() - layoutParams.leftMargin;
int right = view.getRight() + layoutParams.rightMargin;
int top = view.getBottom() + layoutParams.bottomMargin;
//纯色的情况下,高度要写死了
int bottom = top + dpToPx(1);
cd.setBounds(left, top, right, bottom);
cd.draw(c);
}
} else if (mOrientation == LinearLayoutManager.HORIZONTAL) {
int top = parent.getPaddingTop();
int bottom = parent.getHeight() - parent.getPaddingBottom();
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int left = child.getRight() + params.rightMargin;
//同理。纯色的情况下,宽度是写死的
int right = left + dpToPx(1);
cd.setBounds(left, top, right, bottom);
cd.draw(c);
}
}
} public int dpToPx(int dp) {
return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
}
}
这个Decoration中,实现了几种方式。onDraw提供了3种绘制分割线的方式,但getItemOffsets只实现drawable的时候
第一种方式,是在分割线处画一个Drawable,这里的drawable可以是自己写的一个shape的drawable文件,也可以是图片,等等。如果是自己写的drawable,那么要写上宽或高。
第二种方式,是直接在分割线处画一个矩形的纯色区域,也很好理解。
第三种方式,和第一种。换汤不换药。也就是在第一种的情况下,如果drawable是纯色的话,就可以直接用colorDrawable,就不用那么麻烦自己去写一个shape的drawable了。这时候。高度(或宽度)就要自己自定义了,而不是在drawable里面写死。其实也很好理解。
2017-1-4 10:34 更新,后续再更新gridview的divider吧
RecyclerView还有一个和ListView比较常用不同是:当没数据的时候,也就是emptyView,ListView 可以直接设就可以了,而RecyclerView 没有提供相应的api,这就要我们自己去实现了。
实现的原理也很简单。那就是控件的显示和隐藏嘛。
虽然没有提供设置emptyView的api,但是提供了一个数据的观察相应的api,也就是给Adapter注册一个观察者,这个观察者可以观察整个Recycler的数据的变化。这就很简单啦,只要数据是空的时候,那就显示EmptyView,否则显示RecyclerView。都弄清楚了,就看代码。
rvAdapter.registerAdapterDataObserver(obs = new RecyclerView.AdapterDataObserver() {
@Override
public void onChanged() {
super.onChanged();
setEmptyView();
}
});
rv.setAdapter(rvAdapter);
setEmptyView();
private void setEmptyView() {
if (rvAdapter.getItemCount() > 0) {
emptyView.setVisibility(View.GONE);
rv.setVisibility(View.VISIBLE);
} else {
emptyView.setVisibility(View.VISIBLE);
rv.setVisibility(View.GONE);
}
}
这里,注册完了。最后一步,在RecyclerView的数据加载完毕的时候再调一下SemptyView,因为很多时候,加载出来的数据就是空的。所以,这里要设一下。设置EmptyView是否显示也就根据Adapter的返回的getItemCount来判断。
都好了,最后别忘了反注册。注册的观察者要在Activity 销毁的时候反注册。
@Override
protected void onDestroy() {
super.onDestroy();
if (obs != null)
rvAdapter.unregisterAdapterDataObserver(obs);
}
OK ,一切完毕。
更新于2017-1-4 11:46
Android-RecyclerView的更多相关文章
- Android RecyclerView 实现支付宝首页效果
Android RecyclerView 实现支付宝首页效果 [TOC] 虽然我本人不喜欢支付宝的,但是这个网格本身其实还是不错的,项目更新中更改了一个布局为网格模式,类似支付宝.(估计是产品抄袭的= ...
- Android RecyclerView的基本使用
Android RecyclerView 在去年的Google I/O大会上就推出来了,以前经常使用的ListView 继承的是AbsListView,而RecyclerView则直接继承 ViewG ...
- Android RecyclerView单击、长按事件:基于OnItemTouchListener +GestureDetector标准实现(二),封装抽取成通用工具类
Android RecyclerView单击.长按事件:基于OnItemTouchListener +GestureDetector标准实现(二),封装抽取成通用工具类 我写的附录文章2,介绍了 ...
- Android RecyclerView单击、长按事件标准实现:基于OnItemTouchListener + GestureDetector
Android RecyclerView单击.长按事件:基于OnItemTouchListener + GestureDetector标准实现 Android RecyclerView虽然拥有L ...
- Android RecyclerView添加Header头部
Android RecyclerView添加Header头部 Android RecyclerView不像以前的ListView那样直接添加头部,如果要给RecyclerView增加头部,则需要 ...
- Android RecyclerView(瀑布流)水平/垂直方向分割线
Android RecyclerView(瀑布流)水平/垂直方向分割线 Android RecyclerView不像过去的ListView那样随意的设置水平方向的分割线,如果要实现Recycle ...
- 极简的Android RecyclerView Adapter(使用DataBinding)
阅读本篇文章需要读者对Android Databinding和RecyclerView有一定的了解. 简介 我们知道,DataBinding的核心理念是数据驱动.数据驱动驱动的目标就是View,使用D ...
- 浅谈Android RecyclerView
Android RecyclerView 是Android5.0推出来的,导入support-v7包即可使用. 个人体验来说,RecyclerView绝对是一款功能强大的控件. 首先总结下Recycl ...
- [Android]RecyclerView的简单演示样例
去年google的IO上就展示了一个新的ListView.它就是RecyclerView. 下面是官方的说明,我英语能力有限,只是我大概这么理解:RecyclerView会比ListView更具有拓展 ...
- (转载) Android RecyclerView 使用完全解析 体验艺术般的控件
Android RecyclerView 使用完全解析 体验艺术般的控件 标签: Recyclerviewpager瀑布流 2015-04-16 09:07 721474人阅读 评论(458) 收藏 ...
随机推荐
- 在PowerShell中使用curl(Invoke-WebRequest)
前言 习惯了windows的界面模式就很难转去命令行,甚至以命令行发家的git也涌现出各种界面tool.然而命令行真的会比界面快的多,如果你是一个码农. situation:接到需求分析bug,需要访 ...
- 【WCF】自定义错误处理(IErrorHandler接口的用法)
当被调用的服务操作发生异常时,可以直接把异常的原始内容传回给客户端.在WCF中,服务器传回客户端的异常,通常会使用 FaultException,该异常由这么几个东东组成: 1.Action:在服务调 ...
- [原] KVM 虚拟化原理探究(3)— CPU 虚拟化
KVM 虚拟化原理探究(3)- CPU 虚拟化 标签(空格分隔): KVM [TOC] CPU 虚拟化简介 上一篇文章笼统的介绍了一个虚拟机的诞生过程,从demo中也可以看到,运行一个虚拟机再也不需要 ...
- OSGi规范的C#实现开源
这是大约在3-4年前完成的一个C#实现的OSGi框架,实现的过程参照了OSGi规范与与一些实现思路(感谢当时的那些资料与项目),此框架虽然仅在几个小型项目有过实际的应用,但OSGi的规范实现还是相对比 ...
- html中返回上一页的各种写法【转】
超链接返回上一页代码: <a href="#" onClick="javascript :history.back(-1);">返回上一页</ ...
- BPM合同管理解决方案分享
一.方案概述合同是组织与组织间所订协议的法律 表现形式,体现着双方对于合作在法律和道德上的承诺.然而,大多数企业的合同管理都或多或少存在合同审批过程不规范.签订草率.审批权责不清.合同执行跟踪难.合同 ...
- C#事件-使用事件需要的步骤
事件是C#中另一高级概念,使用方法和委托相关.奥运会参加百米的田径运动员听到枪声,比赛立即进行.其中枪声是事件,而运动员比赛就是这个事件发生后的动作.不参加该项比赛的人对枪声没有反应. 从程序员的角度 ...
- vim环境变量配置、背景色配置
我们使用vi或者vim的时候,如果想要显示行号,可能会这样做:切换到命令模式,然后输入set nu,再按回车键就显示了:还有就是咱们在编写程序的时候,有的时候会希望按下回车键后,光标不是每次都在行首, ...
- JavaMail发送邮件
发送邮件包含的内容有: from字段 --用于指明发件人 to字段 --用于指明收件人 subject字段 --用于说明邮件主题 cc字段 -- 抄送,将邮件发送给收件人的同时抄 ...
- angularJS(6)
angularJS(6) 一:angularJs的事件. 1.ng-click指令定义了AngularJS点击事件. <div ng-app="myapp" ng-contr ...