继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

MaterialDesign学习篇(八),掌握RecyclerView和SwipeRefreshLayout

chaychan
关注TA
已关注
手记 54
粉丝 62
获赞 487

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到此就结束了,需要源码的可以查看:

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP