1. RecyclerView介绍
(1)RecyclerView的控件相对于ListView,他好在哪里呢?
1)它封装了viewholder的回收复用。
2)RecyclerView使用布局管理器管理子view的位置,也就是说你再不用拘泥于ListView的竖直线性展示方式。通过设置LayoutManager来快速实现listview、gridview、瀑布流的效果;而且还可以设置横向和纵向显示
3)带了ItemAnimation,可以设置加载和移除时的动画,方便做出各种动态浏览的效果。
4)分开的view :
我们平时用listview的时候,adapter一般这么写的:
1 if (convertView == null) { 2 holder = new ViewHolder(); 3 LayoutInflater inflater = ((Activity) mContext).getLayoutInflater(); 4 convertView = inflater.inflate( 5 R.layout.list_device_binding, parent, false); 6 7 holder.deviceImage = (ImageView) convertView 8 .findViewById(R.id.bluetoothDeviceImage); 9 holder.deviceName = (TextView) convertView 10 .findViewById(R.id.bluetoothDeviceName); 11 holder.deviceType = (TextView) convertView 12 .findViewById(R.id.bluetoothDeviceType); 13 convertView.setTag(holder); 14 } else { 15 holder = (ViewHolder) convertView.getTag(); 16 }
但是,到了这里,RecyclerView分隔开了,如下:
1 @Override 2 public A onCreateViewHolder(ViewGroup parent, int viewType) { 3 final View view = LayoutInflater.from(mContext). 4 inflate(R.layout.listitem_track_history, parent, false); 5 return new ViewHolder(view); 6 } 7 8 @Override 9 public void onBindViewHolder(A holder, int position) { 10 Data da=getData(position); 11 holder.tvDate.setText(da.getDate()); 12 }
5)相对简单的层次结构:
我们看下Listview他背后的继承关系:
1 public class ListView extends AbsListView 2 public abstract class AbsListView extends AdapterView<ListAdapter> 3 public abstract class AdapterView<T extends Adapter> extends ViewGroup
三重继承,内容还挺多的,不是直接继承ViewGroup,而相反的RecycleView却是直接继承自ViewGroup的。
(2)RecyclerView的控件相对于ListView,他有什么缺陷呢?(目前只知道1个缺点)
1)不能简单的添加点击事件onListItemClickListener
我们在使用listview设置子item的点击事件的时候,只需要像下面这么写
1 listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 2 @Override 3 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 4 5 User user= parent.getItemAtPosition(position); 6 } 7 });
但是,如果你使用这个RecycleView,会发现没有这个接口了。RecyclerView不再负责Item视图的布局及显示,所以RecyclerView也没有为Item开放OnItemClick等点击事件。
这就需要我们自己实现,见我的博客:Android 高级UI设计笔记20:RecyclerView 的详解之RecyclerView添加Item点击事件
-
Android RecyclerView 的用法
-
Android RecyclerView 横向布局
-
Android RecyclerView 垂直布局
-
Android RecyclerView 表格布局
这里我们使用的Android Studio,首先新建Android工程之后,右击app,打开 "Open Module Setting" ,然后找到"Dependencies"选项,添加RecyclerView依赖库,如下:
在gradle中添加依赖
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:recyclerview-v7:23.4.0'
}
当然也有很多朋友使用Eclipse开发工具(建议大家尽快使用AS,AS比Eclipse高效太多了),如果要使用RecyclerView也很简单,只要添加一个jar包到工程的libs文件下,然后构建路径就可以使用了,当然静态布局设计的时候还是注意要引用RecyclerView的全路径。
这个jar包为android-support-v7-recyclerview.jar 和 android-support-v4.jar,这个两个jar包往往会因为版本不一致产生冲突,从而会报错:
java.lang.RuntimeException: Unable to start activity ComponentInfo
这里是安全使用的v4 和 v7 库的下载地址为:http://download.csdn.net/detail/hebao5201314/9567555
那么这里我先讲解使用RecyclerView主要方法,如下:
1)setLayoutManager(layout):控制显示方式。
RecyclerView提供了三种LayoutManager:LinearLayoutManager、GridLayoutManager、StaggeredGridLayoutManager
2)setItemAnimator():控制item的增删动画。
3)addItemDecoration(): 控制item间的间隔,这里还可以自定义间隔的样式。
——Adapter:使用RecyclerView时,我们需要一个集成RecyclerView.Adapter的适配器,作用是将数据与每个item的界面进行绑定。
——LayoutManager:用来确定每个item的布局,何时展示和隐藏。回收或重用的时候LayoutManager会向适配器请求新的数据来替换旧的数据,这种机制和ListView的原理类似,都避免了创建过多的View和频繁的调用findViewById方法。
目前SDK中提供了三种自带的LayoutManager:
1)LinearLayoutManager
2)GridLayoutManager
3)StaggeredGridLayoutManager
2. 接下来我们就可以使用RecyclerView。
(1)首先我们来到MainActivity里面,如下:
1 package com.example.hebao.learnrv; 2 3 import android.support.v7.app.AppCompatActivity; 4 import android.os.Bundle; 5 import android.support.v7.widget.LinearLayoutManager; 6 import android.support.v7.widget.RecyclerView; 7 import android.view.Menu; 8 import android.view.MenuItem; 9 import android.view.View; 10 import android.view.ViewGroup; 11 import android.widget.TextView; 12 13 public class MainActivity extends AppCompatActivity { 14 15 private RecyclerView rv; 16 @Override 17 protected void onCreate(Bundle savedInstanceState) { 18 super.onCreate(savedInstanceState); 19 rv = new RecyclerView(this); 20 //先加载RecyclerView 21 setContentView(rv); 22 //然后动态设置RecyclerView 23 rv.setLayoutManager(new LinearLayoutManager(this)); 24 rv.setAdapter(new RecyclerView.Adapter(){ 25 class ViewHolder extends RecyclerView.ViewHolder { 26 private TextView tv; 27 28 public ViewHolder(TextView itemView) { 29 super(itemView); 30 tv = itemView; 31 } 32 33 public TextView getTv() { 34 return tv; 35 } 36 } 37 38 /** 39 * 创建ViewHolder 40 * @param viewGroup 41 * @param i 42 * @return 43 */ 44 @Override 45 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { 46 return new ViewHolder(new TextView(viewGroup.getContext())); 47 } 48 49 /** 50 * 对上面自己创建的ViewHolder携带数据进行处理 51 * @param viewHolder 52 * @param i 53 */ 54 @Override 55 public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) { 56 ViewHolder vh = (ViewHolder) viewHolder; 57 vh.getTv().setText("Item "+i); 58 } 59 60 /** 61 * 62 * 获取RecyclerView的子对象数量 63 */ 64 @Override 65 public int getItemCount() { 66 return 100; 67 } 68 } ); 69 } 70 71 72 }
布署程序到模拟器上,如下:(可以上下滑动滚动)
(2)利用数组承载数据源:
1 package com.example.hebao.learnrv; 2 3 import android.support.v7.app.AppCompatActivity; 4 import android.os.Bundle; 5 import android.support.v7.widget.LinearLayoutManager; 6 import android.support.v7.widget.RecyclerView; 7 import android.view.Menu; 8 import android.view.MenuItem; 9 import android.view.View; 10 import android.view.ViewGroup; 11 import android.widget.TextView; 12 13 public class MainActivity extends AppCompatActivity { 14 15 private RecyclerView rv; 16 @Override 17 protected void onCreate(Bundle savedInstanceState) { 18 super.onCreate(savedInstanceState); 19 rv = new RecyclerView(this); 20 21 setContentView(rv); 22 23 rv.setLayoutManager(new LinearLayoutManager(this)); 24 rv.setAdapter(new RecyclerView.Adapter(){ 25 private String[] data = new String[] {"hello", "二胎时代","九阴真经"}; 26 class ViewHolder extends RecyclerView.ViewHolder { 27 private TextView tv; 28 29 public ViewHolder(TextView itemView) { 30 super(itemView); 31 tv = itemView; 32 } 33 34 public TextView getTv() { 35 return tv; 36 } 37 } 38 39 /** 40 * 创建ViewHolder 41 * @param viewGroup 42 * @param i 43 * @return 44 */ 45 @Override 46 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { 47 return new ViewHolder(new TextView(viewGroup.getContext())); 48 } 49 50 /** 51 * 对上面自己创建的ViewHolder携带数据进行处理 52 * @param viewHolder 53 * @param i 54 */ 55 @Override 56 public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) { 57 ViewHolder vh = (ViewHolder) viewHolder; 58 vh.getTv().setText(data[i]); 59 } 60 61 /** 62 * 63 * 获取RecyclerView的子对象数量 64 */ 65 @Override 66 public int getItemCount() { 67 return data.length; 68 } 69 }); 70 } 71 72 73 }
布署程序到模拟器上,如下:
1 package com.example.hebao.learnrv; 2 3 import android.support.v7.app.AppCompatActivity; 4 import android.os.Bundle; 5 import android.support.v7.widget.LinearLayoutManager; 6 import android.support.v7.widget.RecyclerView; 7 8 public class MainActivity extends AppCompatActivity { 9 10 private RecyclerView rv; 11 @Override 12 protected void onCreate(Bundle savedInstanceState) { 13 super.onCreate(savedInstanceState); 14 rv = new RecyclerView(this); 15 16 setContentView(rv); 17 18 rv.setLayoutManager(new LinearLayoutManager(this)); 19 rv.setAdapter(new MyAdapter()); 20 } 21 22 23 }
(2)此时MyAdapter,如下:
1 package com.example.hebao.learnrv; 2 3 import android.support.v7.widget.RecyclerView; 4 import android.view.LayoutInflater; 5 import android.view.View; 6 import android.view.ViewGroup; 7 import android.widget.TextView; 8 9 /** 10 * Created by hebao on 11/16/15. 11 */ 12 class MyAdapter extends RecyclerView.Adapter { 13 private ItemData[] data = new ItemData[] {new ItemData("东邪", "黄药师"), new ItemData("西狂","杨过")}; 14 15 class ViewHolder extends RecyclerView.ViewHolder { 16 private View root; 17 private TextView tvTitle, tvContent; 18 19 public ViewHolder(View root) { 20 super(root); 21 tvTitle = (TextView) root.findViewById(R.id.tvTitle); 22 tvContent = (TextView) root.findViewById(R.id.tvContent); 23 } 24 25 public TextView getTvContent() { 26 return tvContent; 27 } 28 29 public TextView getTvTitle() { 30 return tvTitle; 31 } 32 } 33 34 /** 35 * 创建ViewHolder 36 * 37 * @param viewGroup 38 * @param i 39 * @return 40 */ 41 @Override 42 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { 43 return new ViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.list_cell, 44 null)); 45 } 46 47 /** 48 * 对上面自己创建的ViewHolder携带数据进行处理 49 * 50 * @param viewHolder 51 * @param i 52 */ 53 @Override 54 public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) { 55 ViewHolder vh = (ViewHolder) viewHolder; 56 ItemData id = data[i]; 57 vh.getTvTitle().setText(id.title); 58 vh.getTvContent().setText(id.content); 59 60 } 61 62 /** 63 * 获取RecyclerView的子对象数量 64 */ 65 @Override 66 public int getItemCount() { 67 return data.length; 68 } 69 70 71 72 }
此处我们用到了一个实体数据类ItemData,如下:
1 package com.example.hebao.learnrv; 2 3 /** 4 * Created by hebao on 11/16/15. 5 */ 6 public class ItemData { 7 public String title = "title"; 8 public String content ="content"; 9 10 public ItemData(String title, String content) { 11 this.title = title; 12 this.content = content; 13 } 14 }
自定义布局文件资源为list_cell.xml:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:orientation="vertical" android:layout_width="match_parent" 4 android:layout_height="match_parent"> 5 6 <TextView 7 android:layout_width="fill_parent" 8 android:layout_height="wrap_content" 9 android:textAppearance="?android:attr/textAppearanceLarge" 10 android:text="Large Text" 11 android:id="@+id/tvTitle" 12 android:layout_gravity="center_horizontal" /> 13 14 <TextView 15 android:layout_width="fill_parent" 16 android:layout_height="wrap_content" 17 android:text="New Text" 18 android:id="@+id/tvContent" 19 android:layout_gravity="center_horizontal" /> 20 </LinearLayout>
(3)程序布署到模拟器上,如下:
![](https://images2015.cnblogs.com/blog/757858/201511/757858-20151116110834593-1706697302.png)
这里我们需要用到RecyclerView.ItemDecoration给每一项Item视图添加子View,可以进行画分隔线之类的东西
那么具体如何使用呢
我们可以创建一个类继承RecyclerView.ItemDecoration类来绘制分隔线,通过ItemDecoration可 以让我们每一个Item从视觉上面相互分开来,实现一个ItemDecoration,系统提供的ItemDecoration是一个抽象类,因为当我们RecyclerView在进行绘制的时候会进行绘制。
下面是转载别人实现的RecycleViewDivider,如下:
1 package com.himi.recyclerviewdemo; 2 3 import android.content.Context; 4 import android.content.res.TypedArray; 5 import android.graphics.Canvas; 6 import android.graphics.Paint; 7 import android.graphics.Rect; 8 import android.graphics.drawable.Drawable; 9 import android.support.v4.content.ContextCompat; 10 import android.support.v7.widget.LinearLayoutManager; 11 import android.support.v7.widget.RecyclerView; 12 import android.support.v7.widget.RecyclerView.ItemDecoration; 13 import android.view.View; 14 15 public class RecycleViewDivider extends ItemDecoration { 16 private Paint mPaint; 17 private Drawable mDivider; 18 private int mDividerHeight = 2;//分割线高度,默认为1px 19 private int mOrientation;//列表的方向:LinearLayoutManager.VERTICAL或LinearLayoutManager.HORIZONTAL 20 private static final int[] ATTRS = new int[]{android.R.attr.listDivider}; 21 22 /** 23 * 默认分割线:高度为2px,颜色为灰色 24 * 25 * @param context 26 * @param orientation 列表方向 27 */ 28 public RecycleViewDivider(Context context, int orientation) { 29 if (orientation != LinearLayoutManager.VERTICAL && orientation != LinearLayoutManager.HORIZONTAL) { 30 throw new IllegalArgumentException("请输入正确的参数!"); 31 } 32 mOrientation = orientation; 33 34 final TypedArray a = context.obtainStyledAttributes(ATTRS); 35 mDivider = a.getDrawable(0); 36 a.recycle(); 37 } 38 39 /** 40 * 自定义分割线 41 * 42 * @param context 43 * @param orientation 列表方向 44 * @param drawableId 分割线图片 45 */ 46 public RecycleViewDivider(Context context, int orientation, int drawableId) { 47 this(context, orientation); 48 mDivider = ContextCompat.getDrawable(context, drawableId); 49 mDividerHeight = mDivider.getIntrinsicHeight(); 50 } 51 52 /** 53 * 自定义分割线 54 * 55 * @param context 56 * @param orientation 列表方向 57 * @param dividerHeight 分割线高度 58 * @param dividerColor 分割线颜色 59 */ 60 public RecycleViewDivider(Context context, int orientation, int dividerHeight, int dividerColor) { 61 this(context, orientation); 62 mDividerHeight = dividerHeight; 63 mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 64 mPaint.setColor(dividerColor); 65 mPaint.setStyle(Paint.Style.FILL); 66 } 67 68 69 //获取分割线尺寸 70 @Override 71 public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 72 super.getItemOffsets(outRect, view, parent, state); 73 outRect.set(0, 0, 0, mDividerHeight); 74 } 75 76 //绘制分割线 77 @Override 78 public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { 79 super.onDraw(c, parent, state); 80 if (mOrientation == LinearLayoutManager.VERTICAL) { 81 drawVertical(c, parent); 82 } else { 83 drawHorizontal(c, parent); 84 } 85 } 86 87 //绘制横向 item 分割线 88 private void drawHorizontal(Canvas canvas, RecyclerView parent) { 89 final int left = parent.getPaddingLeft(); 90 final int right = parent.getMeasuredWidth() - parent.getPaddingRight(); 91 final int childSize = parent.getChildCount(); 92 for (int i = 0; i < childSize; i++) { 93 final View child = parent.getChildAt(i); 94 RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams(); 95 final int top = child.getBottom() + layoutParams.bottomMargin; 96 final int bottom = top + mDividerHeight; 97 if (mDivider != null) { 98 mDivider.setBounds(left, top, right, bottom); 99 mDivider.draw(canvas); 100 } 101 if (mPaint != null) { 102 canvas.drawRect(left, top, right, bottom, mPaint); 103 } 104 } 105 } 106 107 //绘制纵向 item 分割线 108 private void drawVertical(Canvas canvas, RecyclerView parent) { 109 final int top = parent.getPaddingTop(); 110 final int bottom = parent.getMeasuredHeight() - parent.getPaddingBottom(); 111 final int childSize = parent.getChildCount(); 112 for (int i = 0; i < childSize; i++) { 113 final View child = parent.getChildAt(i); 114 RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams(); 115 final int left = child.getRight() + layoutParams.rightMargin; 116 final int right = left + mDividerHeight; 117 if (mDivider != null) { 118 mDivider.setBounds(left, top, right, bottom); 119 mDivider.draw(canvas); 120 } 121 if (mPaint != null) { 122 canvas.drawRect(left, top, right, bottom, mPaint); 123 } 124 } 125 } 126 }
使用方法:
添加默认分割线:高度为2px,颜色为灰色
mRecyclerView.addItemDecoration(new RecycleViewDivider(mContext, LinearLayoutManager.VERTICAL));
添加自定义分割线:可自定义分割线drawable
mRecyclerView.addItemDecoration(new RecycleViewDivider(mContext, LinearLayoutManager.VERTICAL, R.drawable.divider_mileage));
添加自定义分割线:可自定义分割线高度和颜色
mRecyclerView.addItemDecoration(new RecycleViewDivider(mContext, LinearLayoutManager.VERTICAL, 10, getResources().getColor(R.color.divide_gray_color)));
使用很简单,这里我就不添加代码实现了。
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:orientation="vertical" android:layout_width="match_parent" 4 android:layout_height="match_parent"> 5 6 <TextView 7 android:layout_width="wrap_content" 8 android:layout_height="wrap_content" 9 android:textAppearance="?android:attr/textAppearanceLarge" 10 android:text="Large Text" 11 android:id="@+id/tvTitle" 12 android:layout_gravity="center_horizontal" /> 13 14 <TextView 15 android:layout_width="wrap_content" 16 android:layout_height="wrap_content" 17 android:text="New Text" 18 android:id="@+id/tvContent" 19 android:layout_gravity="center_horizontal" /> 20 </LinearLayout>
同时来到MainActivity,如下:
1 package com.example.hebao.learnrv; 2 3 import android.support.v7.app.AppCompatActivity; 4 import android.os.Bundle; 5 import android.support.v7.widget.LinearLayoutManager; 6 import android.support.v7.widget.RecyclerView; 7 8 public class MainActivity extends AppCompatActivity { 9 10 private RecyclerView rv; 11 @Override 12 protected void onCreate(Bundle savedInstanceState) { 13 super.onCreate(savedInstanceState); 14 rv = new RecyclerView(this); 15 16 setContentView(rv); 17 //设置RecyclerView为横向布局 18 rv.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false)); 19 rv.setAdapter(new MyAdapter()); 20 } 21 22 23 }
布署程序到模拟器上,如下:
![](https://images2015.cnblogs.com/blog/757858/201511/757858-20151116122243530-608031947.png)