一、RecyclerView简介

是Google2014年发布的用来代替ListView、GridView的控件

优点:

①、能够控制显示的方法,设定自己的布局。

②、能够自定义分割线(Divider)

③、能够自定义Item动画。

④、相对于ListView,提高了item的复用性。极大的封装了Item与Adapter的创建

缺点:

没有点击事件,也就是说点不了显示的item.需要自定义。

可能有些SDK没有自带RecyclerVIew所以可以添加:

compile 'com.android.support:recyclerview-v7:23.1.0' 

从本地SDK中添加RecyclerVIew。

二、RecyclerView的使用

①、ReyclerView的基础结构


mRecyclerView = findView(R.id.id_recyclerview);
//设置布局管理器
mRecyclerView.setLayoutManager(layout);
//设置adapter
mRecyclerView.setAdapter(adapter)
//设置Item增加、移除动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
//添加分割线
mRecyclerView.addItemDecoration(new DividerItemDecoration(
                getActivity(), DividerItemDecoration.HORIZONTAL_LIST));

通过上面的例子:可以将Recycler分为四部分。

①、设定布局(LayoutManager):

作用:设定RecyclerView的布局和显示。

RecyclerView默认使用线性竖直布局(同ListView的显示方法)

布局部分(自带布局):

LinearLayoutManager:线性布局管理器。  包含纵向布局(VERTICAL)、横向布局(HORIZONTAL)

示例:


LinearLayoutManager layout = new LinearLayoutManager(context);
layout.setOrientation(LinearLayoutManager.VERTICAL);

方式与创建LinearLayout不多。

GridLayoutManager:网格布局。 输入参数:每行有几列。


GridLayoutManager layout = newGridLayoutManager(context, 4);//表示有四列

StaggeredGridLayoutManager:瀑布布局。 输入参数:列数,横向布局(左右滑动)、还是纵向布局(上下滑动)



new StaggeredGridLayoutManager(4,StaggeredGridLayoutManager.VERTICAL)//表示有四列,纵向滑动



显示部分:

findFirstVisibleItemPosition:获取当前显示在界面上的第一个item的position

findLastVisibleItemPosition:获取当前显示在界面上的第最后一个item的position

findFirstCompletelyVisibleItemPosition:获取当前完全显示在界面上的第一个item的position

findLastCompletelyVisibleItemPosition:获取当前完全显示在界面上的第最后一个item的position

②、设定Adapter

基本原理:首先通过ViewHolder实现对item视图的封装。 然后通过Adapter调动onCreateViewHolder()创建ViewHolder,当item显示在屏幕的时候,会回调onBindViewHolder()所以在该方法中实现Item的逻辑

首先创建item的布局


<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!--就一个TextView-->
    <TextView
        android:id="@+id/tv_content"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:gravity="center"
        android:layout_gravity="center" />
</FrameLayout>

然后设定Adapter:



/**
 * Created by PC on 2016/8/17.
 * 1、继承RecyclerView.Adapter
 * 2、创建ViewHolder类,继承RecyclerView.ViewHolder,用来初始化封装item视图。Adapter只接受ViewHolder
 * 3、结合数据,设定
 */
public class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder> {
    private Context mContext;
    private List<String> mStrItems;
    public HomeAdapter(Context context,List<String> strItems) {
        mContext = context;
        mStrItems = strItems;
    }
    //将View封装
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View v = LayoutInflater.from(mContext).inflate(R.layout.item_content,viewGroup,false);
        MyViewHolder viewHolder = new MyViewHolder(v);
        return viewHolder;
    }
    //当item出现在屏幕的时候调用
    @Override
    public void onBindViewHolder(MyViewHolder s, int i) {
        s.tvContent.setText(mStrItems.get(i));
    }

    @Override
    public int getItemCount() {
        return mStrItems.size();
    }

    class MyViewHolder extends RecyclerView.ViewHolder{
        public TextView tvContent;
        public MyViewHolder(View itemView) {
            super(itemView);
            tvContent = (TextView)itemView.findViewById(R.id.tv_content);
        }
    }
}

最后:在Activity中调用

public class MainActivity extends AppCompatActivity {
    private RecyclerView mRecyclerView;
    private HomeAdapter mHomeAdapter;
    private List<String> mStrItems;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mRecyclerView = (RecyclerView) findViewById(R.id.main_recycler);
        //自定义数据
        mStrItems = new ArrayList<>();
        for(int i=0; i<26; ++i){
            char c = 'A';
            char data = (char)(c+i);
            mStrItems.add(Character.toString(data));
        }
        mHomeAdapter = new HomeAdapter(this,mStrItems);
        //设定布局管理器
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        //加载Adapter
        mRecyclerView.setAdapter(mHomeAdapter);

    }
}

结果:


③、实现分割线效果(以LinearLayoutManager布局)

第一种方法:直接在layout.xml中的FrameLayout中加上marginBottom。

第二种:继承ItemDecorate。

1、我们先看下ItemDecorate需要重写的方法

onDraw(Canvas c, RecyclerView parent, State state)

//用Canvas来绘制divider。所以说divider是画图绘制上去,而不是添加控件到item视图中。所以不会改变item视图的大小。
onDrawOver(Canvas c, RecyclerView parent, State state)
//同样是绘制divider。跟onDraw()的差别在于绘制流程。decoration 的 onDraw,child view 的 onDraw,decoration 的 onDrawOver,这三者是依次发生的。而由于 onDrawOver 是绘制在最上层的,所以它的绘制位置并不受限制。

getItemOffsets(Rect outRect, View view, RecyclerView parent, State state)

//可以理解为利用outRect设置item视图的padding。就是说outRect(10,0,0,0);相当于item.paddingLeft = 10;

为什么用outRect 不用 left、top、right、bottom表示。据说是为了能够达到复用的效果。

2、所有的方法都介绍完了,那么开始制作吧。

首先:制作divider的分割线图

然后:绘制分割线

最后:为了不让分割线占用原本item视图的大小,调用getItemOffset()方法,为item设置padding

示例:


/**
 * Created by PC on 2016/8/17.
 * 要做的事情
 * 1、判断RecyclerView是竖着还是横着
 * 2、利用android自带的listDivider来设置分割线
 * 3、因为分割线是draw上去的,还需要设置Item之间的间距,来设置
 */
public class DividerLinearItemDecorate extends RecyclerView.ItemDecoration {
    //获取android自带的listDivider
    private static final int [] STYLEABLE = {android.R.attr.listDivider};

    private static final int VERTICAL = LinearLayoutManager.VERTICAL;
    private static final int HORIZONTAL = LinearLayoutManager.HORIZONTAL;
    private Context mContext;
    private Drawable mDivider;
    private int mOrientation;

    public DividerLinearItemDecorate(Context context, int orientation) {
        mContext = context;
        initWidget(orientation);
    }

    private void initWidget(int orientation){
        //获取Android自带的divider
        TypedArray a = mContext.obtainStyledAttributes(STYLEABLE);
        mDivider = a.getDrawable(0);

        //判定 输入的orientation是否有错误
        switch (orientation){
            case VERTICAL:
                mOrientation = orientation;
                break;
            case HORIZONTAL:
                mOrientation = orientation;
                break;
            default:
                throw new IllegalArgumentException("传入的orientation错误");
        }
    }


    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        //绘制divider
        if (mOrientation == VERTICAL){
            drawVertical(c,parent);
        }
        else {
            drawHorizontal(c,parent);
        }
    }

    private void drawVertical(Canvas c,RecyclerView parent){
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();
        for(int i=0; i<parent.getChildCount(); ++i){
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
            int top = child.getBottom()+params.bottomMargin;
            int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left,top,right,bottom);
            mDivider.draw(c);
        }
    }

    private void drawHorizontal(Canvas c,RecyclerView parent){
        int top = parent.getPaddingTop();
        int bottom = parent.getHeight() - parent.getPaddingBottom();
        for(int i=0; i<parent.getChildCount(); ++i){
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
            int left = child.getLeft()+params.rightMargin;
            int right = left+mDivider.getIntrinsicWidth();
            //divider绘制的区域
            mDivider.setBounds(left,top,right,bottom);
            mDivider.draw(c);
        }
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        if (mOrientation == VERTICAL){
            outRect.set(0,0,0,mDivider.getIntrinsicHeight());
        }
        else {
            outRect.set(0,0,mDivider.getIntrinsicWidth(),0);
        }
    }
}

使用:

mRecyclerView.setItemDecoreation(new DividerLinearItemDecorate(context,LinearLayoutManager.VERTICAL));

获取recyclerview item中的view recyclerview addview_ide

遇到的问题:

①、获取android自带的divider的原理

首先需要看下鸿洋:深入理解Android自定义属性

补充:


TypedArray a = mContext.obtainStyledAttributes(STYLEABLE);

会发现,obtainStyledAttributes();根本没有载入attribute,那么是a是如何获得值的呢?

原因是,android在生成的时候,其实内部已经初始化了自己的原有attibute。(就是style中的AppTheme)

然后,我们通过obtainStyledAttibutes(attr)其实表示的是自身在layout定义的属性,覆盖原先的attribute,所得到的集合。

所以可以获取原生listDivider的值。

那么使用这种方式的优点在哪里?

因为我们使用的从android中获取的dividier。所以能够在style中直接修改


<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <!--在style中直接修改-->
        <item name="android:listDivider">@drawable/divider_home</item>
    </style>



④、实现增加删除的效果

首先在ActionBar上添加按钮。

了解:menu的使用

然后,我们增加或者删除数据的时候如何刷新呢?

ListView使用的是notifyChanged()。

但是使用RecyclerView就变成了


public void addData(int pos){
        //pos表示在第几个位置插入
        mStrItems.add(pos,"数据");
        notifyItemInserted(pos);
    }

    public void removeData(int pos){
        //pos表示在第几个位置插入
        mStrItems.remove(pos);
        notifyItemRemoved(pos);
    }



⑤、由于Recycler没有点击事件,所以需要自定义点击事件

//第一步:设置监听器接口
    public interface OnItemSelectedListener{
        void itemSelected(int position);
    }
//第二步:添加设置监听器方法
    public void setOnItemSeletedListener(OnItemSelectedListener listener){
        mListener = listener;
    }
//第三步:设置回调
  @Override
    public void onBindViewHolder(MyViewHolder s, int i) {
        s.tvContent.setText(mStrItems.get(i));
        //设置回调
        if (mListener != null){
            mListener.itemSelected(i);
        }
    }