美文网首页
RecyclerView基本用法笔记

RecyclerView基本用法笔记

作者: 咸鱼而已 | 来源:发表于2016-05-24 16:36 被阅读463次

主要包括RecyclerView基本用法、插入删除Item、Item点击事件监听

一、RecyclerView的基本用法

1.RecyclerView的介绍
RecyclerView是support-V7报下的一个控件,用来替代ListView、GridView等。下面是官方的解释:

RecyclerView is a more advanced and flexible version of ListView. This widget is a container for large sets of views that can be recycled and scrolled very efficiently. Use the RecyclerView widget when you have lists with elements that change dynamically. 其实也就是说当你需要动态展示一些item的时候建议使用RecyclerView。

2.RecyclerView的使用

  1. 首先需要导入这个包的引用,需要在app对应的build.gradle文件中导入引用:
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.android.support:recyclerview-v7:23.4.0'//这个是RecyclerView的依赖项
}

我用的AndroidStudio一般都是下面这个步骤来添加引用:File→Project Structure左侧选择对应的Moudle,选择右侧Dependences→add→Library Dependece然后找到对应的包选择添加。

  1. 布局文件activity_normal中使用:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="com.zwf.recyclerviewdemo.Activity.NormalActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <Button
            android:id="@+id/insertBtn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="插入"
            android:layout_weight="1"/>
        <Button
            android:id="@+id/removeBtn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="移除"
            android:layout_weight="1"/>
    </LinearLayout>
    
    <!--这里是使用RecyclerView-->
    <android.support.v7.widget.RecyclerView
        android:id="@+id/id_normal_recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </android.support.v7.widget.RecyclerView>
</LinearLayout>

布局文件很简单,上面两个按钮,下面一个RecyclerView。

3.然后在代码中对RecyclerView进行使用,一般需要做以下设置:

public class NormalActivity extends AppCompatActivity implements View.OnClickListener {
    private RecyclerView mRecyclerView;
    private List<String> mDatas;
    private HomeAdapter adapter;
    private Button insertBtn,removeBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_normal);

        insertBtn = (Button) findViewById(R.id.insertBtn);
        removeBtn = (Button) findViewById(R.id.removeBtn);
        insertBtn.setOnClickListener(this);
        removeBtn.setOnClickListener(this);

        mDatas = this.getIntent().getStringArrayListExtra("data");

        //获取RecyclerView
        mRecyclerView = (RecyclerView) findViewById(R.id.id_normal_recyclerview);

        //为RecyclerView设置布局管理器
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));

        //为RecyclerView设置Adapter
        adapter = new HomeAdapter(this, mDatas);
        mRecyclerView.setAdapter(adapter);

        //为RecyclerView设置分割线(这个可以对DividerItemDecoration进行修改,自定义)
        mRecyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.HORIZONTAL_LIST));

        //动画
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.insertBtn:
                adapter.addData(1);
                break;
            case R.id.removeBtn:
                adapter.removeData(1);
                break;
        }
    }
}

要想使用一个RecyclerView一般需要设置布局管理器、适配器、分割线、动画。

主要说一下布局管理器,一共有三种:LinearLayoutManager(线性布局)、GridLayoutManager(网格布局)、StaggeredGridLayoutManager(瀑布流布局)

然后看一下HomeAdapter的内容:

public class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder>{
    private List<String> mDatas;
    private LayoutInflater mInflater;
    /**
     * Adapter适配器的构造函数,主要用来初始化数据,例如:LayoutInflater、data
     * @param mInflaters    这个参数也可以是Context,无非就是在哪获得LayoutInflater
     * @param mDatas        这是对应的RecyclerView要显示的数据
     */
    public HomeAdapter(Context context, List<String> mDatas){
        this.mDatas = mDatas;
        mInflater = LayoutInflater.from(context);
    }
    /**
     * 创建一个ViewHolder并返回
     * @param parent
     * @param viewType  可以根据这个参数创建不同item的ViewHolder
     * @return
     */
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        /**
         * LayoutInflater.from(MainActivity.this) 获取到父容器的inflater,跟fragment类似
         */
        MyViewHolder holder = new MyViewHolder(mInflater.inflate(R.layout.item_normal, parent, false));
        return holder;
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        holder.tv.setText(mDatas.get(position));
    }

    /**
     * 返回item的个数
     */
    @Override
    public int getItemCount() {
        return mDatas.size();
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {
        private TextView tv;
        public MyViewHolder(View view){
            super(view);
            // 这里传进来的view是一个MyViewHolder
            tv = (TextView) view.findViewById(R.id.id_num);
        }
    }

    public void addData(int position){
        mDatas.add(position, "insert");
        notifyItemInserted(position);
    }

    public void removeData(int position){
        mDatas.remove(position);
        notifyItemRemoved(position);
    }
}

这个Adapter需要继承自RecyclerView.Adapter,并且实现其中的抽象方法

上面还用到了这么一个类DividerItemDecoration,用来画分割线:

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) {

        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);
        }
    }
}
二、插入删除Item

上面的HomeAdapter中已经实现了插入删除的功能,如下:

public void addData(int position){
    mDatas.add(position, "insert");
    notifyItemInserted(position);
}

public void removeData(int position){
    mDatas.remove(position);
    notifyItemRemoved(position);
}

其实主要是对数据进行插入删除操作,然后更新界面。插入用notifyItemInserted()删除用notifyItemRemoved(),这两个方法都需要一个下标参数。

三、Item点击监听

这里其实我是用接口回调实现的,具体会在瀑布流相关的代码中体现。

  • 初始化一个接口
  • 在Adapter中声明一个接口属性
  • onBindViewHolder()方法中为每一个Item添加点击事件或者长按事件,同时调用接口变量中对应的方法(此时可以把点击的下标也传进去)
  • 在为RecyclerView设置Adapter的时候,同时给Adapter设置上面声明接口属性,此时就可以实现在Activity或者Fragment中实现对Item点击事件的监听
四、关于瀑布流的使用

这里需要把RecyclerView的布局管理器设置成StaggeredGridLayoutManager,如下:

mRecycler.setLayoutManager(new StaggeredGridLayoutManager(4, StaggeredGridLayoutManager.VERTICAL));
//第一个参数是表示显示列(行),第二个参数是滚动方向
public class StaggeredAdapter extends RecyclerView.Adapter<MyViewHolder> {
    private List<String> mDatas;
    private LayoutInflater mInflaters;

    private List<Integer> mHieghts;
    private List<Integer> mWidths;

    /**
     * Adapter适配器的构造函数,主要用来初始化数据,例如:LayoutInflater、data
     * @param mInflaters    这个参数也可以是Context,无非就是在哪获得LayoutInflater
     * @param mDatas        这是对应的RecyclerView要显示的数据
     */
    public StaggeredAdapter(LayoutInflater mInflaters, List<String> mDatas){
        this.mInflaters = mInflaters;
        this.mDatas = mDatas;

        //init heights and width
        mHieghts = new ArrayList<>();
        for (int i=0; i<mDatas.size(); i++){
            mHieghts.add((int) (150+Math.random()*300));
        }
        mWidths = new ArrayList<>();
        for (int i=0; i<mDatas.size();i++){
            mWidths.add((int) (320+Math.random()*300));
        }
    }

    /**
     * 创建一个ViewHolder并返回
     * @param parent
     * @param viewType  可以根据这个参数创建不同item的ViewHolder
     * @return
     */
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        MyViewHolder holder = new MyViewHolder(mInflaters.inflate(R.layout.item_home, parent, false));

        return holder;
    }

    @Override
    public int getItemViewType(int position) {
        return super.getItemViewType(position);
    }

    @Override
    public void onBindViewHolder(final MyViewHolder holder, int position) {
        //获取Layout内容
        LayoutParams lp = holder.tv.getLayoutParams();

        //设置Layout的高度(根据具体位置的具体要求设置不同的高度)
        lp.height = mHieghts.get(position);
//        lp.width = mWidths.get(position);

        holder.tv.setLayoutParams(lp);
        holder.tv.setText(mDatas.get(position));

        if (mOnItemClickListener != null){
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    int pos = holder.getLayoutPosition();
                    mOnItemClickListener.onItemClick(view, pos);
                }
            });

            holder.itemView.setOnLongClickListener(new View.OnLongClickListener(){
                @Override
                public boolean onLongClick(View view) {
                    int pos = holder.getLayoutPosition();
                    mOnItemClickListener.onItemLongClick(holder.itemView, pos);
                    removeData(pos);

                    return false;
                }
            });
        }
    }

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

    /**
     * add item
     * @param position
     */

    /**********************add and remove*************************/
    public void addData(int position){
        mDatas.add(position, "insert");
        mHieghts.add(position, (int) (150 + Math.random() * 300));
        mWidths.add(position, (int) (320 + Math.random() * 300));
        notifyItemInserted(position);
    }

    public void removeData(int position){
        if (mDatas.size() > position)
        {
            mDatas.remove(position);
        }else {
            return;
        }

        mHieghts.remove(position);
        mWidths.remove(position);
        notifyItemRemoved(position);
    }
    /**********************add and remove*************************/

    /**********************click listener*************************/
    public interface OnItemClickListener{
        void onItemClick(View view, int position);
        void onItemLongClick(View view, int position);
    }

    private OnItemClickListener mOnItemClickListener;

    public void setmOnItemClickListener(OnItemClickListener onItemClickListener){
        mOnItemClickListener = onItemClickListener;
    }
    /**********************click listener*************************/
}

上面的实现是其实是使用的同一个ViewHolder,然后对这个ViewHolder的布局文件高度进行改变,来实现类似瀑布流的效果。
重点看一下onCreateViewHolder(ViewGroup parent, int viewType)这个方法,这个方法有两个参数。其中第二个参数就是View的类型,其实是getItemViewType(int position)的返回值。我们可以重写这个方法, 根据不同的数据,返回不同的值(这个值可以定义枚举类型),然后在穿件ViewHolder的时候就可以直接根据类型来创建了。

然后就是ViewHolder中常用的一个API是:getLayoutPosition(),可以获得Item的下标。

总的来说,关于RecyclerView的基本用法是弄明白了,但是表述的有点乱。这里做个总结,整理一下思路。还有像mRecyclerView.addItemDecoration(); mRecyclerView.setItemAnimator();这两个方法的用法可能在实际用的时候还需要深究。还有点击的时候长按在手指离开的时候也会触发点击事件。

相关文章

网友评论

      本文标题:RecyclerView基本用法笔记

      本文链接:https://www.haomeiwen.com/subject/sppzrttx.html