偷懒新姿势,打造属于RecyclerView的万能适配器Adapter和ViewHolder

前言

昨天开始接触江湖口碑很好的RecyclerView,事实上,我已经被她的强大所征服了!资源回收,数据绑定,布局显示,分割线,Item动画多个模块高度解耦,灵活优雅。其实,RecyclerView在使用上已经是相当简单了(个人觉得),但仍有很多代码是可以加以封装的。今天受简书上一篇博文的启发,作为写代码喜欢优(tou)雅(lan)的人,想到了一种封装方式,打造万能适配器,供大家食用。

正统模式:

<span style="font-size:12px;"><code class="hljs axapta has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SimplerItemAdapter</span> <span class="hljs-inheritance"><span class="hljs-keyword">extends</span></span> <span class="hljs-title">RecyclerView</span>.<span class="hljs-title">Adapter</span><<span class="hljs-title">SimplerItemAdapter</span>.<span class="hljs-title">SimpleItemViewHolder</span> > {</span>

  <span class="hljs-keyword">private</span> List <String> items;

  <span class="hljs-keyword">public</span> SimplerItemAdapter (@NonNull List<String> dateItems ) {
    <span class="hljs-keyword">this</span>.items = (dateItems != <span class="hljs-keyword">null</span> ? dateItems : <span class="hljs-keyword">new</span> ArrayList<String>());
  }

  @Override <span class="hljs-keyword">public</span> SimpleItemViewHolder onCreateViewHolder (ViewGroup viewGroup, <span class="hljs-keyword">int</span> viewType) {
    View itemView = LayoutInflater.from( viewGroup.getContext ()).inflate(R.layout .item, viewGroup, <span class="hljs-keyword">false</span> );
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> SimpleItemViewHolder(itemView);
  }

  @Override <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> onBindViewHolder (SimpleItemViewHolder viewHolder, <span class="hljs-keyword">int</span> position) {
    viewHolder.textView .setText(items.get (position));
  }

  @Override <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> getItemCount () {
    <span class="hljs-keyword">return</span> (<span class="hljs-keyword">this</span>.items != <span class="hljs-keyword">null</span>) ? <span class="hljs-keyword">this</span> .items. size() : <span class="hljs-number">0</span> ;
  }

  <span class="hljs-keyword">protected</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SimpleItemViewHolder</span> <span class="hljs-inheritance"><span class="hljs-keyword">extends</span></span> <span class="hljs-title">RecyclerView</span>.<span class="hljs-title">ViewHolder</span> {</span>
    <span class="hljs-keyword">protected</span> TextView textView ;

    <span class="hljs-keyword">public</span> SimpleItemViewHolder (View itemView) {
      <span class="hljs-keyword">super</span>(itemView);
      <span class="hljs-keyword">this</span>.textView = (TextView) itemView.findViewById (R. id.text);
    }
  }
}
</code></span>
  • 首先,
    @Override public int getItemCount () {
    return (this.items != null) ? this .items. size() : 0 ;
    }
    这段代码完全可以封装起来的。
  • onCreatedViewHolder()方法作用是绑定item视图,可以进一步封装,给子类提供一个getLayoutItemId的抽象方法,这样就可以简化成一行代码了。
  • 因此我们发现,这个adapter的核心代码在与onBindViewHolder()中,作用是将数据跟视图(ViewHolder)绑定,可以给子类提供一个bindData()抽象方法。
  • 当然了,使用泛型也是极好的,拓广了adapter的使用范围。
  • 添加点击事件的监听也可以封装到万能adapter中,子类就不用再写item点击事件处理代码了

封装后的Adapter

<span style="font-size:12px;"><code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> <span class="hljs-keyword">class</span> BaseRecyclerAdapter<T> extends RecyclerView.Adapter<RecyclerViewHolder> {
    <span class="hljs-keyword">protected</span> final List<T> mData;
    <span class="hljs-keyword">protected</span> final Context mContext;
    <span class="hljs-keyword">protected</span> LayoutInflater mInflater;
    <span class="hljs-keyword">private</span> OnItemClickListener mClickListener;
    <span class="hljs-keyword">private</span> OnItemLongClickListener mLongClickListener;

    <span class="hljs-keyword">public</span> <span class="hljs-title">BaseRecyclerAdapter</span>(Context ctx, List<T> list) {
        mData = (list != <span class="hljs-keyword">null</span>) ? list : <span class="hljs-keyword">new</span> ArrayList<T>();
        mContext = ctx;
        mInflater = LayoutInflater.<span class="hljs-keyword">from</span>(ctx);
    }

    @Override
    <span class="hljs-keyword">public</span> RecyclerViewHolder <span class="hljs-title">onCreateViewHolder</span>(ViewGroup parent, <span class="hljs-keyword">int</span> viewType) {
        final RecyclerViewHolder holder = <span class="hljs-keyword">new</span> RecyclerViewHolder(mContext,
                mInflater.inflate(getItemLayoutId(viewType), parent, <span class="hljs-keyword">false</span>));
        <span class="hljs-keyword">if</span> (mClickListener != <span class="hljs-keyword">null</span>) {
            holder.itemView.setOnClickListener(<span class="hljs-keyword">new</span> View.OnClickListener() {
                @Override
                <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) {
                    mClickListener.onItemClick(holder.itemView, holder.getLayoutPosition());
                }
            });
        }
        <span class="hljs-keyword">if</span> (mLongClickListener != <span class="hljs-keyword">null</span>) {
            holder.itemView.setOnLongClickListener(<span class="hljs-keyword">new</span> View.OnLongClickListener() {
                @Override
                <span class="hljs-keyword">public</span> boolean <span class="hljs-title">onLongClick</span>(View v) {
                    mLongClickListener.onItemLongClick(holder.itemView, holder.getLayoutPosition());
                    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
                }
            });
        }
        <span class="hljs-keyword">return</span> holder;
    }

    @Override
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onBindViewHolder</span>(RecyclerViewHolder holder, <span class="hljs-keyword">int</span> position) {
        bindData(holder, position, mData.<span class="hljs-keyword">get</span>(position));
    }

    @Override
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getItemCount</span>() {
        <span class="hljs-keyword">return</span> mData.size();
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">add</span>(<span class="hljs-keyword">int</span> pos, T item) {
        mData.add(pos, item);
        notifyItemInserted(pos);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">delete</span>(<span class="hljs-keyword">int</span> pos) {
        mData.remove(pos);
        notifyItemRemoved(pos);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setOnItemClickListener</span>(OnItemClickListener listener) {
        mClickListener = listener;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setOnItemLongClickListener</span>(OnItemLongClickListener listener) {
        mLongClickListener = listener;
    }

    <span class="hljs-keyword">abstract</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getItemLayoutId</span>(<span class="hljs-keyword">int</span> viewType);

    <span class="hljs-keyword">abstract</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">bindData</span>(RecyclerViewHolder holder, <span class="hljs-keyword">int</span> position, T item);

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> OnItemClickListener {
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onItemClick</span>(View itemView, <span class="hljs-keyword">int</span> pos);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> OnItemLongClickListener {
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onItemLongClick</span>(View itemView, <span class="hljs-keyword">int</span> pos);
    }
}</code></span>

Super ViewHolder!

其实,这还没完呢!重头戏在ViewHolder上!RecyclerView强制我们使用ViewHolder模式,然而缺不可避免地要写findViewById代码,有没有办法不写这样的代码呢?甚至连ViewHolder都不写呢?当然可以!

<span style="font-size:12px;"><code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RecyclerViewHolder</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">RecyclerView</span>.<span class="hljs-title">ViewHolder</span> {</span>
    <span class="hljs-keyword">private</span> SparseArray<View> mViews;<span class="hljs-comment">//集合类,layout里包含的View,以view的id作为key,value是view对象</span>
    <span class="hljs-keyword">private</span> Context mContext;<span class="hljs-comment">//上下文对象</span>

    <span class="hljs-keyword">public</span> <span class="hljs-title">RecyclerViewHolder</span>(Context ctx, View itemView) {
        <span class="hljs-keyword">super</span>(itemView);
        mContext = ctx;
        mViews = <span class="hljs-keyword">new</span> SparseArray<View>();
    }

    <span class="hljs-keyword">private</span> <T extends View> T <span class="hljs-title">findViewById</span>(<span class="hljs-keyword">int</span> viewId) {
        View view = mViews.get(viewId);
        <span class="hljs-keyword">if</span> (view == <span class="hljs-keyword">null</span>) {
            view = itemView.findViewById(viewId);
            mViews.put(viewId, view);
        }
        <span class="hljs-keyword">return</span> (T) view;
    }

    <span class="hljs-keyword">public</span> View <span class="hljs-title">getView</span>(<span class="hljs-keyword">int</span> viewId) {
        <span class="hljs-keyword">return</span> findViewById(viewId);
    }

    <span class="hljs-keyword">public</span> TextView <span class="hljs-title">getTextView</span>(<span class="hljs-keyword">int</span> viewId) {
        <span class="hljs-keyword">return</span> (TextView) getView(viewId);
    }

    <span class="hljs-keyword">public</span> Button <span class="hljs-title">getButton</span>(<span class="hljs-keyword">int</span> viewId) {
        <span class="hljs-keyword">return</span> (Button) getView(viewId);
    }

    <span class="hljs-keyword">public</span> ImageView <span class="hljs-title">getImageView</span>(<span class="hljs-keyword">int</span> viewId) {
        <span class="hljs-keyword">return</span> (ImageView) getView(viewId);
    }

    <span class="hljs-keyword">public</span> ImageButton <span class="hljs-title">getImageButton</span>(<span class="hljs-keyword">int</span> viewId) {
        <span class="hljs-keyword">return</span> (ImageButton) getView(viewId);
    }

    <span class="hljs-keyword">public</span> EditText <span class="hljs-title">getEditText</span>(<span class="hljs-keyword">int</span> viewId) {
        <span class="hljs-keyword">return</span> (EditText) getView(viewId);
    }

    <span class="hljs-keyword">public</span> RecyclerViewHolder <span class="hljs-title">setText</span>(<span class="hljs-keyword">int</span> viewId, String value) {
        TextView view = findViewById(viewId);
        view.setText(value);
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
    }

    <span class="hljs-keyword">public</span> RecyclerViewHolder <span class="hljs-title">setBackground</span>(<span class="hljs-keyword">int</span> viewId, <span class="hljs-keyword">int</span> resId) {
        View view = findViewById(viewId);
        view.setBackgroundResource(resId);
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
    }

    <span class="hljs-keyword">public</span> RecyclerViewHolder <span class="hljs-title">setClickListener</span>(<span class="hljs-keyword">int</span> viewId, View.OnClickListener listener) {
        View view = findViewById(viewId);
        view.setOnClickListener(listener);
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
    }
}</code></span>

该类的核心方法是private T findViewById(int viewId),核心成员变量是private SparseArray mViews; 不信可以不写一句ViewHolder代码?接下来看看用法。

实践用法

添加Adapter仅需短短的几行代码:

<span style="font-size:12px;"><code class="hljs java has-numbering">Adapter = <span class="hljs-keyword">new</span> BaseRecyclerAdapter<String>(<span class="hljs-keyword">this</span>,mDataList) {
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getItemLayoutId</span>(<span class="hljs-keyword">int</span> viewType) {
                <span class="hljs-keyword">return</span> R.layout.item;
            }
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">bindData</span>(RecyclerViewHolder holder, <span class="hljs-keyword">int</span> position,String item) {
                <span class="hljs-comment">//调用holder.getView(),getXXX()方法根据id得到控件实例,进行数据绑定即可</span>
                holder.setText(R.id.tv_num,item)
                        .getTextView(R.id.tv_title,item).setText(item);
            }
        };</code></span>

完整代码:

<span style="font-size:12px;"><code class="hljs java has-numbering">    <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">init</span>() {
        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
        mDataList = <span class="hljs-keyword">new</span> ArrayList<>();
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i <= <span class="hljs-number">100</span>; i++) {
            mDataList.add(String.valueOf(i));
        }
        <span class="hljs-comment">//设置item动画</span>
        recyclerView.setItemAnimator(<span class="hljs-keyword">new</span> DefaultItemAnimator());
        mAdapter = <span class="hljs-keyword">new</span> BaseRecyclerAdapter<String>(<span class="hljs-keyword">this</span>,mDataList) {
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getItemLayoutId</span>(<span class="hljs-keyword">int</span> viewType) {
                <span class="hljs-keyword">return</span> R.layout.item;
            }
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">bindData</span>(RecyclerViewHolder holder, <span class="hljs-keyword">int</span> position,String item) {
                <span class="hljs-comment">//调用holder.getView(),getXXX()方法根据id得到控件实例,进行数据绑定即可</span>
                holder.setText(R.id.tv_num,item)
                        .getTextView(R.id.tv_title,item).setText(item);
            }
        };
        recyclerView.setAdapter(mAdapter);
        <span class="hljs-comment">//添加item点击事件监听</span>
        ((BaseRecyclerAdapter)mAdapter).setOnItemClickListener(<span class="hljs-keyword">new</span> BaseRecyclerAdapter.OnItemClickListener() {
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onItemClick</span>(View itemView, <span class="hljs-keyword">int</span> pos) {
                Toast.makeText(AdapterTestActivity.<span class="hljs-keyword">this</span>, <span class="hljs-string">"click "</span> + pos, Toast.LENGTH_SHORT).show();
            }
        });
        ((BaseRecyclerAdapter)mAdapter).setOnItemLongClickListener(<span class="hljs-keyword">new</span> BaseRecyclerAdapter.OnItemLongClickListener() {
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onItemLongClick</span>(View itemView, <span class="hljs-keyword">int</span> pos) {
                Toast.makeText(AdapterTestActivity.<span class="hljs-keyword">this</span>, <span class="hljs-string">"long click "</span> + pos, Toast.LENGTH_SHORT).show();
            }
        });
        <span class="hljs-comment">//设置布局样式LayoutManager</span>
        recyclerView.setLayoutManager(<span class="hljs-keyword">new</span> LinearLayoutManager(AdapterTestActivity.<span class="hljs-keyword">this</span>, LinearLayoutManager.VERTICAL, <span class="hljs-keyword">false</span>));
<span class="hljs-comment">//        recyclerView.addItemDecoration(new ItemDividerDecoration(MainActivity.this, OrientationHelper.VERTICAL));</span>

    }</code></span>

如果觉得有什么不妥之处或建议,敬请指教!
完整项目代码已上传至Github。—Github跳转

see also:
Listview的Adapter应该这样写

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值