Android学习之RecyclerView学习

来源:互联网 发布:服务器与数据库的区别 编辑:程序博客网 时间:2024/05/17 08:33

内容概括

  • 给recyclerView添加分割线
  • 给recyclerView添加HeaderView和FootView
  • 给recyclerView添加刷新功能,这里使用的是Android系统的SwipeRefreshLayout
  • recyclerView子项的点击事件【三种实现方式】

RecyclerView出来很长时间,但一直没有用在项目中,这个项目打算全部使用RecyclerView代替ListView,所以学习了一波,把看到的整理出来

RecyclerView比ListView更灵活,可以动态设置布局方向,也可以动态切换成ListView gridview以及瀑布流形式

    -

基础的使用

添加依赖

compile 'com.android.support:recyclerview-v7:24.0.0-beta1'

xml布局中像使用正常控件一样添加

<android.support.v7.widget.RecyclerView        android:id="@+id/recycler"        android:layout_width="match_parent"        android:layout_height="match_parent"        />

初始化视图

  mRecyclerView = (RecyclerView) findViewById(R.id.recycler);   linear = new LinearLayoutManager(this);        //设置布局管理器,这里设置为线性布局        mRecyclerView.setLayoutManager(linear);        //设置布局方向        linear.setOrientation(LinearLayoutManager.VERTICAL);        //设置适配器        adpt = new MyAdapter(this, mList);        //设置适配器        mRecyclerView.setAdapter(adpt);

初始化数据

private void initDatas() {        mList = new ArrayList<>();        for (int i = 0; i < 20; i++) {            mList.add("item" + i);        }    }

RecyclerView与ListView的适配器不同,需要继承自RecyclerView的Adapter

代码

package com.cd.recyclerviewdemo;import android.content.Context;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.List;/** * Author Chendan * Create Date 2016/9/9. * Description: */public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyVeiwHold> {    Context mContext;    List<String> mList;    //构造方法,初始化数据    public MyAdapter(Context context, List<String> mlist) {        mContext = context;        mList = mlist;    }    //初始化控件    @Override    public MyVeiwHold onCreateViewHolder(ViewGroup parent, int viewType) {        View layout = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycler, parent, false);        return new MyVeiwHold(layout);    }    //绑定控件    @Override    public void onBindViewHolder(MyVeiwHold holder, final int position) { ((MyVeiwHold)holder).mTextView.setText(mList.get(position ));    /**     * @return     */    @Override    public int getItemCount() {            return mList.size();    }    //    class MyVeiwHold extends RecyclerView.ViewHolder {        TextView mTextView;        public MyVeiwHold(View itemView) {            super(itemView);mTextView = (TextView) itemView.findViewById(R.id.item_tv);        }    }}

通过以上代码的编写,就能实现普通ListView展示的功能了

但是会发现子项之间没有分割线,于是需要特别为它写一个类去绘制分割线

    -

添加分割线

代码

package com.cd.recyclerviewdemo;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Rect;import android.graphics.drawable.Drawable;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import android.view.View;import android.widget.LinearLayout;import org.w3c.dom.Attr;/** * Author Chendan * Create Date 2016/9/9. * Description: */public class Decretion extends RecyclerView.ItemDecoration {    /**     * 成员变量     */    //上下文    Context mContext;    //线    private Drawable mdevider;    //方向    private int moritation;    //水平    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;    //垂直    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;    //获取系统属性    public static final int[] Attr = new int[]{android.R.attr.listDivider};    public Decretion(Context context, int oritation) {        mContext = context;        //通过上下文对象获取属性数组        TypedArray ta = context.obtainStyledAttributes(Attr);        //通过属性数组。获取对应属性获取线        this.mdevider = ta.getDrawable(0);        //回收属性        ta.recycle();        //初始化方向        setOritation(oritation);    }    private void setOritation(int oritation) {        //如果方向不为水平也不为垂直,则跑出非法异常        if (oritation != HORIZONTAL_LIST && oritation != VERTICAL_LIST) {            new IllegalArgumentException("invalid orientation");        } else {            moritation = oritation;        }    }    //    @Override    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {        super.onDraw(c, parent, state);        if (moritation == HORIZONTAL_LIST) {            drawVerticalLine(c, parent, state);        } else {            drawHorizontalLine(c, parent, state);        }    }    //绘制水平线    private void drawHorizontalLine(Canvas c, RecyclerView parent, RecyclerView.State state) {        //左边        int left = parent.getPaddingLeft();        //右边        int right = parent.getWidth() - parent.getPaddingRight();        //子视图数量        int childCount = parent.getChildCount();        for (int i = 0; i < childCount; i++) {            View child = parent.getChildAt(i);            RecyclerView.LayoutParams param = (RecyclerView.LayoutParams) child.getLayoutParams();            final int top = child.getBottom() + param.bottomMargin;            final int bottom = top + mdevider.getIntrinsicHeight();            mdevider.setBounds(left, top, right, bottom);            mdevider.draw(c);        }    }    //绘制垂直线    private void drawVerticalLine(Canvas c, RecyclerView parent, RecyclerView.State state) {        int top = parent.getPaddingTop();        int bottom = parent.getHeight() - parent.getPaddingBottom();        final int childCount = parent.getChildCount();        for (int i = 0; i < childCount; i++) {            final View child = parent.getChildAt(i);            //获得child的布局信息            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();            final int left = child.getRight() + params.rightMargin;            final int right = left + mdevider.getIntrinsicWidth();            mdevider.setBounds(left, top, right, bottom);            mdevider.draw(c);        }    }    @Override    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {        super.getItemOffsets(outRect, view, parent, state);        if (moritation == HORIZONTAL_LIST) {            //画横线,就是往下偏移一个分割线的高度            outRect.set(0, 0, 0, mdevider.getIntrinsicHeight());        } else {            //画竖线,就是往右偏移一个分割线的宽度            outRect.set(0, 0, mdevider.getIntrinsicWidth(), 0);        }    }}

获取到系统属性然后通过方向判断绘制横向的还是垂直的

在视图初始化中添加这样一行代码

 //添加下划线        mRecyclerView.addItemDecoration(new Decretion(this, Decretion.VERTICAL_LIST));

Decretion类中使用到的attr和drawable代码如下
在AppTheme中添加

 <item name="android:listDivider">@drawable/divider</item>

在drawable文件夹下定义divider

<shape xmlns:android="http://schemas.android.com/apk/res/android"       android:shape="rectangle">    <solid android:color="#7b7a7a"/>    <size android:height="1dp"/></shape>

添加完以上代码运行就会有下划线的效果了,这样比较灵活可以自定义线的颜色和粗细

    -

RecyclerView添加头部视图和足部视图

分别定义好头部和足部布局

header

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"              android:orientation="vertical"              android:layout_width="match_parent"              android:layout_height="100dp">    <TextView        android:id="@+id/header"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:text="我是Header"        android:textSize="30sp"        android:textColor="#fde70b0b"        android:background="#f9777979"        android:gravity="center"/></LinearLayout>

footer与header同样,只是显示的文本不同
然后在activity中添加两个方法把布局添加进来

    //设置HeardView    private void setHeaderView(RecyclerView view) {        View header = LayoutInflater.from(this).inflate(R.layout.header, view, false);        adpt.setHeaderView(header);    }    //设置FooterView    private void setFooterView(RecyclerView view) {        View footer = LayoutInflater.from(this).inflate(R.layout.footer, view, false);        adpt.setFooterView(footer);    }

这个adpt就是我的适配器,适配器中定义了setFooterView和setHeaderView方法

代码:

 //获取HeaderView    public View getHeaderView()    {        return mHeaderView;    }    //设置HeaderView    public void setHeaderView(View headerView) {        mHeaderView = headerView;        //更新第一条        notifyItemInserted(0);    }    //获取FootView    public View getFooterView() {        return mFooterView;    }    //设置FooterView    public void setFooterView(View footerView) {        mFooterView = footerView;        //更新最后一条        notifyItemInserted(getItemCount() - 1);    }

其他方法代码也要相应改变,适配器所有代码

package com.cd.recyclerviewdemo;import android.content.Context;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.List;/** * Author Chendan * Create Date 2016/9/9. * Description: */public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyVeiwHold> {    Context mContext;    List<String> mList;    public static final int TYPE_HEADER = 0;  //带有Header的类型    public static final int TYPE_FOOTER = 1;  //是带有Footer的类型    public static final int TYPE_NORMAL = 2;  //不带有header和footer的类型,正常类型    private View mHeaderView;//头部视图    private View mFooterView;//足部视图    public onRecyclerViewItemOnClick mItemOnClick;    /**     * 定义recyclerView的点击事件接口     */    public interface onRecyclerViewItemOnClick {        //点击事件        void onItemClick(View view, int position);        //长按事件        void onItemLongClick(View view, int position);    }    /**     * 设置set方法     */    public void setItemClick(onRecyclerViewItemOnClick itemOnClick) {        mItemOnClick = itemOnClick;    }    //构造方法,初始化数据    public MyAdapter(Context context, List<String> mlist) {        mContext = context;        mList = mlist;    }    //获取HeaderView    public View getHeaderView()    {        return mHeaderView;    }    //设置HeaderView    public void setHeaderView(View headerView) {        mHeaderView = headerView;        //更新第一条        notifyItemInserted(0);    }    //获取FootView    public View getFooterView() {        return mFooterView;    }    //设置FooterView    public void setFooterView(View footerView) {        mFooterView = footerView;        //更新最后一条        notifyItemInserted(getItemCount() - 1);    }    //初始化控件    @Override    public MyVeiwHold onCreateViewHolder(ViewGroup parent, int viewType) {        //如果有头部视图        if (mHeaderView != null && viewType == TYPE_HEADER) {            //返回头部视图的Hold            return new MyVeiwHold(mHeaderView);        }        //如果有足部视图        if (mFooterView != null && viewType == TYPE_FOOTER) {            //返回足部视图的Hold            return new MyVeiwHold(mFooterView);        }        View layout = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycler, parent, false);        return new MyVeiwHold(layout);    }    //绑定控件    @Override    public void onBindViewHolder(MyVeiwHold holder, final int position) {        /**         * 对子项视图的类型进行判断,1,正常无头部和足部视图的情况下2,有头部视图的情况下3,有足部视图的情况下以及头足部都有的情况下         */        if (getItemViewType(position) == TYPE_NORMAL) {            if (holder instanceof MyVeiwHold) {                //这里加载数据的时候要注意,是从position-1开始,因为position==0已经被header占用了                ((MyVeiwHold) holder).mTextView.setText(mList.get(position - 1));                if (mItemOnClick!=null){                    holder.mTextView.setOnClickListener(new View.OnClickListener() {                        @Override                        public void onClick(View v) {                            mItemOnClick.onItemClick(v,position);                        }                    });                    holder.mTextView.setOnLongClickListener(new View.OnLongClickListener() {                        @Override                        public boolean onLongClick(View v) {                            mItemOnClick.onItemLongClick(v,position);                            return false;                        }                    });                }                return;            }            return;        } else if (getItemViewType(position) == TYPE_HEADER) {            return;        } else {            return;        }    }    /**     * @return     */    @Override    public int getItemCount() {        if (mHeaderView == null && mFooterView == null) {            return mList.size();        } else if (mHeaderView == null && mFooterView != null) {            return mList.size() + 1;        } else if (mHeaderView != null && mFooterView == null) {            return mList.size() + 1;        } else {            return mList.size() + 2;        }    }    //    class MyVeiwHold extends RecyclerView.ViewHolder {        TextView mTextView;        public MyVeiwHold(View itemView) {            super(itemView);            //如果是headerview或者是footerview,直接返回            if (itemView == mHeaderView) {                return;            }            if (itemView == mFooterView) {                return;            }            mTextView = (TextView) itemView.findViewById(R.id.item_tv);        }    }    /**     * 重写这个方法,很重要,是加入Header和Footer的关键,我们通过判断item的类型,从而绑定不同的view    *     */    @Override    public int getItemViewType(int position) {        if (mHeaderView == null && mFooterView == null) {            return TYPE_NORMAL;        }        if (position == 0) {            //第一个item应该加载Header            return TYPE_HEADER;        }        if (position == getItemCount() - 1) {            //最后一个,应该加载Footer            return TYPE_FOOTER;        }        return TYPE_NORMAL;    }}

在activity中调用

 //为RecyclerView添加HeaderView和FooterView        setHeaderView(mRecyclerView);        setFooterView(mRecyclerView);

这样就实现了添加头部视图和足部视图

-

RecyclerView刷新

在布局的最外层包裹一个控件SwipeRefreshLayout

<android.support.v4.widget.SwipeRefreshLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:id="@+id/swip"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.cd.recyclerviewdemo.MainActivity">    <android.support.v7.widget.RecyclerView        android:id="@+id/recycler"        android:layout_width="match_parent"        android:layout_height="match_parent"        /></android.support.v4.widget.SwipeRefreshLayout>

在初始化中添加

 mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swip);  //设置卷内的颜色        mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_bright,                android.R.color.holo_green_light, android.R.color.holo_orange_light, android.R.color.holo_red_light);

刷新

  //刷新        mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {            @Override            public void onRefresh() {                //往集合中添加数据                mList.add(0, "嘿,我是下拉刷新的数据");                //更新适配器中的数据                adpt.notifyDataSetChanged();                //关闭刷新                mSwipeRefreshLayout.setRefreshing(false);            }        });

加载

   //加载        mRecyclerView.addOnScrollListener(new EndLessOnScrollListener(linear) {            @Override            public void onLoadMore(int currentPage) {                //加载更多,这里是测试数据,所以直接加载更多,                // 如果是真是开发中,需要传入currentPage这个参数来实现分页效果,                // 否则一次性加载太多影响性能                loadMoreData();            }        });
private void loadMoreData() {        for (int i = 0; i < 10; i++) {            mList.add("哈哈,拉出第" + i + "条数据");            adpt.notifyDataSetChanged();        }    }

这里的EndLessOnScrollListener是定义的一个接口,继承自RecyclerView.OnScrollListener

package com.cd.recyclerviewdemo;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import android.util.Log;/** * Author Chendan * Create Date 2016/9/9. * Description: */public abstract class EndLessOnScrollListener extends RecyclerView.OnScrollListener {    //声明一个LinearLayoutManager    private LinearLayoutManager mLinearLayoutManager;    //    当前页,从0开始    private int currentPage = 0;    //已经加载出来的Item的数量    private int totalItemCount;    //主要用来存储上一个totalItemCount    private int previousTotal = 0;    //在屏幕上可见的item数量    private int visibleItemCount;    //在屏幕可见的Item中的第一个    private int firstVisibleItem;    //是否正在上拉数据    private boolean loading = true;    public EndLessOnScrollListener(LinearLayoutManager linearLayoutManager) {        this.mLinearLayoutManager = linearLayoutManager;    }    @Override    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {        super.onScrolled(recyclerView, dx, dy);       //显示在屏幕中的数量        visibleItemCount = recyclerView.getChildCount();        //总数量        totalItemCount = mLinearLayoutManager.getItemCount();        //第一项可见数量        firstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition();        //如果正在上拉数据,并且数据当前总条数大于上一次总条数        if (loading) {            if (totalItemCount > previousTotal) {                //说明数据已经加载结束                loading = false;                //保存这次的总条数                previousTotal = totalItemCount;            }        }        //这里需要好好理解        if (!loading && totalItemCount - visibleItemCount <= firstVisibleItem) {            currentPage++;            onLoadMore(currentPage);            loading = true;        }    }    /**     * 提供一个抽闲方法,在Activity中监听到这个EndLessOnScrollListener     * 并且实现这个方法     */    public abstract void onLoadMore(int currentPage);}

RecyclerView的点击事件

有三种实现方式,但是我只选择一种我最熟悉也最适合我们项目的记下来,要具体了解可以看看这篇文章
http://www.tuicool.com/articles/au6ZvmB
1,通过 RecyclerView 已有的方法 addOnItemTouchListener() 实现
2,在绑定 ItemView 时添加点击监听
3,当 ItemView attach RecyclerView 时实现
我使用的是第二种

在适配器中定义设置监听的接口

 /**     * 定义recyclerView的点击事件接口     */    public interface onRecyclerViewItemOnClick {        //点击事件        void onItemClick(View view, int position);        //长按事件        void onItemLongClick(View view, int position);    }

给接口设置set方法,方便调用

 /**     * 设置set方法     */    public onRecyclerViewItemOnClick mItemOnClick;    public void setItemClick(onRecyclerViewItemOnClick itemOnClick) {        mItemOnClick = itemOnClick;    }

在绑定视图的时候给item每一个控件设置点击事件,这是一种item点击事件的假象,但是可以实现功能

 //绑定控件    @Override    public void onBindViewHolder(MyVeiwHold holder, final int position) {        /**         * 对子项视图的类型进行判断,1,正常无头部和足部视图的情况下2,有头部视图的情况下3,有足部视图的情况下以及头足部都有的情况下         */        if (getItemViewType(position) == TYPE_NORMAL) {            if (holder instanceof MyVeiwHold) {                //这里加载数据的时候要注意,是从position-1开始,因为position==0已经被header占用了                ((MyVeiwHold) holder).mTextView.setText(mList.get(position - 1));                if (mItemOnClick!=null){                    holder.mTextView.setOnClickListener(new View.OnClickListener() {                        @Override                        public void onClick(View v) {                            mItemOnClick.onItemClick(v,position);                        }                    });                    holder.mTextView.setOnLongClickListener(new View.OnLongClickListener() {                        @Override                        public boolean onLongClick(View v) {                            mItemOnClick.onItemLongClick(v,position);                            return false;                        }                    });                }                return;            }            return;        } else if (getItemViewType(position) == TYPE_HEADER) {            return;        } else {            return;        }    }

在activity中调用

   adpt.setItemClick(new MyAdapter.onRecyclerViewItemOnClick() {            @Override            public void onItemClick(View view, int position) {                Toast.makeText(MainActivity.this, "点击view.getId()+position:" + (view.getId() + position), Toast.LENGTH_SHORT).show();            }            @Override            public void onItemLongClick(View view, int position) {                Toast.makeText(MainActivity.this, "长按view.getId()+position:" + (view.getId() + position), Toast.LENGTH_SHORT).show();            }        });

参考文章

http://www.jianshu.com/p/3bf125b4917d

http://www.jianshu.com/p/4eff036360da
http://www.jianshu.com/p/991062d964cf

0 0