RecyclerView替换ListView(包含SwipeRefreshLayout)

来源:互联网 发布:hype淘宝哪家店是正品 编辑:程序博客网 时间:2024/05/08 03:37

    在android.support.v7包中,提供了一个新控件RecyclerView,是Android 5.0 materials design中的组件之一,用来替换ListView、GridView。本文主要讲解RecycleView的基本用法,以及如何配合SwipeRefreshLayout实现下拉刷新,上拉加载的功能。SwipeRefreshLayout是android.support.v4包中提供的。

一、RecyclerView的基本介绍

//设置适配器m_recyclerView.setAdapter(m_adapter);//设置布局方式m_recyclerView.setLayoutManager(m_layoutManager);//设置分割线m_recyclerView.addItemDecoration(decoration);//设置Item切换的动画m_recyclerView.setItemAnimator(new DefaultItemAnimator());
这是RecycleView使用的四个基本步骤,看起来是比ListView麻烦点,但是它的功能更加强大。


1、RecycleView.LayoutManager

  

   LinearLayoutManager 相当于ListView;


   GridLayoutManager 相当于GridView;


   StaggeredGridLayoutManager 相当于瀑布流。


   这三种LayoutManager都有含有orientation参数的构造方法,这个参数用来判断RecycleView是竖直的还是水


平的。所有用RecycleView来实现水平的ListView和GridView更加方便。   


2、RecycleView.ItemDecoration


   相对于ListView设置分割线这方面而言,RecycleView没有提供分割线,但是提供了ItemDecoration这个抽象


类给我们自定义分割线的样式。


以下提供几种分割线的实现方式:


(1)设置每个Item与周边的边距,RecycleView的父布局的颜色就成了分割线的颜色。(以上一种LayoutManager


通用)

public class SpacesItemDecoration extends RecyclerView.ItemDecoration {    private int space;    public SpacesItemDecoration(int space) {        this.space = space;    }    /**     * 设置每个Item与周边的间距     */    @Override    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {        outRect.left = space;        outRect.right = space;        outRect.bottom = space;        if (parent.getChildAdapterPosition(view) == 0 || parent.getChildAdapterPosition(view) == 2 || parent.getChildAdapterPosition(view) == 1) {            outRect.top = space;        }    }}
(2)可以看到通过读取系统主题中的 android.R.attr.listDivider作为Item间的分割线,并且支持横向和纵向。以下是LinearLayoutManager作为LayoutManager时的方式。

public class DividerListItemDecoration extends RecyclerView.ItemDecoration {    private static final int[] ATTRS = new int[]{            android.R.attr.listDivider    };    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;    private Drawable mDivider;    private int mOrientation;    public DividerListItemDecoration(Context context, int orientation) {        final TypedArray a = context.obtainStyledAttributes(ATTRS);        mDivider = a.getDrawable(0);        a.recycle();        setOrientation(orientation);    }    public void setOrientation(int orientation) {        if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {            throw new IllegalArgumentException("invalid orientation");        }        mOrientation = orientation;    }    @Override    public void onDraw(Canvas c, RecyclerView parent) {        if (mOrientation == VERTICAL_LIST) {            drawVertical(c, parent);        } else {            drawHorizontal(c, parent);        }    }    public void drawVertical(Canvas c, RecyclerView parent) {        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);            android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext());            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);        }    }    public void drawHorizontal(Canvas c, RecyclerView parent) {        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();            mDivider.setBounds(left, top, right, bottom);            mDivider.draw(c);        }    }    @Override    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {        if (mOrientation == VERTICAL_LIST) {            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());        } else {            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);        }    }}
(3)GridLayoutManager作为LayoutManager时,分割线的处理。

public class DividerGridItemDecoration extends RecyclerView.ItemDecoration{        private static final int[] ATTRS = new int[] { android.R.attr.listDivider };        private Drawable mDivider;        public DividerGridItemDecoration(Context context)        {            final TypedArray a = context.obtainStyledAttributes(ATTRS);            mDivider = a.getDrawable(0);            a.recycle();        }        @Override        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state)        {            drawHorizontal(c, parent);            drawVertical(c, parent);        }        private int getSpanCount(RecyclerView parent)        {            // 列数            int spanCount = -1;            RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();            if (layoutManager instanceof GridLayoutManager)            {                spanCount = ((GridLayoutManager) layoutManager).getSpanCount();            } else if (layoutManager instanceof StaggeredGridLayoutManager)            {                spanCount = ((StaggeredGridLayoutManager) layoutManager)                        .getSpanCount();            }            return spanCount;        }        public void drawHorizontal(Canvas c, RecyclerView parent)        {            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.getLeft() - params.leftMargin;                final int right = child.getRight() + params.rightMargin                        + mDivider.getIntrinsicWidth();                final int top = child.getBottom() + params.bottomMargin;                final int bottom = top + mDivider.getIntrinsicHeight();                mDivider.setBounds(left, top, right, bottom);                mDivider.draw(c);            }        }        public void drawVertical(Canvas c, RecyclerView parent)        {            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.getTop() - params.topMargin;                final int bottom = child.getBottom() + params.bottomMargin;                final int left = child.getRight() + params.rightMargin;                final int right = left + mDivider.getIntrinsicWidth();                mDivider.setBounds(left, top, right, bottom);                mDivider.draw(c);            }        }        private boolean isLastColum(RecyclerView parent, int pos, int spanCount,                                    int childCount)        {            RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();            if (layoutManager instanceof GridLayoutManager)            {                if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边                {                    return true;                }            } else if (layoutManager instanceof StaggeredGridLayoutManager)            {                int orientation = ((StaggeredGridLayoutManager) layoutManager)                        .getOrientation();                if (orientation == StaggeredGridLayoutManager.VERTICAL)                {                    if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边                    {                        return true;                    }                } else                {                    childCount = childCount - childCount % spanCount;                    if (pos >= childCount)// 如果是最后一列,则不需要绘制右边                        return true;                }            }            return false;        }        private boolean isLastRaw(RecyclerView parent, int pos, int spanCount,                                  int childCount)        {            RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();            if (layoutManager instanceof GridLayoutManager)            {                childCount = childCount - childCount % spanCount;                if (pos >= childCount)// 如果是最后一行,则不需要绘制底部                    return true;            } else if (layoutManager instanceof StaggeredGridLayoutManager)            {                int orientation = ((StaggeredGridLayoutManager) layoutManager)                        .getOrientation();                // StaggeredGridLayoutManager 且纵向滚动                if (orientation == StaggeredGridLayoutManager.VERTICAL)                {                    childCount = childCount - childCount % spanCount;                    // 如果是最后一行,则不需要绘制底部                    if (pos >= childCount)                        return true;                } else                // StaggeredGridLayoutManager 且横向滚动                {                    // 如果是最后一行,则不需要绘制底部                    if ((pos + 1) % spanCount == 0)                    {                        return true;                    }                }            }            return false;        }        @Override        public void getItemOffsets(Rect outRect, int itemPosition,                                   RecyclerView parent)        {            int spanCount = getSpanCount(parent);            int childCount = parent.getAdapter().getItemCount();            if (isLastRaw(parent, itemPosition, spanCount, childCount))// 如果是最后一行,则不需要绘制底部            {                outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);            } else if (isLastColum(parent, itemPosition, spanCount, childCount))// 如果是最后一列,则不需要绘制右边            {                outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());            } else            {                outRect.set(0, 0, mDivider.getIntrinsicWidth(),                        mDivider.getIntrinsicHeight());            }        }}
参考http://blog.csdn.net/lmj623565791/article/details/45059587

3、RecycleView.ItemAnimator

这也是个抽象类,用来处理RecycleView的Item删除和插入时的动画,不过SDK给我们提供了一个默认的实现类DefaultItemAnimator。

4、到了RecycleView四个步骤中最重要的一步了,setAdapter。与ListView和GridView一样,也需要设置适配器,不过这里并不是继承BaseAdapter了,而是RecycleView.Adapter。之前ListView的适配器中ViewHolder的使用不是强制性的,在RecycleView中已经是必须得写的了。

public class MyRecyclerAdapter extends RecyclerView.Adapter<MyRecyclerAdapter.MyViewHolder> {    private Context context;    public MyRecyclerAdapter(List<String> list, Context context) {        this.context = context;    }    @Override    public MyRecyclerAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        View view = LayoutInflater.from(context).inflate(R.layout.content_main, null);        MyViewHolder myViewHolder = new MyViewHolder(view);        return myViewHolder;    }    @Override    public void onBindViewHolder(final MyRecyclerAdapter.MyViewHolder holder, final int position) {        holder.textView.setText("第"+position+"个Item");    }    @Override    public int getItemCount() {        return 30;    }    class MyViewHolder extends RecyclerView.ViewHolder {        TextView textView;        CardView cardView;        public MyViewHolder(View itemView) {            super(itemView);            textView = (TextView) itemView.findViewById(R.id.textView);        }    }}
重写这个适配器,主要实现三个方法、写一个ViewHolder。

(1)onCreateViewHolder  创建ViewHolder

(2)onBindViewHolder      将ViewHolder与数据源绑定

(3)getItemCount             Item的总是

实现以上四步,RecycleView的基本使用就掌握了,效果图如下:


二、RecycleView功能进阶。

1、点击事件、长按事件

RecycleView没有提供OnClickListener和OnLongClickListener,需要我们自己实现。

在自定义的Adapter中定义一个接口,代码如下:

private OnItemClickListener onItemClickListener;public interface OnItemClickListener {    void OnItemClick(View view, int position);    void OnItemLongClick(View view, int position);}public void setOnItemClickListener(OnItemClickListener onItemClickListener) {    this.onItemClickListener = onItemClickListener;}
在onBindViewHolder方法中调用接口中的方法

if (onItemClickListener != null) {    holder.textView.setOnClickListener(new View.OnClickListener() {        @Override        public void onClick(View v) {            int pos = holder.getLayoutPosition();            onItemClickListener.OnItemClick((((MyViewHolder) holder).textView), pos);        }    });    holder.textView.setOnLongClickListener(new View.OnLongClickListener() {        @Override        public boolean onLongClick(View v) {            int pos = holder.getLayoutPosition();            onItemClickListener.OnItemLongClick((((MyViewHolder) holder).textView), pos);            return false;        }    });}
在MainActivity中使用时,直接调用Adapter中的方法,实现这个接口。

m_adapter.setOnItemClickListener(new MyRecyclerAdapter.OnItemClickListener() {    @Override    public void OnItemClick(View view, int position) {        Toast.makeText(MainActivity.this, "点击了" + position, Toast.LENGTH_SHORT).show();    }    @Override    public void OnItemLongClick(View view, int position) {
Toast.makeText(MainActivity.this, "长按了" + position, Toast.LENGTH_SHORT).show();
}});

2、删除与插入

在自定义的Adapter中定义两个方法,如下:

public void addItem(int position) {    m_listData.add(position, "Insert One");    notifyItemInserted(position);}public void deleteItem(int position) {    m_listData.remove(position);    notifyItemRemoved(position);}
刷新适配器的方法调用上面notifyItemInserted(position)和notifyItemRemoved(position),这样才能执行之前设置的Item的Animation

三、RecycleView与SwipeRefreshLayout配合实现下拉刷新、加载更多。

1、下拉刷新

<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/swipe_refresh_layout"    android:layout_width="match_parent"    android:layout_height="match_parent">    <android.support.v7.widget.RecyclerView        android:id="@+id/recycler_view"        android:layout_width="match_parent"        android:layout_height="match_parent" /></android.support.v4.widget.SwipeRefreshLayout>
//设置下拉刷新进度条的颜色m_swipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_light,        android.R.color.holo_red_light, android.R.color.holo_orange_light,        android.R.color.holo_green_light);m_swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {    @Override    public void onRefresh() {        m_swipeRefreshLayout.postDelayed(new Runnable() {            @Override            public void run() {                m_swipeRefreshLayout.setRefreshing(false);                m_adapter.addItem(1);            }        }, 1000);    }});

直接这样使用,RecycleView的滚动和SwipeRefreshLayout的下拉冲突,造成每次下拉都会触发onRefreshListener。需要判断,只有当RecycleView滚动到顶部时,下拉才触发刷新的事件。

//通过对RecycleView的滚动状态进行监听,当第0个位置的Item的顶部的坐标大于或等于0时,将SwipeRefreshView设置成可以刷新的,反之设置成不可刷新的m_recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {    @Override    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {        super.onScrollStateChanged(recyclerView, newState);        //设置下拉刷新的事件        int topRowVerticalPosition = (recyclerView == null || recyclerView.getChildCount() == 0) ? 0 : recyclerView.getChildAt(0).getTop();        m_swipeRefreshLayout.setEnabled(topRowVerticalPosition >= 0);    }    @Override    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {        super.onScrolled(recyclerView, dx, dy);    }});
在OnScrollStateChanged中的那两句代码写的很好,判断当RecycleView的第一个Item的顶部的坐标等于0时将SwipeRefreshLayout设置成Enabled,反之不触发滚动事件。

2、加载更多

在ListView中有addFootView方法,RecycleView没有,所以需要我们自己判断。

在Adapter中有getItemViewType(position)方法,可以判断每个Item的类型。

@Overridepublic int getItemViewType(int position) {    if (position + 1 == getItemCount()) {        return TYPE_FOOTER;    } else {        return TYPE_ITEM;    }}
完整的Adapter类如下:

package com.example.newviewuser;import android.content.Context;import android.support.v7.widget.CardView;import android.support.v7.widget.RecyclerView;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;import java.util.ArrayList;import java.util.List;import java.util.Random;/** * Created by Administrator on 2016/1/25. */public class MyRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {    private Random random = new Random();    private List<String> m_listData;    private Context context;    private List<Integer> mHeights;    private static final int GENERAL_VIEW = 0;  //普通Item    private static final int FOOTER_VIEW = 1;  //最底部的Item    //上拉加载更多    public static final int  PULLUP_LOAD_MORE=0;    //正在加载中    public static final int  LOADING_MORE=1;    //上拉加载更多状态-默认为0    private int load_more_status=0;    private OnItemClickListener onItemClickListener;    public interface OnItemClickListener {        void OnItemClick(View view, int position);        void OnItemLongClick(View view, int position);    }    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {        this.onItemClickListener = onItemClickListener;    }    public MyRecyclerAdapter(List<String> list, Context context) {        m_listData = list;        this.context = context;        mHeights = new ArrayList<Integer>();        for (int i = 0; i < m_listData.size(); i++) {            mHeights.add((int) (100 + Math.random() * 300));        }    }    @Override    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        if (viewType == GENERAL_VIEW) {            View view = LayoutInflater.from(context).inflate(R.layout.content_main, null);            MyViewHolder myViewHolder = new MyViewHolder(view);            return myViewHolder;        } else if (viewType == FOOTER_VIEW) {            View view = LayoutInflater.from(context).inflate(R.layout.footer_layout, null);            FootViewHolder footViewHolder = new FootViewHolder(view);            return footViewHolder;        }        return null;    }    @Override    public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {        if (holder instanceof MyViewHolder) {            (((MyViewHolder) holder).textView).setText("第"+position+"个Item");            if (onItemClickListener != null) {                (((MyViewHolder) holder).textView).setOnClickListener(new View.OnClickListener() {                    @Override                    public void onClick(View v) {                        int pos = holder.getLayoutPosition();                        onItemClickListener.OnItemClick((((MyViewHolder) holder).textView), pos);                    }                });                (((MyViewHolder) holder).textView).setOnLongClickListener(new View.OnLongClickListener() {                    @Override                    public boolean onLongClick(View v) {                        int pos = holder.getLayoutPosition();                        onItemClickListener.OnItemLongClick((((MyViewHolder) holder).textView), pos);                        return false;                    }                });            }        }else if (holder instanceof FootViewHolder){            FootViewHolder footViewHolder = (FootViewHolder) holder;            switch (load_more_status){                case PULLUP_LOAD_MORE:                    footViewHolder.textView.setText("上拉加载更多...");                    break;                case LOADING_MORE:                    footViewHolder.textView.setText("正在加载更多数据...");                    break;            }        }    }    @Override    public int getItemCount() {        return m_listData.size() + 1;    }    @Override    public int getItemViewType(int position) {        if (position + 1 == getItemCount()) {            return FOOTER_VIEW;        } else {            return GENERAL_VIEW;        }    }    public void addItem(int position) {        m_listData.add(position, "Insert One");        if (mHeights.size() < m_listData.size()) {            mHeights.add((int) (100 + Math.random() * 300));        }        notifyItemInserted(position);    }    public void deleteItem(int position) {        m_listData.remove(position);        notifyItemRemoved(position);    }    public void addMoreItem(List<String> newDatas) {        m_listData.addAll(newDatas);        notifyDataSetChanged();    }    /**     * //上拉加载更多     * PULLUP_LOAD_MORE=0;     *  //正在加载中     * LOADING_MORE=1;     * //加载完成已经没有更多数据了     * NO_MORE_DATA=2;     * @param status     */    public void changeMoreStatus(int status){        load_more_status=status;        notifyDataSetChanged();    }    class MyViewHolder extends RecyclerView.ViewHolder {        TextView textView;        CardView cardView;        public MyViewHolder(View itemView) {            super(itemView);            textView = (TextView) itemView.findViewById(R.id.textView);        }    }    class FootViewHolder extends RecyclerView.ViewHolder {        TextView textView;        public FootViewHolder(View itemView) {            super(itemView);            textView = (TextView) itemView.findViewById(R.id.text_view_footer);        }    }}
在RecycleView滚动的监听中判断,当滚动底部时,显示加载更多的布局,改变FootView的状态。

m_recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {    @Override    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {        super.onScrollStateChanged(recyclerView, newState);        //设置下拉刷新的时间        int topRowVerticalPosition = (recyclerView == null || recyclerView.getChildCount() == 0) ? 0 : recyclerView.getChildAt(0).getTop();        m_swipeRefreshLayout.setEnabled(topRowVerticalPosition >= 0);        if (newState == RecyclerView.SCROLL_STATE_IDLE && lastVisibleItem +1 == m_adapter.getItemCount()) {            m_adapter.changeMoreStatus(MyRecyclerAdapter.LOADING_MORE);            new Handler().postDelayed(new Runnable() {                @Override                public void run() {                    List<String> addString = new ArrayList<String>();                    addString.add("加载更多增加的数据");                    m_adapter.addMoreItem(addString);                    m_adapter.changeMoreStatus(MyRecyclerAdapter.PULLUP_LOAD_MORE);                    m_adapter.notifyDataSetChanged();                }            },2500);        }    }    @Override    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {        super.onScrolled(recyclerView, dx, dy);        lastVisibleItem = m_layoutManager.findLastVisibleItemPosition();    }});
下载源码

0 0