【从 0 开始开发一款直播 APP】3.2 高层封装之 Adapter — RecyclerView 实现单布局展示

字数 961阅读 174

本文为菜鸟窝作者蒋志碧的连载。“从 0 开始开发一款直播 APP ”系列来聊聊时下最火的直播 APP,如何完整的实现一个类"腾讯直播"的商业化项目
视频地址:http://www.cniao5.com/course/10121


【从 0 开始开发一款直播 APP】3.1 高层封装之 Adapter — ListView & GridView
【从 0 开始开发一款直播 APP】3.2 高层封装之 Adapter — RecyclerView 实现单布局展示
【从 0 开始开发一款直播 APP】3.3 高层封装之 Adapter -- RecyclerView 实现多条目展示
【从 0 开始开发一款直播 APP】3.4 高层封装之 Adapter -- RecyclerView 优雅的添加 Header、Footer


一、前言

之前已经讲过 ListView、GridView 的 Adapter 封装,这次将带来 RecyclerView 的封装。

RecyclerView 是一种新的视图组,目标是为任何基于适配器的视图提供相似的渲染方式。该控件用于在有限的窗中展示大量数据集,它被作为 ListView 和 GridView 控件的继承者。

主要有以下功能:

1、简单数据绑定(单种 item)

2、多布局绑定 (多种 item)

**3、设置监听 **

4、优雅的添加 header、footer

老规矩,先封装 ViewHolder,再封装 Adapter。

二、ViewHolder 的封装

2.1、RecyclerView 在使用之前需要添加依赖

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

2.2、创建一个类继承 RecyclerView.ViewHolder,实现其构造函数

public class RecyclerViewHolder extends RecyclerView.ViewHolder {
  public RecyclerViewHolder(View itemView) {
        super(itemView);
    }
}

2.3、其它的封装和之前写过对 Adapter 封装类似,讲了封装的大致步骤,这里不细讲了,直接看 从 0 开始开发一款直播 APP | 3.1 高层封装之 Adapter — ListView & GridView,一个 getView() 获取控件,通过 SparseArray 存储控件。

private SparseArray<View> mViews;

public RecyclerViewHolder(View itemView) {
    super(itemView);
    this.mViews = new SparseArray<>();
}

/**
 * 从ItemView获取View
 * @param id  ItemView里包含的ViewId
 * @param <V> 返回View
 * @return
 */
public <V extends View> V getView(int id) {
    View view = mViews.get(id);
    if (view == null) {
        view = itemView.findViewById(id);
        mViews.put(id, view);
    }
    return (V) view;
}

2.4、设置控件以及监听(采用链式编程方法)

/**
 * 设置TextView的值
 * @param viewId
 * @param text
 * @return
 */
public RecyclerViewHolder setText(int viewId, String text) {
    TextView tv = getView(viewId);
    tv.setText(text);
    return this;
}

/**
 * 设置ImageView的值
 * @param viewId
 * @param resId
 * @return
 */
public RecyclerViewHolder setImageResource(int viewId, int resId) {
    ImageView view = getView(viewId);
    view.setImageResource(resId);
    return this;
}

/**
 * 设置ImageView的值
 * 第三方  ImageLoder Glide Picasso
 * 不能直接写死第三方图片加载
 * 使用自己的一套规范  ImageLoder
 * @param viewId
 * @return
 */
public RecyclerViewHolder setImagePath(int viewId,ImageLoder imageLoder) {
    ImageView view = getView(viewId);
    imageLoder.loadImage(view,imageLoder.getPath());
    return this;
}

//图片加载 (对第三方库加载图片等封装)
public abstract static class ImageLoder{
    private String path;
    public ImageLoder(String path){
        this.path = path;
    }
    //需要复写该方法加载图片
    public abstract void loadImage(ImageView imageView,String path);
    public String getPath() {
        return path;
    }
}

/**
 * 设置是否可见
 * @param viewId
 * @param visible
 * @return
 */
public RecyclerViewHolder setVisible(int viewId, boolean visible) {
    View view = getView(viewId);
    view.setVisibility(visible ? View.VISIBLE : View.GONE);
    return this;
}

/**
 * 设置tag
 * @param viewId
 * @param tag
 * @return
 */
public RecyclerViewHolder setTag(int viewId, Object tag) {
    View view = getView(viewId);
    view.setTag(tag);
    return this;
}

public RecyclerViewHolder setTag(int viewId, int key, Object tag) {
    View view = getView(viewId);
    view.setTag(key, tag);
    return this;
}

/**
 * 设置Checkable
 * @param viewId
 * @param checked
 * @return
 */
public RecyclerViewHolder setChecked(int viewId, boolean checked) {
    Checkable view = (Checkable) getView(viewId);
    view.setChecked(checked);
    return this;
}

//点击事件
public RecyclerViewHolder setOnClickListener(int viewId,View.OnClickListener listener) {
    View view = getView(viewId);
    view.setOnClickListener(listener);
    return this;
}

//触摸事件
public RecyclerViewHolder setOnTouchListener(int viewId,View.OnTouchListener listener) {
    View view = getView(viewId);
    view.setOnTouchListener(listener);
    return this;
}

//长按事件
public RecyclerViewHolder setOnLongClickListener(int viewId,View.OnLongClickListener listener) {
    View view = getView(viewId);
    view.setOnLongClickListener(listener);
    return this;
}

三、Adapter 的封装

首先创建 Adapter 并继承 RecyclerView.Adapter ,将 ViewHolder 作为范型传入,将数据作为范型传入,并实现其方法。方法和 ListView 封装类似,传入布局,数据,上下文,通过 onCreateViewHolder() 实例化 ViewHolder,onBindViewHolder() 绑定 ViewHolder,用于数据,事件绑定,将该方法抽象出去,让用户实现,用户通过该方法拿到 item 对象。

public abstract class RecyclerViewAdapter<T> extends RecyclerView.Adapter<RecyclerViewHolder> {
  
    protected int mLayoutId;//布局id
    protected List<T> mDatas;//数据源
    protected Context mContext;//上下文
    private LayoutInflater mInflater;

    public RecyclerViewAdapter(Context context, int layoutId, List<T> datas) {
        this.mContext = context;
        this.mLayoutId = layoutId;
        this.mDatas = datas;
        this.mInflater = LayoutInflater.from(mContext);
    }

    @Override
    public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = mInflater.inflate(mLayoutId,parent,false);
        return new RecyclerViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(RecyclerViewHolder holder, final int position) {
        bindData(holder,mDatas.get(position),position);
    }

    /**
     * 把必要参数传进去,让每个 Adapter 去设置具体值
     * @param holder RecyclerViewHolder 
     * @param t 数据
     * @param position 当前位置
     */
    protected abstract void bindData(RecyclerViewHolder holder, T t,int position);

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

四、Demo 演示单布局显示

4.1、由于用到网络图片,我使用的Glide加载,先添加依赖

dependencies {
    compile 'com.github.bumptech.glide:glide:3.7.0'
    compile 'com.android.support:recyclerview-v7:25.2.0'
}

4.2、在 AndroidManifest.xml 中添加网络访问权限

    <uses-permission android:name="android.permission.INTERNET"/>

4.3、创建一个类继承 RecyclerView.ViewHolder,实现其构造函数
RecyclerAdapterDemo.java

public class RecyclerAdapterDemo extends RecyclerViewAdapter<Item> {

    public RecyclerAdapterDemo(Context context, List<Item> datas) {
        super(context, R.layout.list_item, datas);
    }

    @Override
    protected void bindData(RecyclerViewHolder holder, final Item item, int position) {

        holder.setText(R.id.tv1,item.getTv1())
//                .setImageResource(R.id.img,item.getRes())
                .setOnClickListener(R.id.tv1, new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(mContext,item.getTv1(),Toast.LENGTH_SHORT).show();
                    }
                }).setOnLongClickListener(R.id.tv1, new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                Toast.makeText(mContext,"长按:"+item.getTv1(),Toast.LENGTH_SHORT).show();
                return true;//true 表示不响应点击事件 false 长按事件结束还会响应点击事件
            }
        });

        //加载网络图片
        holder.setImagePath(R.id.img, new RecyclerViewHolder.ImageLoder("http://tomcat.apache.org/images/tomcat.png") {
            @Override
            public void loadImage(ImageView imageView, String path) {
                Glide.with(mContext).load(path).into(imageView);
            }
        });
    }
}

4.5、单布局 Adapter 的使用

public class AdapterActivity extends BaseActivity {

    private RecyclerAdapterDemo mAdapterDemo;
    private ArrayList<Item> Datas;
    private RecyclerView mRecyclerView;

    @Override
    protected void setToolbar() {
    }

    @Override
    protected void setListener() {
    }

    //填充数据
    @Override
    protected void initData() {
        Datas = new ArrayList<>();
        for (int i = 1; i <= 30; i++) {
            Datas.add(new Item(R.drawable.tab_publish_normal,"我 get 新技能 " + i));
        }

        mAdapterDemo = new RecyclerAdapterDemo(this, Datas);
        mRecyclerView.setAdapter(mAdapterDemo);
    }

    @Override
    protected void initView() {
        mRecyclerView = obtainView(R.id.recyclerView);
        //LinearLayoutManager
        //添加分割线
        mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));  
        //添加布局管理器
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
      
      //分割线网上有很多示例,这里不贴代码
        //GridLayoutManager
//        mRecyclerView.addItemDecoration(new DividerGridItemDecoration(this));
//        mRecyclerView.setLayoutManager(new GridLayoutManager(this,2));
    }

    @Override
    protected int getLayoutId() {
        return R.layout.activity_adapter;
    }
}

4.6、activity_adapter.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.dali.admin.fragment.AdapterActivity">

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

</LinearLayout>

五、运行效果

**5.1、LinearLayoutManager **

5.2、GridLayoutManager

总结

单条目封装步骤

1、 ViewHolder 的封装,继承 RecyclerView.ViewHolder,实现其方法和构造函数
2、封装 ViewHolder 的 getView() 方法,避免 findViewById 及类型转换,提供设置控件和监听的一系列方法
3、Adapter 的封装,继承 RecyclerView.Adapter 类,将封装的 ViewHolder 作为范型传入,将数据源作为范型传入。如:BaseAdapter<T> extends RecyclerView.Adapter<BaseViewHolder>。实现其构造方法和抽象方法。
4、设置数据源,上下文对象,布局 ID,抽象绑定 ViewHolder 具体实现,将 ViewHolder 和数据源作为参数传递出去,让用户对数据进行相应处理。

单条目 Adapter 封装及其使用讲解完,下一章讲解多条目 Adapter 封装

更多内容,请关注菜鸟窝(微信公众号ID: cniao5),程序猿的在线学习平台。 转载请注明出处,本文出自菜鸟窝,原文链接http://www.cniao5.com/forum/thread/107b6b5210fe11e790dc00163e0230fa

关注公众号免费领取“N套客户端实战项目教程”

推荐阅读更多精彩内容