Android RecyclerView使用详解一

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_30276961/article/details/50198113

转载请注明:
http://blog.csdn.net/sinat_30276961/article/details/50198113

今天,开始总结一下RecyclerView。本篇内容,介绍下RecyclerView,然后讲解RecyclerView的常规使用方法,包括点击事件通用添加的方法。

介绍

说到RecyclerView,我想大部分朋友都会想到ListView。没错,这两者都是用来展示一个页面一定数量的列表信息的。而且,它们的内部实现也何其相似,都是通过维护少量view来展示大量信息,这个少量view就是一页的view。

那么,为啥google在去年这么大张旗鼓的推出了RecyclerView呢?原因有几个:
1. RecyclerView把原先的ListView需要写的ViewHolder给集成进来了,我们只要复写一下就行。
2. RecyclerView把每个item的布局展示方式给解耦出来了。这样一来,灵活度就大大提高了。(通过LayoutManager的子类)
3. RecyclerView把分隔图设置也抽离出来了。通过继承ItemDecoration。
4. RecyclerView增加了添加和删除item的动画效果。你可以自定义动画效果。通过继承ItemAnimator。

从以上几点,可以看出,RecyclerView的设计充分考虑了自己定制的自由度。但由此也带来了一些麻烦,就是这些都要自己去实现。当然,很多是有现成的子类,如果够用,那就不用自己去实现了。

常规使用

因为RecyclerView现在还没有加入到sdk里,所以我们要把它的jar包加进来。如果是eclipse,还需要导入一个对应的资源工程。

下面是Android Studio的添加方式:

dependencies {
    compile 'com.android.support:recyclerview-v7:23.0.1'
}

然后在布局文件里,这样申明:

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scrollbars="none"/>

接下来,我通过一个小实例代码,展示一下RecyclerView的常规使用方式。

有图有真相:
这里写图片描述

实例很简单,意在说明RecyclerView的基本使用方法。

先总结一下RecyclerView的常规使用方式:
1. 先获取RecyclerView对象
2. 创建一个LayoutManager对象,然后RecyclerView设置这个LayoutManager
3. 继承RecyclerView.Adapter,然后定义一个子类复写ViewHolder。
4. 创建这个Adapter对象,然后RecyclerView设置这个Adapter。

这里重点是2和3,和ListView比起来,有些区别。

ok,废话不多说,看代码:

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_normal_recycler_view_test);

        mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
        // 如果可以确定每个item的高度是固定的,设置这个选项可以提高性能
        mRecyclerView.setHasFixedSize(true);

        // 确定每个item的排列方式
        LinearLayoutManager layoutManager = new LinearLayoutManager(this) {
            @Override
            public RecyclerView.LayoutParams generateDefaultLayoutParams() {
                // 这里要复写一下,因为默认宽高都是wrap_content
                // 这个不复写,你点击的背景色就只充满你的内容
                return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT);
            }
        };
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        mRecyclerView.setLayoutManager(layoutManager);

        mAdapter = new RecyclerViewAdapter(this, R.layout.item_normal_recycler_view, 30);
        mAdapter.setOnItemClickListener(new CommonAdapter.OnRecyclerViewItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Toast.makeText(NormalRecyclerViewTest.this, "Item " + position + " click"
                        , Toast.LENGTH_SHORT).show();
            }
        });
        mRecyclerView.setAdapter(mAdapter);
    }

第一步就不讲了。。需要注意的是RecyclerView可以设定一个固定高度属性,这样就避免所有子View第一次高度的计算,提高了效率。

我们来看第二步,创建LayoutManager。我创建了一个LinearLayoutManager–线性排布,并设置为垂直排列。并且复写了它的generateDefaultLayoutParams。因为默认它的宽高都是设置为WRAP_CONTENT,这样一来,点击的背景高亮效果就会只覆盖一部分。如下所示:
这里写图片描述
说到LayoutManager,google已经给我们提供了三个子类:
1. LinearLayoutManager 线性排列
2. GridLayoutManager 网格排列
3. StaggeredGridLayoutManager 瀑布流排列
网格排列和瀑布流排列,会在后面再讲解。

接着说说Adapter。RecyclerView的adapter,需要复写RecyclerView.Adapter。我简单实现了一个常规的抽象Adapter。

public abstract class CommonAdapter<T extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<T>{
    protected final Context mContext;
    protected final int mLayoutId;
    protected int mNum;

    private OnRecyclerViewItemClickListener mOnItemClickListener = null;

    public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) {
        mOnItemClickListener = listener;
    }

    public CommonAdapter(Context context, int layoutId, int num) {
        mContext = context;
        mLayoutId = layoutId;
        mNum = num;
    }

    @Override
    public abstract T onCreateViewHolder(ViewGroup parent, int viewType);

    @Override
    public void onBindViewHolder(T holder, int position) {
        holder.itemView.setOnClickListener(new OnClickListener(position, mOnItemClickListener));
    }

    @Override
    public int getItemCount() {
        return mNum;
    }

    public static class OnClickListener implements View.OnClickListener{
        final int mPosition;
        final OnRecyclerViewItemClickListener mListener;

        public OnClickListener(int i, OnRecyclerViewItemClickListener listener) {
            mPosition = i;
            mListener = listener;
        }

        @Override
        public void onClick(View v) {
            if (mListener != null) {
                mListener.onItemClick(v, mPosition);
            }
        }
    }

    // 用来设置每个item的接听
    public static interface OnRecyclerViewItemClickListener {
        void onItemClick(View view, int position);
    }
}

增加这层抽象,主要是为了加个点击响应。懒得在每个adapter里都这样去。。
这里不得不吐槽一下RecyclerView,为了提升灵活度,它的排列方式可以实现千变万化,所带来的情况是,google觉得点击也让用户自己去实现,以匹配多变的布局。这样一来,我们只能苦逼的自己去实现点击事件了。相比较ListView,这点是一大败笔。
既然google没有添加,那只能自己实现了。于是,我就多加了一层抽象类,用于给每个item添加点击监听和回调。

我们在实现Adapter时,需要复写以下几个方法和类:
1. onCreateViewHolder 解析item布局,绑定item里的子view
2. onBindViewHolder 在这里给每个子view赋值
3. getItemCount 设置list的数量
4. RecyclerView.ViewHolder 用于保存绑定子view信息

接下去,贴出具体Adapter类:

public class RecyclerViewAdapter extends CommonAdapter<RecyclerViewAdapter.ViewHolder>{
    // 临时数据
    private static final int[] DRAW_IDS = new int[] {
            R.drawable.cheese_1, R.drawable.cheese_2, R.drawable.cheese_3,
            R.drawable.cheese_4, R.drawable.cheese_5
    };

    public RecyclerViewAdapter(Context context, int layoutId, int num) {
        super(context, layoutId, num);
    }

    public void setNum(int num) {
        mNum = num;
    }

    public int getNum() {
        return mNum;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View view = View.inflate(mContext, mLayoutId, null);
        ViewHolder viewHolder = new ViewHolder(view);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder, int i) {
        super.onBindViewHolder(viewHolder, i);
        viewHolder.mTitle.setText("I'm a " + i + " item");
        Glide.with(mContext)
                .load(DRAW_IDS[i%5])
                .fitCenter()
                .into(viewHolder.mLogo);
        // 用这种方式会卡
//        viewHolder.mLogo.setImageResource(DRAW_IDS[i%5]);
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        private CircleImageView mLogo;
        private TextView mTitle;

        public ViewHolder(View itemView) {
            super(itemView);
            mLogo = (CircleImageView) itemView.findViewById(R.id.logo);
            mTitle = (TextView) itemView.findViewById(R.id.text);
        }
    }
}

上面就是一个常规Adapter的写法。当然,这里我继承的是自己写的那个CommonAdapter,里面多了个点击响应。
可以看到,它把ViewHolder直接集成进来了,我们只要复写RecyclerView.ViewHolder,并在里面添加子view申明和find代码就行。
然后在onCreateViewHolder传回的值也是ViewHolder。

ok,再回到最初始的RecyclerView初始化代码:

        mAdapter = new RecyclerViewAdapter(this, R.layout.item_normal_recycler_view, 30);
        mAdapter.setOnItemClickListener(new CommonAdapter.OnRecyclerViewItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Toast.makeText(NormalRecyclerViewTest.this, "Item " + position + " click"
                        , Toast.LENGTH_SHORT).show();
            }
        });
        mRecyclerView.setAdapter(mAdapter);

这样一来,点击事件就绑定了,并且adapter设置完成。然后RecyclerView就可以显示了。

篇幅问题,今天就写到这。天气好冷,手残了。。
Have a good night!

猜你喜欢

转载自blog.csdn.net/sinat_30276961/article/details/50198113