Android RecyclerView的基本使用
Android RecyclerView 在去年的Google I/O大会上就推出来了,以前经常使用的ListView 继承的是AbsListView,而RecyclerView则直接继承 ViewGroup,并实现了ScrollingView 和 NestedScrollingChild接口,RecyclerView相比ListView,是一次彻底的改变,RecyclerView 比ListView更加强大灵活。
DEMO实现功能:
- RecyclerView的点击事件: Item及item中的子View添加点击事件
- RecyclerView Item之间添加分隔线:垂直与水平方向
- RecyclerView 单个与多个Item的添加与删除
- RecyclerView Item添加与删除动画效果
- RecyclerView滚动状态监听
- LayoutManager的使用
DEMO效果图:
RecyclerView的相关的LayoutManager ItemDecoration 和 ItemAnimator
- LayoutManager:这个是为RecyclerView设置布局管理器的,决定RecyclerView的显示风格,它有两个直接子类:LinearLayoutManager 和 StaggeredGridLayoutManager,还有一个间接子类GridLayoutManager,GridLayoutManager继承LinearLayoutManager 。线性布局管理器 LinearLayoutManager 的布局像ListView显示多列,可以直接设置其布局方向(垂直或水平),网格布局管理器 GridLayoutManager像Gridview那样显示多行多列,而StaggeredGridLayoutManager则可以实现流式布局。
- ItemDecoration:在Item之间设置分隔线和偏移,提供了三个方法:getItemOffsets,onDraw,onDrawOver。ItemDecoration的绘制是有一定的顺序的,onDraw的绘制在Item视图绘制之前,onDrawOver 的绘制在Item视图绘制之后,并将其绘制的显示在视图之上,getItemOffsets为Item设置偏移量。如果你只是想实现简单的Item之间的分割线的话,可以直接在item的XML文件中直接定义就可以了。
- ItemAnimator:用来设置item的添加或者删除的动画风格,默认的动画是DefaultItemAnimator,也可以自己设置,继承RecyclerView.ItemAnimator,然后重写animateAdd,animateMove等方法就可以了。
- <android.support.v7.widget.RecyclerView
- android:id="@+id/recyclerview"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:scrollbars="vertical" />
如果想水平排列显示,把layoutManager.setOrientation(LinearLayoutManager.VERTICAL)替换成layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL)即可。
- // 如果布局大小一致有利于优化
- recyclerView.setHasFixedSize(true);
- // 创建一个线性布局管理器
- LinearLayoutManager layoutManager = new LinearLayoutManager(this);
- layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
- // 设置布局管理器
- recyclerView.setLayoutManager(layoutManager);
- // 创建数据集
- List<User> listData = new ArrayList<User>();
- for (int i = 0; i < 20; ++i) {
- User uBean = new User();
- uBean.setUsername("我是Item" + i);
- listData.add(uBean);
- }
- // 创建Adapter,并指定数据集
- MyAdapter adapter = new MyAdapter(context, listData);
- // 设置Adapter
- recyclerView.setAdapter(adapter);
MyAdapter:
- public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MViewHolder> {
- private Context context;
- private List<User> listData;
- public MyAdapter(Context context, List<User> mList) {
- super();
- this.context = context;
- this.listData = mList;
- }
- @Override
- public int getItemCount() {
- // TODO Auto-generated method stub
- return listData.size();
- }
- @Override
- public MViewHolder onCreateViewHolder(ViewGroup viewGroup, int arg1) {
- View view = View.inflate(viewGroup.getContext(),
- R.layout.item_user_friend_nod, null);
- // 创建一个ViewHolder
- MViewHolder holder = new MViewHolder(view);
- return holder;
- }
- @Override
- public void onBindViewHolder(MViewHolder mViewHolder, int arg1) {
- mViewHolder.mTextView.setText(listData.get(arg1).getUsername());
- mViewHolder.image.setBackgroundResource(R.drawable.head);
- }
- public class MViewHolder extends RecyclerView.ViewHolder {
- public TextView mTextView;
- public ImageView image;
- public MViewHolder(View view) {
- super(view);
- this.mTextView = (TextView) view.findViewById(R.id.tv_friend_name);
- this.image = (ImageView) itemView.findViewById(R.id.img_friend_avatar);
- }
- }
- }
MViewHolder是一个内部类,在其构造函数中,获取控件。 MyAdapter继承了RecyclerView.Adapter<ViewHolder>,并重写了getItemCount(),onCreateViewHolder和onBindViewHolder三个方法。
为RecyclerView的Item及item中的子View添加点击事件
- /**
- * item点击回调接口
- *
- * @author wen_er
- *
- */
- public interface ItemClickListener {
- /**
- * Item 普通点击
- */
- public void onItemClick(View view, int postion);
- /**
- * Item 长按
- */
- public void onItemLongClick(View view, int postion);
- /**
- * Item 内部View点击
- */
- public void onItemSubViewClick(View view, int postion);
- }
然后再稍稍改造一下MyAdapter:
- public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MViewHolder> {
- private Context context;
- private List<User> listData;
- private ItemClickListener mItemClickListener;
- public MyAdapter(Context context, List<User> mList) {
- super();
- this.context = context;
- this.listData = mList;
- }
- public void setItemClickListener(ItemClickListener mItemClickListener) {
- this.mItemClickListener = mItemClickListener;
- }
- @Override
- public int getItemCount() {
- // TODO Auto-generated method stub
- return listData.size();
- }
- @Override
- public MViewHolder onCreateViewHolder(ViewGroup viewGroup, int arg1) {
- View view = View.inflate(viewGroup.getContext(),
- R.layout.item_user_friend_nod, null);
- // 创建一个ViewHolder
- MViewHolder holder = new MViewHolder(view);
- return holder;
- }
- @Override
- public void onBindViewHolder(final MViewHolder mViewHolder,
- final int postion) {
- mViewHolder.mTextView.setText(listData.get(postion).getUsername());
- mViewHolder.image.setBackgroundResource(R.drawable.head);
- // 为image添加监听回调
- mViewHolder.image.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (null != mItemClickListener) {
- mItemClickListener.onItemSubViewClick(mViewHolder.image,
- postion);
- }
- }
- });
- }
- public class MViewHolder extends RecyclerView.ViewHolder {
- public TextView mTextView;
- public ImageView image;
- public MViewHolder(final View view) {
- super(view);
- this.mTextView = (TextView) view.findViewById(R.id.tv_friend_name);
- this.image = (ImageView) itemView.findViewById(R.id.img_friend_avatar);
- //为item添加普通点击回调
- view.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (null != mItemClickListener) {
- mItemClickListener.onItemClick(view, getPosition());
- }
- }
- });
- //为item添加长按回调
- view.setOnLongClickListener(new OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- if (null != mItemClickListener) {
- mItemClickListener.onItemLongClick(view, getPosition());
- }
- return true;
- }
- });
- }
- }
- }
对比以上的代码,只是在onBindViewHolder中为Item的子View添加监听回调,在MViewHolder的构造方法中为Item添加点击和长按监听回调。
- //为Item具体实例点击3种事件
- adapter.setItemClickListener(new ItemClickListener() {
- @Override
- public void onItemSubViewClick(View view, int postion) {
- T.showShort(context, "亲,你点击了Image"+postion);
- }
- @Override
- public void onItemLongClick(View view, int postion) {
- T.showShort(context, "亲,你长按了Item"+postion);
- }
- @Override
- public void onItemClick(View view, int postion) {
- T.showShort(context, "亲,你点击了Item"+postion);
- }
- });
为Item之间添加分隔线
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@drawable/selector_item_action" >
- <TextView
- android:id="@+id/tv_friend_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:layout_marginLeft="20dp"
- android:layout_toRightOf="@+id/img_friend_avatar"
- android:text="test"
- android:textSize="18sp" />
- <ImageView
- android:id="@+id/img_friend_avatar"
- android:layout_width="50dp"
- android:layout_height="50dp"
- android:layout_alignParentLeft="true"
- android:layout_marginBottom="8dip"
- android:layout_marginLeft="8dip"
- android:layout_marginTop="8dip"
- android:background="@drawable/ic_launcher" />
- </RelativeLayout>
- <!-- 可以添加以下代码为Item之间设置分隔线 -->
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:layout_alignParentBottom="true"
- android:background="@drawable/divider_horizontal_line" />
- </RelativeLayout>
addItemDecoration的参数ItemDecoration需要我们重写它的onDraw,onDrawOver和getItemOffsets方法,因为item可能是水平排列,也可能是垂直排列,所以我们传入一个参数oritation值,作为item排列方向的标记,参考了Git上的代码,原来的代码当水平布局时,分隔线的高度会填满整个屏幕(Item并未填满整个屏幕),所以稍稍做了改动。
- <pre name="code" class="java">public class ItemDecorationDivider extends ItemDecoration {
- private Drawable mDivider;
- private int mOritation;
- public ItemDecorationDivider(Context context, int resId, int oritation) {
- mDivider = context.getResources().getDrawable(resId);
- this.mOritation = oritation;
- Log.i("ItemDecorationDivider", "mOritation=" + mOritation);
- }
- @Override
- public void onDrawOver(Canvas c, RecyclerView parent) {
- if (mOritation == LinearLayoutManager.VERTICAL) {
- 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);
- 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);
- }
- } else if (mOritation == LinearLayoutManager.HORIZONTAL) {
- 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();
- final int bottom = child.getBottom();
- mDivider.setBounds(left, top, right, bottom);
- mDivider.draw(c);
- }
- }
- }
- @Override
- public void getItemOffsets(Rect outRect, int position,
- RecyclerView parent) {
- if (mOritation == LinearLayoutManager.VERTICAL) {
- outRect.set(0, 0, 0, mDivider.getIntrinsicWidth());
- // outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
- } else if (mOritation == LinearLayoutManager.HORIZONTAL) {
- // outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
- outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
- }
- }
- }
- recyclerView.addItemDecoration(new ItemDecorationDivider(context,
- R.drawable.item_divider, LinearLayoutManager.VERTICAL));
item_divider:
- <?xml version="1.0" encoding="utf-8"?>
- <shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle" >
- <solid android:color="#CCCCCC" />
- <size android:height="1dp" />
- </shape>
RecyclerView Item的添加与删除
notifyItemChanged(int position)//通知位置position的Item的数据改变notifyItemInserted(int)//通知位置position的Item的数据插入notifyItemRemoved(int)//通知位置position的Item的数据移除
notifyItemRangeChanged(int positionStart, int itemCount) //通知从位置positionStart开始,有itemCount个Item的数据发生改变notifyItemRangeInserted(int positionStart, int itemCount) //通知从位置positionStart开始,有itemCount个Item的数据插入notifyItemRangeRemoved(int positionStart, int itemCount)//通知从位置positionStart开始,有itemCount个Item的数据移除
- </pre><pre name="code" class="java"> /**
- * TODO<添加数据,指定其位置>
- */
- public void addData(User info, int position) {
- listData.add(position, info);
- notifyItemInserted(position);
- // notifyDataSetChanged(); //不会触发Item的动画效果,告知数据改变,刷新UI
- }
- /**
- * TODO<添加数据到最后面添加>
- */
- public void addData(User info) {
- // listData.add(position, info);
- // notifyItemInserted(position);
- listData.add(info);
- notifyDataSetChanged();
- }
- /**
- * TODO<删除数据,指定其位置>
- */
- public void daleteData(int position) {
- listData.remove(position);
- notifyItemRemoved(position);
- }
- /**
- * TODO<某一位置开始,有itemCount个Item的数据删除>
- */
- public void itemRangeRemoved(int positionStart, int itemCount) {
- for (int i = positionStart; i < itemCount; i++) {
- listData.remove(positionStart);
- }
- notifyItemRangeRemoved(positionStart, itemCount);
- // notifyDataSetChanged(); //不会触发Item的动画效果,告知数据改变,刷新UI
- }
- /**
- * TODO<某一位置开始,有itemCount个Item的数据插入>
- */
- public void itemRangeInserted(User info, int positionStart, int itemCount) {
- for (int i = positionStart; i < itemCount; i++) {
- listData.add(i, info);
- }
- notifyItemRangeInserted(positionStart, itemCount);
- // notifyDataSetChanged();
- }
- </pre><pre>
- case R.id.btn3:
- User uBean = new User();
- uBean.setUsername("我是增加的Item");
- adapter.addData(uBean, 0);// 添加到第一个
- break;
- case R.id.btn4:
- adapter.daleteData(0); // 删除第一个
- break;
- case R.id.btn5:
- User uBean1 = new User();
- uBean1.setUsername("我是连续添加的Item");
- adapter.itemRangeInserted(uBean1, 0, 5);
- break;
- case R.id.btn6:
- adapter.itemRangeRemoved(0, 5);
- break;
为RecyclerView的Item添加动画
- // 使用RecyclerView提供的默认的动画效果
- recyclerView.setItemAnimator(new DefaultItemAnimator());
这就是我们在效果图看到的动画效果,如果想要其它的动画效果,参见GitHub:https://github.com/gabrielemariotti/RecyclerViewItemAnimators
RecyclerView滚动状态监听
- recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
- @Override
- public void onScrollStateChanged(RecyclerView recyclerView,
- int scrollState) {
- updateState(scrollState);
- }
- @Override
- public void onScrolled(RecyclerView recyclerView, int i, int i2) {
- String s = "可见Item数量:" + layoutManager.getChildCount()+"\n"
- + "可见Item第一个Position:"
- + layoutManager.findFirstVisibleItemPosition()+"\n"
- + "可见Item最后一个Position:"
- + layoutManager.findLastVisibleItemPosition();
- tv.setText(s);
- }
- });
- private void updateState(int scrollState) {
- String stateName = "Undefined";
- switch (scrollState) {
- case SCROLL_STATE_IDLE:
- stateName = "Idle";
- break;
- case SCROLL_STATE_DRAGGING:
- stateName = "Dragging";
- break;
- case SCROLL_STATE_SETTLING:
- stateName = "Flinging";
- break;
- }
- tv_state.setText("滑动状态:" + stateName);
- }
当滚动RecyclerView的时候,效果如DEMO效果图所示。
- LinearLayoutManager
- GridLayoutManager
这是类似GridView的网格布局,三个构造函数:
GridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout)//spanCount为列数,orientation为布局方向,reverseLayout决定布局是否反向。
- gridLayoutManager = new GridLayoutManager(this, 3,
- GridLayoutManager.VERTICAL, false);
- // 设置布局管理器
- recyclerView.setLayoutManager(gridLayoutManager);
- StaggeredGridLayoutManager
StaggeredGridLayoutManager(int spanCount, int orientation) //spanCount为列数,orientation为布局方向
- StaggeredGridLayoutManager = new StaggeredGridLayoutManager(2,
- StaggeredGridLayoutManager.VERTICAL);
- // 设置布局管理器
- recyclerView.setLayoutManager(StaggeredGridLayoutManager);
GITHUb下载地址:https://github.com/myjoybar/Android-RecyclerView
CSDN下载地址:http://download.csdn.net/detail/yalinfendou/8837997
Android RecyclerView的基本使用的更多相关文章
- Android RecyclerView 实现支付宝首页效果
Android RecyclerView 实现支付宝首页效果 [TOC] 虽然我本人不喜欢支付宝的,但是这个网格本身其实还是不错的,项目更新中更改了一个布局为网格模式,类似支付宝.(估计是产品抄袭的= ...
- 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) 收藏 ...
随机推荐
- iOS实现简书的账号识别方式(正则表达式)
通过简书iOS客户端登录,我们会看到请输入手机号或者邮箱登录,但是我们随机输入1234567的时候,便会弹出手机格式不正确,同样也会识别我们的邮箱格式,那么我们在项目中怎么实现这种判断呢? 0E471 ...
- MFC 打开文件夹 调用其他程序 打开文件
ShellExecute(NULL,TEXT("OPEN"),要打开的文件的路径,NULL,NULL,SW_SHOWNORMAL); ShellExecute(NULL, &quo ...
- WPF画线问题,几千条以后就有明显的延迟了。
我现在是这么画的,class A { private GeometryGroup _lines; private Path _path; public A() { _path.Data = ...
- 原生js添加和删除类
原生js添加和删除类: this.className +=" "; this.className = this.className.replace(" 原来的类" ...
- ACM: Find MaxXorSum 解题报告-字典树
Find MaxXorSum Time Limit:2000MS Memory Limit:65535KB 64bit IO Format: Description Given n non-negat ...
- SQL中union运算操作的理解
在SQL中,对于并运算,可以使用union关键字. 例如: SELECT column_name(s) FROM table_name1 UNION SELECT column_name(s) FRO ...
- 纪念逝去的岁月——C++实现一个队列(使用类模板)
1.代码 2.运行结果 1.代码 #include <stdio.h> #include <string.h> template <typename T> clas ...
- Javascript 异步编程的4种方法
你可能知道,Javascript语言的执行环境是"单线程"(single thread). 所谓"单线程",就是指一次只能完成一件任务.如果有多个任务,就必须排 ...
- jquery插件之表格隔行变色并鼠标滑过高亮显示
该插件乃本博客作者所写,目的在于提升作者的js能力,也给一些js菜鸟在使用插件时提供一些便利,老鸟就悠然地飞过吧. 此插件旨在实现表格隔行变色,且鼠标移动在表格的某一行上时,该行能高亮显示.整体代码如 ...
- 【资源】mp3的外链资源
做开发有时候会遇到需要mp3外链来进行程序的调试,但是自己搭建服务器比较麻烦,而主流的音乐平台都不提供外链音乐.晚上偶得一外链mp3的网站,标记之: http://9hok.com/