RecyclerView介绍
RecyclerView是Android5.0添加的一个用于取代ListView的控件,它的灵活性比ListView和GridView更加优秀,ListView和GridView能够做到的,它都可以做到,可以说是ListView和GridView的升级版,但是它的使用又和ListView与GridView有些不同,下面将会对此进行介绍。
如何使用RecyclerView
先看下演示效果:
列表布局:
网格布局:
瀑布流布局:
可以看到一个RecyclerView就可以实现列表、网格和瀑布流的布局,并且还有纵向和水平两个方向的摆放,这些都是由LayoutManager(布局管理器)控制的,在使用RecyclerView之前,我们需要先了解LayoutManager
LayoutManager(布局管理器)介绍
RecyclerView的使用和ListView大同小异,也是需要设置Adapter(适配器),但是在设置适配器之前,需要先设置LayoutManager(布局管理器),LayoutManager用来确定每一个item如何进行排列摆放,何时展示和隐藏。回收或重用一个View的时候,LayoutManager会向适配器请求新的数据来替换旧的数据,这种机制避免了创建过多的View和频繁的调用findViewById方法(与ListView原理类似)。
RecyclerView的三种LayoutManager(布局管理器)
-
LinearLayoutManager(线性布局管理器)
-
GridLayoutManager(网格布局管理器)
-
StaggeredGridLayoutManager(瀑布流布局管理器)
以上三种布局管理器均可以设置垂直和水平两个方向,效果对应以上三张图,接下来开始介绍RecyclerView的使用。
导入依赖
compile 'com.android.support:recyclerview-v7:26.0.0-alpha1'
布局文件中引用RecyclerView
<?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="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
java文件中,找到对应的RecyclerView,对其设置布局管理器和设置Adapter
mRv = (RecyclerView) findViewById(R.id.rv);
MyListAdapter adapter = new MyListAdapter(this, mDatas);
LinearLayoutManager layoutManger = new LinearLayoutManager(this, LinearLayout.VERTICAL, false);
mRv.setLayoutManager(layoutManger);
mRv.setAdapter(adapter);
LinearLayoutManager的使用
一般使用LinearLayoutManager的这两个构造方法:
单个参数的构造方法,传入的是上下文,方向是默认的垂直方向:
new LinearLayoutManager(Context context)
三个参数的构造方法,分别传入上下文,方向,第三个参数是布尔类型的值,表示是否反转,不反转的垂直布局:数据从上到下加载,加载新数据,新数据在底部,滑动从下往上。
new LinearLayoutManager( Context context, int orientation, boolean reverseLayout)
这里我们使用第二个构造方法,第三个参数传入false,不反转,第二个参数根据选择的方向传入LinearLayout.VERTICAL或 LinearLayout.HORIZONTAL。
当点击切换成水平方向的列表布局时,调用以下代码:
MyListAdapter adapter = new MyListAdapter(this, mDatas);
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayout.HORIZONTAL, false);
mRv.setLayoutManager(layoutManager);
mRv.setAdapter(adapter);
改变了LinearLayoutManager的方向,变成横向列表展示。
GridLayoutManager的使用
一般使用GridLayoutManager的这两个构造方法:
两个参数的构造方法,第一个参数是上下文,第二个参数设置显示的列数,方向默认为垂直方向:
new GridLayoutManager(Context context, int spanCount)
四个参数的构造方法,第一个参数是上下文,第二个参数设置显示的列数,第三个参数为方向,第四个参数为是否反转:
new GridLayoutManager( Context context, int spanCount, int orientation, boolean reverseLayout)
纵向方向网格布局是设置以下代码:
GridLayoutManager layoutManager = new GridLayoutManager(this,2, GridLayout.VERTICAL, false);
mRv.setLayoutManager(layoutManager);
MyListAdapter adapter = new MyListAdapter(this, mDatas);
mRv.setAdapter(adapter);
当点击切换成水平方向的网格布局时:
GridLayoutManager layoutManager = new GridLayoutManager(this,2,GridLayout.HORIZONTAL, false);
mRv.setLayoutManager(layoutManager);
MyListAdapter adapter = new MyListAdapter(this, mDatas);
mRv.setAdapter(adapter);
StaggeredGridLayoutManager的使用
两个参数的构造方法,第一个参数是显示的列数,第二个参数是方向:
new StaggeredGridLayoutManager(int spanCount, int orientation);
纵向瀑布流布局是设置以下代码:
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);
mRv.setLayoutManager(layoutManager);
MyStaggerAdapter adapter = new MyStaggerAdapter(this, mStaggerDatas);
mRv.setAdapter(adapter);
当点击切换成水平方向的瀑布流布局时:
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.HORIZONTAL);
mRv.setLayoutManager(layoutManager);
MyStaggerAdapter adapter = new MyStaggerAdapter(this, mStaggerDatas);
mRv.setAdapter(adapter);
RecyclerView的Adapter
public class MyListAdapter extends RecyclerView.Adapter<MyListAdapter.MyHolder> {
private List<DataBean> list;
private Context context;
public MyListAdapter(Context context, List<DataBean> list) {
this.list = list;
this.context = context;
}
/**创建条目布局*/
@Override
public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = View.inflate(context, R.layout.item_list, null);
return new MyHolder(view);
}
/**绑定数据*/
@Override
public void onBindViewHolder(MyHolder myHolder, int position) {
myHolder.setDataAndRefreshUI(list.get(position));
}
@Override
public int getItemCount() {
return list.size();
}
public class MyHolder extends RecyclerView.ViewHolder{
private ImageView mIv;
private TextView mTv;
public MyHolder(View itemView) {
super(itemView);
mIv = (ImageView) itemView.findViewById(R.id.iv_icon);
mTv = (TextView) itemView.findViewById(R.id.tv_name);
}
public void setDataAndRefreshUI(DataBean dataBean){
mIv.setImageResource(dataBean.iconId);
mTv.setText(dataBean.content);
}
}
}
定义MyListAdapter继承RecyclerView.Adapter,泛型中需要传入RecyclerView.ViewHolder的子类,我们定义了MyHolder继承RecyclerView.ViewHolder,在ViewHolder中,构造函数需要传入View对象,即子条目的根View,接着我们做了子条目view各个控件的初始化操作和定义了setDataAndRefreshUI()方法,需要传入对应的bean类,并为相应的控件设置数据。
在Adapter需要重写onCreateViewHolder()返回对应的ViewHolder对象,还需要重写onBindViewHolder()方法,绑定数据,通过调用MyHolder中的setDataAndRefreshUI()方法,传入对应position的bean类;重写getItemCount()方法,返回对应条目的个数,这里返回集合的大小。
从Adapter的写法和思想可以看出和ListView的Adapter很相似,定义一个Adapter也不是一件很难的事情。
Item的点击事件
由于RecyclerView不再负责Item视图的布局及显示,所以RecyclerView也没有为Item开放OnItemClick等点击事件,这就需要开发者自己实现,在这里我们可以模拟ListView的setOnItemClickListener(),在Adapter中创建setOnItemClickListener(),并创建一个OnItemClickListener接口,用于回调ViewHolder的根View的点击事件。
一、创建子条目点击回调的接口:
public interface MyItemClickListener {
public void onItemClick(View view,int postion);
}
二、创建接口成员变量:
private MyItemClickListener mOnItemClickListener;
三、创建对应set方法:
public void setOnItemClickListener(MyItemClickListener listener){
this.mOnItemClickListener = listener;
}
四、在对应ViewHolder根View点击事件中回调:
public MyHolder(View itemView) {
super(itemView);
mIv = (ImageView) itemView.findViewById(R.id.iv_icon);
mTv = (TextView) itemView.findViewById(R.id.tv_name);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mOnItemClickListener != null){
mOnItemClickListener.onItemClick(v,getPosition());
}
}
});
}
五、在代码中,调用Adapter的setOnItemClickListener()获得子条目点击的回调:
private void initListener() {
mListAdapter.setOnItemClickListener(new MyListAdapter.MyItemClickListener() {
@Override
public void onItemClick(View view, int postion) {
Toast.makeText(RecyclerViewActivity.this, mDatas.get(postion).content, Toast.LENGTH_SHORT).show();
}
});
}
看下效果:
可以看到,子条目的点击事件已经可以像ListView一样,通过设置回调,在回调中做相应的处理就完成了,同理,子条目长按的事件也可以通过像上面的操作实现,在这里就不再做演示了,这里只是简单介绍下思想,有兴趣的话你可以尝试去实现下。
这里向大家推荐下一个很好用的开源框架,RecyclerView万能适配器,它可以省去编写Adapter的大部分代码,使用起来非常方便,特别是多Item布局使用,只需要简单的操作就可以实现,它的github地址是
RecyclerView的分割线
使用ListView的时候,当我们想添加条目之间的分割线时,我们只需要在ListView中配置divider,简单的两行配置:
android:divider="#fffff" 分割线颜色
android:dividerHeight="1px" 分割线高度
但是RecyclerView却没有提供这些配置,而是提供了方法
recyclerView.addItemDecoration()
需要自己定制ItemDecoration,有别于ListView,ItemDecoration这种可插拔设计不仅好用,而且功能强大,ItemDecoration中主要有以下三个方法:
public void onDraw(Canvas c, RecyclerView parent, State state) //可以实现类似绘制背景的效果,内容在上面
public void onDrawOver(Canvas c, RecyclerView parent, State state) //可以绘制在内容的上面,覆盖内容
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)//可以实现类似padding的效果
绘制分割线
要实现分割线效果需要重写onDraw()和getItemOffsets()这两个方法:
public class MyDividerItemDecoration extends RecyclerView.ItemDecoration{
private Paint mPaint;
private int mDividerHeight;
public MyDividerItemDecoration(Context context, int dividerHeight, int dividerColor){
mDividerHeight = dividerHeight;
mPaint = new Paint();
mPaint.setColor(dividerColor);
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
outRect.bottom = mDividerHeight;//矩形的底部赋值分割线的高度
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
int childCount = parent.getChildCount();//获取到子View的个数
int left = parent.getPaddingLeft();
int right = parent.getWidth() - parent.getPaddingRight();
for (int i = 0; i < childCount - 1; i++) {
View view = parent.getChildAt(i);
float top = view.getBottom();
float bottom = view.getBottom() + mDividerHeight;//子View底部添加分割线的高度
c.drawRect(left, top, right, bottom, mPaint);//绘制
}
}
}
这里简单实现了绘制分割线,其中,分割线的高度和颜色可以通过构造方法传入,代码中使用:
mRv.addItemDecoration(new MyDividerItemDecoration(this,1,getResources().getColor(R.color.divider)));
看下未添加分割线和添加分割线的效果
这里只是简单教了如何绘制分割线,对于条目之间美化和修饰,可以通过ItemDecoration实现,这些需要去学习和探索。
SwipeRefreshLayout介绍
SwipeRefreshLayout是Google提供的一个官方的下拉刷新控件,该控件和以往的下拉刷新控件不同,第一次看到的时候觉得让人耳目一新,现在大部分的App都使用SwipeRefreshLayout作为下拉刷新控件,因为它不仅看起来美观,使用起来也是相当方便。
SwipeRefreshLayout在support v4包中,要想使用它,需要导入v4包的依赖,AS创建项目的时候默认有添加v4包的依赖,所以我们可以直接使用SwipeRefreshLayout。
布局文件中,使用SwipeRefreshLayout
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/srl"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v4.widget.SwipeRefreshLayout>
和ScrollView的使用相似,SwipeRefreshLayout只能有一个直接子View,如果使用其他布局,比如不是SwipeRefreshLayout + RecyclerView,那么可以使用一个ViewGroup将布局好的控件包裹起来,最外层再用SwipeRefreshLayout包裹即可。
添加下拉刷新监听
mSrlRoot = (SwipeRefreshLayout) findViewById(R.id.srl);
mSrlRoot.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mSrlRoot.setRefreshing(false);//收起下拉刷新
Toast.makeText(RecyclerViewActivity1.this, "刷新完毕", Toast.LENGTH_SHORT).show();
}
},2000);
}
});
效果如下:
可以看到,当RecyclerView处于顶部时,向下拉动,会出现一个小圆圈在转动,这就是SwipeRefreshLayout控件的样子,我们在下拉刷新的回调中,简单模拟了刷新的过程,延时2秒后收起下拉刷新,并弹出吐司,显示“刷新完毕”,可以看到SwipeRefreshLayou的出现、加载中和消失动画看起来也是很不错的。
修改SwipeRefreshLayout显示的颜色
上图我们看到了SwipeRefreshLayout的样子,是一个小圆圈,Google为我们提供了可定制化修改的属性,比如修改它的颜色:
mSrlRoot.setColorSchemeColors(Color.RED);
上面代码设置它的颜色为红色,看下效果:
可以看到,SwipeRefreshLayout的小圈圈变成了红色。SwipeRefreshLayout设置颜色的方法setColorSchemeColors(int…colors),参数是可变参数,接收一个或多个颜色,如果我们传入多个颜色,将会是什么效果呢?
mSrlRoot.setColorSchemeColors(Color.RED,Color.GREEN,Color.BLUE);
这里我们设置了红绿蓝三个颜色,效果如下:
可以看到,加载中时,小圈圈交替变换红绿蓝这三种颜色,变得多姿多彩。
好了,本篇介绍RecyclerView和SwipeRefreshLayout到此就结束了,需要源码的可以查看: