RecyclerView的关键用法总结, 张鸿洋大神的课后笔记

2018-03-29  本文已影响127人  fc82bb084ee7
课程学习笔记

张鸿洋大神的课程, 明日之星-RecyclerView
https://www.imooc.com/learn/424

代码直接看张鸿洋大神的blog即可: https://blog.csdn.net/lmj623565791/article/details/45059587
详细的代码可以看: https://github.com/carrys17/RecyclerViewTest

为什么叫做RecyclerView

RecyclerView在功能上和ListView是相似的, 那么为什么把它的名字起名叫做RecyclerView呢? 有没有想过这个问题?
以ListView为例, 它使用的Adapter通常是继承自BaseAdapter. 在它的核心方法 getView() 中, 有经验的开发人员都知道要使用ViewHolder的模式, 避免反射layout file, 來提高代码的执行效率.
但我们也知道, 在ListView中, 并没有强制要求开发者必须使用ViewHolder的模式. 这就造成了使用ListView的话, 如果开发者的经验不足, 可能会写出一些效率低的ListView代码出来.

而RecyclerView使用的Adapter, 并不是继承自BaseAdapter, 而是RecyclerView自己的內部类, RecyclerView.Adapter

public abstract static class Adapter<VH extends ViewHolder> {

}

这里的ViewHolder, 也是RecyclerView自己的内部类, RecyclerView.ViewHolder

public abstract static class ViewHolder {
}

这样, 如果要使用RecyclerView的话, 就等于强制要求开发人员必须使用ViewHolder的模式, 保证了代码的执行效率. 这就是RecyclerView, 即"回收View"这个名字的由来.

相比ListView来说最大的好处.
  1. 是强制开发人员使用ViewHolder的模式, 保证代码的执行效率.
  2. 动态的改变布局样式, 在ListView, GridView, 瀑布流这几种样式之间, 可以非常方便的进行动态切换.
简洁的代码

HomeAdaper.java

import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ViewHolder;

    class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder>
    {

        @Override
        public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
        {
            MyViewHolder holder = new MyViewHolder(LayoutInflater.from(
                    HomeActivity.this).inflate(R.layout.item_home, parent,
                    false));
            return holder;
        }


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

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

        class MyViewHolder extends ViewHolder
        {

            TextView tv;

            public MyViewHolder(View view)
            {
                super(view);
                tv = (TextView) view.findViewById(R.id.id_num);
            }
        }
    }

这里的onCreateViewHolder()和onBindViewHolder(), 实际上就是把ListView BaseAdapter中的getView()的实现, 强制分解成了2步来完成.
onCreateViewHolder()的目的是, 获取到每个item的view 对象.
onBindViewHolder()的目的是, 给每个item view 去绑定数据.

public class HomeActivity extends Activity
{

    private RecyclerView mRecyclerView;
    private List<String> mDatas;
    private HomeAdapter mAdapter;

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

        initData();
        mRecyclerView = (RecyclerView) findViewById(R.id.id_recyclerview);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        mRecyclerView.setAdapter(mAdapter = new HomeAdapter());

    }

    protected void initData()
    {
        mDatas = new ArrayList<String>();
        for (int i = 'A'; i < 'z'; i++)
        {
            mDatas.add("" + (char) i);
        }
    }
}

如何设置具体的布局样式, 重点是如何实现瀑布流的效果.

//设置为线性布局, 样子就和普通的ListView一样了.

mRecyclerView.setLayoutManager(new LinearLayoutManager(this));

//设置为gridView的样式, eg. 下面这样设置就是每行展示4个小宫格.

mRecyclerView.setLayoutManager(new GridLayoutManager(this,4));

//设置为瀑布流的效果, 直接使用StaggeredGridLayoutManager就行. eg. 下面这样设置, 就是每行展示4个小宫格.

//瀑布流的实现本质就是给每个item view都设置一个随机的高度值,
//然后再使用StaggeredGridLayoutManager, 这样就实现了瀑布流的效果.
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(4, StaggeredGridLayoutManager.VERTICAL));

设置随机高度值的代码在这里:
在onBindViewHolder()中, 也就是在给每个item view 绑定数据时, 给每个item view设置一个新的随机的高度值.

public class StaggeredAdapter extends MyViewAdapter{


    private List<Integer>mHeights;

    public StaggeredAdapter(Context context, List<String>datas){
        super(context,datas);

        mHeights = new ArrayList<>();
        for (int i = 0; i<mDatas.size();i++){
            //高度取值在100~400px之间的一个随机值.
            mHeights.add((int) (100+Math.random()*300));
        }
    }


    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        holder.mItemTX.setText(mDatas.get(position));
        ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
        lp.height = mHeights.get(position);

        //在这里, 从新设置每个item view的高度值, 给它设置一个随机值.
        holder.itemView.setLayoutParams(lp);


        setUpItemEvent(holder);
    }



}


如何设置每行item view 之间的分割线的样式

我们可以通过该方法添加分割线:

mRecyclerView.addItemDecoration()

该方法的参数为RecyclerView.ItemDecoration,该类为抽象类,官方目前并没有提供默认的实现类.
github上有一些现成的实现类, eg. DividerItemDecoration

public class DividerItemDecoration extends RecyclerView.ItemDecoration {
}

这个类实现的是一个颜色渐变的分割线的效果.

具体的实现就先不看了, 用到的时候再说.

如何设置每个 item增加、删除时的动画

ItemAnimator也是一个抽象类,好在系统为我们提供了一种默认的实现类, DefaultItemAnimator.
github上有一些ItemAnimator的实现类, 实现了更好的动画效果, 可以这里找到.
https://github.com/gabrielemariotti/RecyclerViewItemAnimators

// 设置item动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
public void addData(int position) {
        mDatas.add(position, "Insert One");
        notifyItemInserted(position);
    }

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

有一点细节要注意, 这里更新数据集不是用adapter.notifyDataSetChanged()而是
notifyItemInserted(position)与notifyItemRemoved(position)
否则没有动画效果。

如何给RecyclerView设置item onClick listener 和 onLongClickListener.

RecyclerView.Adapter 中并没有提供一个默认的ClickListener和LongClickListener.
因此需要自己去写一个接口.

    public interface OnItemClickLitener
    {
        void onItemClick(View view, int position);
        void onItemLongClick(View view , int position);
    }
class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder>
{

//...
    public interface OnItemClickLitener
    {
        void onItemClick(View view, int position);
        void onItemLongClick(View view , int position);
    }

    private OnItemClickLitener mOnItemClickLitener;

    public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener)
    {
        this.mOnItemClickLitener = mOnItemClickLitener;
    }

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

        // 如果用户设置了回调对象进来,则设置点击事件
        if (mOnItemClickLitener != null)
        {
            // 在这里给item view设置点击回调.
            holder.itemView.setOnClickListener(new OnClickListener()
            {
                @Override
                public void onClick(View v)
                {
                    int pos = holder.getLayoutPosition();
                    mOnItemClickLitener.onItemClick(holder.itemView, pos);
                }
            });

            holder.itemView.setOnLongClickListener(new OnLongClickListener()
            {
                @Override
                public boolean onLongClick(View v)
                {
                    int pos = holder.getLayoutPosition();
                    mOnItemClickLitener.onItemLongClick(holder.itemView, pos);
                    return false;
                }
            });
        }
    }
//...
}
一点意外的收获

大神讲课用的是eclipse, 里面有个log filter 挺好用的, 我在android studio的插件库中找到了个logviewer, 也可以实现类似的效果.
logviewer的用法总结到简书的文章中去了.

---DONE.---

上一篇下一篇

猜你喜欢

热点阅读