打造万能的RecyclerView的Adapte

     自从Google推出MD之后,受到了广大程序员的喜爱。其中,最常用的莫过于RecyclerView,相对于传统的ListView而言,RecyclerView的功能更加的强大,其自身提供的LayoutManager可以对Item所显示的样式进行快速切换。好了,废话不多讲,我们就一起来看看吧。注意:我今天并不会讲RecyclerView的基本用法,我相信RecyclerView的基本用法是每个人都会的,所有我在这里就不再啰嗦了。


     在使用RecyclerView的时候,我们写适配器的时候一般写法如下:

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);
            }
        }
    }
那么我们现在先来分析一下这个Adapter,注意这个Adapter是由以下的几个部分组成。

1》ViewHolder

2》onBindViewHolder()

3》onCreateViewHolder()

4》getItemCount()

所谓打造一个万能的Adapter适配器,其实就是对这几个方法的再度封装。那么下面我们就一个一个的来分析下如何对这些方法进行封装。

1.  ViewHolder

public class ViewHolder extends RecyclerView.ViewHolder {

    // 存放所有的控件
    private SparseArray<View> mViews;

    public ViewHolder(View itemView) {
        super(itemView);
        mViews = new SparseArray<>();
    }

    // 获取每个控件
    public <T extends View> T getView(int viewId) {
        View view = mViews.get(viewId);
        if (view == null) {
            view = itemView.findViewById(viewId);
            mViews.put(viewId, view);
        }
        return (T) view;
    }

    // 设置TextView(采用链式调用的方式)
    public ViewHolder setText(int viewId, CharSequence text) {
        TextView tv = getView(viewId);
        tv.setText(text);
        return this;
    }

    // 设置ImageView(本地资源)
    public ViewHolder setImageView(int viewId, int resourceId) {
        ImageView imageView = getView(viewId);
        imageView.setImageResource(resourceId);
        return this;
    }

    // 设置ImageView(url)
    // 遵循开闭原则,我们在这里最好不要写死
    // Glide.with(context).load(imageUrl).into(imageView);
    public ViewHolder setImageView(int viewId, HolderImageView holdImageView) {
        ImageView imageView = getView(viewId);
        holdImageView.loadImage(holdImageView.getPath(), imageView);
        return this;
    }

    // 使用静态类供外部使用
    public abstract static class HolderImageView {

        String imageUrl;

        public HolderImageView(String imageUrl) {
            this.imageUrl = imageUrl;
        }

        public abstract void loadImage(String imageUrl, ImageView imageView);

        public String getPath() {
            return imageUrl;
        }
    }
}
首先我们建一个类ViewHolder继承自RecyclerView.ViewHolder,当然我们必须要创建一个方法供外部调用获取到控件。那么这个方法就是getView方法,只需要传递一个控件所对应的id就能通过findViewById来拿到控件,在这里我们就要想了?如果我们的id相同,那么我们有没有什么办法不要每次都findViewById。哈哈哈,方法肯定是有,如上面所示,我们创建一个SparseArray来保存每次的view就可以了,实现过程请看上面的代码。在这个方法里面我还对常用的TextView和ImageView进行了处理,一看就懂,当然,我用到了当前比较火的一种方式,即链式调用的方式,不懂的童靴们赶快去学习一下哟,这个真的很好用。

2.getItemCount

   这个方法没有什么好讲的,也无须进行封装

3.onBindViewHolder

   大家都知道,这个方法其实是展示数据的方法,那么在这里我们就不能直接展示数据,否则的话封装就无从谈起。我们需要把这个方法通过抽象的方法传递出去,供具体的实现者来调用,实现方法也很简单

    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        convert(holder, list.get(position), position);
    }
            
     
    // 将展示的数据向外传递
    public abstract void convert(ViewHolder holder, T t, int position);
这里我要讲一下这个 T,大家都知道,T其实就是泛型,因为在这个里面我们并不知道外面的数据的具体采用的格式,可能是Bean,也可能是map等等,所以在这里我们只能采用泛型来实现数据的分离。


4.onCreateViewHolder   

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
       View view = inflater.inflate(viewId, parent, false);
        ViewHolder viewHolder = new ViewHolder(view);
        return viewHolder;
    }
注意,这里的viewId就是我们要加载的布局文件,我们把它声明为全局变量,然后在构造函数里面进行获取,这样,我们要显示的布局就可以通过外部来进行传递了,这样也就实现了布局分离的效果。

看到这里有些人可能已经懂了,有些人可能还是有些疑惑,为了让大家更方便快速的搞懂,我先把封装的那个adapter的全部代码先贴出来

**
 * Created by zhoufan on 2018/1/13.
 * 打造万能的适配器
 * 1.分离ViewHolder
 * 2.实现数据分离
 * 3.实现布局分离
 * 4.实现多布局效果
 */

public abstract class CommonAdapter<T> extends RecyclerView.Adapter<ViewHolder> {

    // 实现数据分离,采用泛型
    private List<T> list;
    // 布局通过外面传递进来
    private int viewId;
    private LayoutInflater inflater;
    // 支持多布局
    private MultiItemSupport multiItemSupport;

    public CommonAdapter(Context context, List<T> list, int viewId) {
        this.list = list;
        this.viewId = viewId;
        inflater = LayoutInflater.from(context);
    }

    public CommonAdapter(Context context, List<T> list, MultiItemSupport multiItemSupport) {
        this(context, list, -1);
        this.multiItemSupport = multiItemSupport;
    }


    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (multiItemSupport != null) {
            viewId = viewType;
        }
        View view = inflater.inflate(viewId, parent, false);
        ViewHolder viewHolder = new ViewHolder(view);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        convert(holder, list.get(position), position);
        // 设置点击事件
        if (onItemClick != null) {
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onItemClick.onItemClick(position);
                }
            });
        }

        // 设置长按点击事件
        if (onItemLongClick != null) {
            holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    return onItemLongClick.onItemLongClick(position);
                }
            });
        }
    }


    @Override
    public int getItemViewType(int position) {
        if (multiItemSupport != null) {
            return multiItemSupport.getItemView(position);
        }
        return super.getItemViewType(position);
    }

    // 将展示的数据向外传递
    public abstract void convert(ViewHolder holder, T t, int position);


    @Override
    public int getItemCount() {
        return list != null ? list.size() : 0;
    }

    // 设置Item的点击事件
    private OnItemClickListener onItemClick;

    public void setOnItemClickListener(OnItemClickListener onItemClick) {
        this.onItemClick = onItemClick;
    }

    // 设置Item的长按点击事件
    private OnItemLongClickListener onItemLongClick;

    public void setOnItemLongClickListener(OnItemLongClickListener onItemLongClick) {
        this.onItemLongClick = onItemLongClick;
    }

}
最后我要讲解的一点就是关于多布局的问题,我们都知道,在实际的开发过程中,多布局是很普遍的,那么当我们涉及到多布局的时候我们应该怎么处理呢?首先,我们需要复写getItemViewType方法,然后判断当前是否有多布局,如果有,那么我们获取到当前位置的布局,然后进行向下传递。因为onCreateViewHolder是在getItemViewType后执行的,而getItemViewType的返回值等于onCreateViewHolder的viewType,所以我们只需要对viewType进行处理即可。


接下来就是重点了,当我们封装完了之后如何使用呢?哈哈,其实使用也是so easy的,不信,你瞧

/**
 * Created by zhoufan on 2018/1/13.
 * 具体的实现类
 */

public class Adapter extends CommonAdapter {

    public Adapter(Context context, final List<Map<String, Object>> list) {
        super(context, list, new MultiItemSupport() {
            @Override
            public int getItemView(int position) {
                if (list.get(position).get("flag").toString().equals("0")) {
                    return R.layout.item_me;
                } else {
                    return R.layout.item_friend;
                }
            }
        });
    }


    @Override
    public void convert(ViewHolder holder, Object data, int position) {
        Map<String, Object> map = (Map<String, Object>) data;
        holder.setText(R.id.name, String.valueOf(map.get("name")))
                .setText(R.id.age, String.valueOf(map.get("age")));
    }
}
哈哈,是不是很简单,至此,一个万能的adapter就完成了,在以后的开发过程中,我们就只需调用convert方法来展示数据就可以了


源码下载





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值