RecyclerView的使用

  原文来自:http://www.cnblogs.com/liuling/p/2015-11-04-01.html

什么是RecyclerView
        RecyclerView是Android 5.0 materials design中的组件之一,相应的还有CardView、Palette等。看名字我们就能看出一点端倪,没错,它主要的特点就是复用。我们知道,Listview中的Adapter中可以实现ViewHolder的复用。RecyclerView提供了一个耦合度更低的方式来复用ViewHolder,RecyclerView只管回收与复用View,其他的你可以自己去设置,其高度解耦可以轻松的实现ListView、GridView以及瀑布流的效果
 
RecyclerView的用法        首先我们要gradle的依赖库中添加  
compile 'com.android.support:recyclerview-v7:23.3.0'
。如果是eclipse直接导入android-support-v7-recyclerview.jar就可以了。

找到v7下的包,编译的时候出错,可以更改为

 

  • 控制其显示的方式,通过布局管理器LayoutManager
  • 控制Item间的间隔(可绘制),通过ItemDecoration
  • 控制Item增删的动画,通过ItemAnimator
  • 控制点击、长按事件,自己写
 
实例:
运用recyclerView实现listview的效果,并实现自定义监听,进行item的点击,长按事件
layout_recyclerview_item.xml:
注意:设置listview垂直滚动时,父布局的高度不能为mathch_parent ,不然会出现一个页面显示一条数据的现象,横向滚动同理.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <TextView
        android:id="@+id/tv_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>
 
适配器的代码如下:
/**
 * Created by Administrator on 2016/9/27.
 */
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ItemViewHolder> {
    private Context context;
    private List<String>dataList;
    private MyOnItemClickListener myOnItemClickListener;

    /**
     * 重写构造方法
     * @param context 当前activity对象
     * @param dataList 数据源
     */
    public MyRecyclerViewAdapter(Context context, List<String> dataList) {
        this.context = context;
        this.dataList = dataList;

    }



    /**
     * 创建viewHolder
     * @param parent
     * @param viewType
     * @return
     */
    @Override
    public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        //加载布局
        View view = LayoutInflater.from(context).inflate(R.layout.layout_recyclerview_item,parent,false);
        //创建viewholder对象,将布局传入
        ItemViewHolder viewHolder = new ItemViewHolder(view);
        return viewHolder;
    }

    /**
     * 绑定viewHolder
     * @param holder  recycleView.ViewHolder的子类
     * @param position 当前索引
     */
    @Override
    public void onBindViewHolder(final ItemViewHolder holder,final int position) {
        holder.tv_text.setText(dataList.get(position));
        //给每个item设置点击事件
        //判断是否存在监听
/**
 这里加了判断,itemViewHolder.itemView.hasOnClickListeners()
 目的是减少对象的创建,如果已经为view设置了click监听事件,就不用重复设置了,不然每次调用onBindViewHolder方法,都会创建两个监听事件对象,增加了内存的开销
*/
        if(myOnItemClickListener != null&&!holder.tv_text.hasOnClickListeners()){
            holder.tv_text.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //注意:当点击该position的时候,recycleView中默认点击的是在父控件中的位置,当删除数据时,也是删除父布局中的对应索引的位置
                    //比如:删除点击item4会删除Item4,再点击item6,会删除item7,因为item7在父布局中位置为6,因此推荐使用父类的position
                    myOnItemClickListener.myOnItemClick(v,holder.getLayoutPosition());
                }
            });
            holder.tv_text.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    myOnItemClickListener.myOItemLongClick(v,holder.getLayoutPosition());
                    return true;
                }
            });

        }
    }
    @Override
    public int getItemCount() {
        return dataList.size();
    }
    /**
     * 设置自定义对外暴露的监听
     * @param myOnItemClickListener
     */
    public void setMyOnItemClickListener(MyOnItemClickListener myOnItemClickListener){
        this.myOnItemClickListener = myOnItemClickListener;

    }

    /**
     * 删除数据
     * @param position
     */
    public void deleteData(int position){
        dataList.remove(position);
        //更新当前删除条目的数据
        notifyItemRemoved(position);
    }
    public void addData(int positon,String data){
        dataList.add(positon,data);
        //只更新当前插入的条目的数据
        notifyItemInserted(positon);
    }
    /**
     * 添加数据
     * @param list
     */
    public void updateData(List<String> list){
        dataList.addAll(list);
        notifyDataSetChanged();
    }

    class ItemViewHolder extends RecyclerView.ViewHolder{
        private TextView tv_text;
        public ItemViewHolder(View itemView) {
            super(itemView);
            tv_text = (TextView) itemView.findViewById(R.id.tv_text);
        }
    }
    public interface  MyOnItemClickListener{
        void myOnItemClick(View view,int Postion);
        void myOItemLongClick(View view,int position);
    }

}

可以看到数据适配器与BaseAdapter比较发生了相当大的变化,主要有3个方法:

getItemCount 这个不用说,获取总的条目数

onCreateViewHolder 创建ViewHolder

onBindViewHolder 将数据绑定至ViewHolder

可见,RecyclerView对ViewHolder也进行了一定的封装,但是如果你仔细观察,你会发出一个疑问,ListView里面有个getView返回View为Item的布局,那么这个Item的样子在哪控制?

其实是这样的,我们创建的ViewHolder必须继承RecyclerView.ViewHolder,这个RecyclerView.ViewHolder的构造时必须传入一个View,这个View相当于我们ListView getView中的convertView (即:我们需要inflate的item布局需要传入)。

还有一点,ListView中convertView是复用的,在RecyclerView中,是把ViewHolder作为缓存的单位了,然后convertView作为ViewHolder的成员变量保持在ViewHolder中,也就是说,假设没有屏幕显示10个条目,则会创建10个ViewHolder缓存起来,每次复用的是ViewHolder,所以他把getView这个方法变为了onCreateViewHolder。有兴趣的自己打印下Log,测试下

main方法:

public class MainActivity extends AppCompatActivity {

    private RecyclerView recyclerView;
    private List<String> dataList = new ArrayList<String>();
    private MyRecyclerViewAdapter recyclerViewAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //初始化控件
        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
        /**
            设置布局管理器,listview风格则设置为LinearLayoutManager
            gridview风格则设置为GridLayoutManager
            pu瀑布流风格的设置为StaggeredGridLayoutManager
                */
        //默认竖直滚动
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(linearLayoutManager);
        //初始化数据
        initData();
        //创建适配器
        recyclerViewAdapter = new MyRecyclerViewAdapter(this,dataList);
        //设置适配器
        recyclerView.setAdapter(recyclerViewAdapter);
        //设置自定义点击监听
        recyclerViewAdapter.setMyOnItemClickListener(new MyRecyclerViewAdapter.MyOnItemClickListener() {
            //点击item的时候调用
            @Override
            public void myOnItemClick(View view, int Postion) {
                Toast.makeText(MainActivity.this, dataList.get(Postion), Toast.LENGTH_SHORT).show();
                recyclerViewAdapter.deleteData(Postion);
            }
            //长按item的时候调用
            @Override
            public void myOItemLongClick(View view, int position) {
                Toast.makeText(MainActivity.this,"长按--"+dataList.get(position), Toast.LENGTH_SHORT).show();
                recyclerViewAdapter.addData(position,"我是新来的");
            }
        });
    }

    private void initData() {
        for (int i = 0; i < 30; i++) {
            dataList.add((i+1)+"条数据");
        }
    }
}

效果如下:

点击时:

点击后:

 长按后:

 

ItemDecoration

我们可以通过该方法添加分割线: 
mRecyclerView.addItemDecoration() 

RecyclerView.ItemDecoration的实现类

package fanggao.qf.recycleviewlistview;

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.util.Log;
import android.view.View;

/**
 * Created by Administrator on 2016/9/27.
 */
public class DividerItemDecoration 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 DividerItemDecoration(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) {
        Log.v("recyclerview - itemdecoration", "onDraw()");

        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);
        }
    }
}

该实现类可以看到通过读取系统主题中的 Android.R.attr.listDivider作为Item间的分割线,并且支持横向和纵向

在源代码上添加一句

 recyclerView.addItemDecoration(new DividerItemDecoration(this,
                DividerItemDecoration.VERTICAL_LIST));

效果:

该分割线是系统默认的,你可以在theme.xml中找到该属性的使用情况

续:recycleView的一些常用属性:

recyclerView.getChildCount()只是获取的是可见的item个数

recyclerView.getLayoutManager().getItemCount()获取的是所有的item的个数

recyclerView.getChildAt(int position) 获取某个位置的view

recyclerView.getChildAdapterPosition(View view) 获取某个view的位置

可通过上面几个属性,在recycleView的滚动监听中实现自定义的recycleView的上拉加载和下拉刷新

具体见转载请标明出处: 
http://blog.csdn.net/lmj623565791/article/details/45059587; 
本文出自:【张鸿洋的博客】

原文地址:https://www.cnblogs.com/fangg/p/5914005.html