美文网首页 Android知识进阶(遥远的重头开始)
Android-RecyclerView通用适配器BaseAda

Android-RecyclerView通用适配器BaseAda

作者: MonkeyLei | 来源:发表于2019-07-18 09:07 被阅读1次

之前我们也说过Android-RecyclerView通用适配器BaseAdapter-多绘制类型-开始篇,通用适配器有两个重要的点:

1. 适配器的创建

2.数据的绑定

所以我们的BaseAdapter相当于需要进行

1. 封装好的BaseViewHolder的创建

2. 封装好的BaseDataModel提供的onBindViewHolder的方法(相当于onBindData方法)

3. 再说一点资源释放的地方onViewDetachedFromWindow方法中调用BaseDataModel提供的releaseResource方法。释放除了资源,还有设置的监听事件都可以释放(这些都是内存的相关优化的点)

4. 另外Context建议采用弱引用,避免持有上下文导致的内存泄漏

这里小白做个简单记录,封装也不是很好,另外可能还得逐渐深入和学习...

所以我们的BaseAdapter可以如下:

BaseAdapter.java

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;

import java.lang.ref.WeakReference;
import java.util.List;

/*
*@Description: 基础适配器
*@Author: hl
*@Time: 2018/11/1 11:42
*/
public class BaseAdapter extends RecyclerView.Adapter<BaseViewHolder> {

    private List<BaseDataModel> baseMulDataModelList = null;
    private WeakReference<Context> contextWeakReference = null;
    private BaseViewHolder.HandleView handleView = null;

    public BaseAdapter(Context context,
                       List<BaseDataModel> baseMulDataModelList,
                       BaseViewHolder.HandleView handleView){
        this.contextWeakReference = new WeakReference<>(context);
        this.baseMulDataModelList = baseMulDataModelList;
        this.handleView = handleView;
    }

    public BaseAdapter(Context context,
                       List<BaseDataModel> baseMulDataModelList){
        this.contextWeakReference = new WeakReference<>(context);
        this.baseMulDataModelList = baseMulDataModelList;
    }

    @Override
    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new BaseViewHolder(contextWeakReference.get(),
                DrawTypeManager.onCreateViewHolder(parent, viewType),
                handleView);
    }

    @Override
    public void onBindViewHolder(BaseViewHolder holder, int position) {
        baseMulDataModelList.get(position).onBindViewHolder(holder, handleView, position);
    }

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

    @Override
    public int getItemViewType(int position) {
        return baseMulDataModelList.get(position).getDrawType();
    }

    /**
     * Item资源释放
     * @param holder
     */
    @Override
    public void onViewDetachedFromWindow(BaseViewHolder holder) {
        super.onViewDetachedFromWindow(holder);

        ///< 释放资源
        int position = holder.getAdapterPosition();
        ///< 越界检查
        if(position < 0 || position >= baseMulDataModelList.size()){
            return;
        }
        baseMulDataModelList.get(position).releaseResource();
    }

    /**
     * 适配器资源释放
     * @param recyclerView
     */
    @Override
    public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
        super.onDetachedFromRecyclerView(recyclerView);
        if (null != contextWeakReference){
            contextWeakReference.clear();
            contextWeakReference = null;
        }
    }
}

而我们的BaseViewHolder可以干什么呢?既然是布局创建容器,自然需要提供控件的查找(findViewById), 另外还得提供监听事件,同时我们自定义了一些回调(比如重新动态设置控件的宽高、文本高亮等方法).

BaseViewHolder.java

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

/*
*@Description: 基础ViewHolder
*@Author: hl
*@Time: 2018/11/1 17:21
*/
public class BaseViewHolder<T extends BaseDataModel> extends RecyclerView.ViewHolder{
    private Context context;
    private SparseArray<View> views;
    private View itemView;
    private HandleView handleView;

    public BaseViewHolder(Context context, View itemView, HandleView handleView) {
        super(itemView);
        this.context = context;
        this.views = new SparseArray<>();
        this.itemView = itemView;
        this.handleView = handleView;
    }

    public View getItemView() {
        return itemView;
    }

    public View getView(int resId) {
        return retrieveView(resId);
    }

    public TextView getTextView(int resId){
        return retrieveView(resId);
    }

    public ImageView getImageView(int resId){
        return retrieveView(resId);
    }

    public Button getButton(int resId){
        return retrieveView(resId);
    }

    protected <V extends View> V retrieveView(int viewId){
        View view = views.get(viewId);
        if(view == null){
            view = itemView.findViewById(viewId);
            views.put(viewId, view);
            ///< 动态对控件进行设置
            if (null != handleView){
                handleView.readjust(view);
            }
        }
        return (V) view;
    }

    public Context getContext() {
        return context;
    }

    /**
     * 处理View相关
     */
    public interface HandleView<T extends BaseDataModel>{
        /**
         * 重新调整控件信息
         */
        public void readjust(View view);
        /**
         * 关键字高亮
         */
        public void highLight(View view, String text);
        /**
         * 点击事件
         */
        public void onclick(View view, int postion, T baseDataModel);
    }
}

由于是多布局类型,所以我们增加了一个DrawTypeManager作为布局类型的管理。小白看的有些网友是没有将这个单独提出来做,个人是想单独提出来进行管理。所以就由DrawTypeManager提供视图的创建并返回该布局创建的视图:

DrawTypeManager.java

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.lieyunwang.finance.R;

import java.util.HashMap;

/*
*@Description: 绘制类型管理器
*@Author: hl
*@Time: 2018/10/31 13:22
*/
public class DrawTypeManager extends BaseDrawType{
    public static final int INFO_NEWS_BANNER_TYPE = 1;
    public static final int INFO_NEWS_CONTENT_TYPE = 2;
    public static final int INFO_LIEZUAN_CONTENT_TYPE = 3;
    ///< RV渲染类型对应的布局ID
    private static final HashMap<Integer, Integer> type2layoutid = new HashMap<Integer, Integer>(){
        {
            put(INFO_NEWS_BANNER_TYPE, R.layout.fragment_info_news_banner_adapter);
            put(INFO_NEWS_CONTENT_TYPE, R.layout.fragment_info_news_adapter);
            put(INFO_LIEZUAN_CONTENT_TYPE, R.layout.info_liezuan_info_adapter);
        }
    };

    /**
     * 获取布局视图对象
     * @param parent
     * @param viewType
     * @return
     */
    public static View onCreateViewHolder(ViewGroup parent, int viewType){
        return LayoutInflater.from(parent.getContext())
                .inflate(getLayoutId(viewType),
                        parent, false);
    }

    /**
     * 根据viewType获取布局id
     * @param viewType
     * @return
     */
    private static int getLayoutId(int viewType) {
        if (type2layoutid.containsKey(viewType)){
            return type2layoutid.get(viewType);
        }
        throw new RuntimeException("未定义布局!");
    }
}

当然不一定好,后面慢慢完善,说不定还是别的人比较好呢。

基本上创建ViewHolder就差不多可以了。然后就是数据的定义,我们的数据肯定是需要提供绘制类型drawType,然后提供绑定的方法onBindViewHolder, 还得提供释放资源的方法releaseResource。根据具体还可进行扩展:

BaseDataModel.java

   /*
*@Description: 基础数据标准
*@Author: hl
*@Time: 2018/11/5 16:12
*/
public abstract class BaseDataModel {
    protected int drawType = -1;

    public int getDrawType() {
        return drawType;
    }

    public void setDrawType(int drawType) {
        this.drawType = drawType;
    }

    public abstract void onBindViewHolder(BaseViewHolder holder, BaseViewHolder.HandleView handleView, int position);

    public abstract void releaseResource();
}

这里我做成抽象类,所以的具体实体类都需要实现绑定和释放的两个方法,然后drawType提供一个设置和返回的方法,这个地方其实也可以做成抽象类的。只是小白想绘制类型由父类进行管理!!!当然这个还得再考虑考虑...

还有一点就是DrawTypeManager继承了BaseDrawType,BaseDrawType中默认设置了几个类型:

BaseDrawType.java

   /*
*@Description: 绘制类型(布局统一配置)
*@Author: hl
*@Time: 2018/10/31 13:17
*/
public class BaseDrawType {
    public static final int ERROR_TYPE = Integer.MAX_VALUE - 1;
    public static final int EMPTY_TYPE = Integer.MAX_VALUE - 2;
    public static final int LOADING_TYPE = Integer.MAX_VALUE - 3;
    public static final int LOAD_MORE_TYPE = Integer.MAX_VALUE - 4;
    public static final int LOAD_NOMORE_TYPE = Integer.MAX_VALUE - 5;
}

这个后面做显示错误,空数据,加载中,加载更多会用到。

到这里基本上初步封装就算完事了。为了验证下,我们可以搞点测试数据..

简单测试数据

TestModel.java - 这是我工程的的测试数据,基本上就是这样的结构,重点就实现绑定和释放就行。看具体实际需要进行扩展。

public class TestModel extends BaseDataModel {
    /**
     * 基本属性相关 - 如果可以往父类搁置尽量搁置在父类
     */
    private int id;
    private String post_title;
    private String time_ago;
    private String poster;
    private int type = 0;

    ///< type == -1, Banner资源相关 - 有则显示,无则不管
    private List<String> titleList = null;
    private List<String> imageList = null;

    /**
     * 控件相关
     */
    private Banner banner = null;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getPost_title() {
        return post_title;
    }

    public void setPost_title(String post_title) {
        this.post_title = post_title;
    }

    public String getTime_ago() {
        return time_ago;
    }

    public void setTime_ago(String time_ago) {
        this.time_ago = time_ago;
    }

    public String getPoster() {
        return poster;
    }

    public void setPoster(String poster) {
        this.poster = poster;
    }

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    public List<String> getTitleList() {
        return titleList;
    }

    public void setTitleList(List<String> titleList) {
        this.titleList = titleList;
    }

    public List<String> getImageList() {
        return imageList;
    }

    public void setImageList(List<String> imageList) {
        this.imageList = imageList;
    }

    public void setDrawType(int drawType) {
        super.setDrawType(drawType);
    }

    public int getDrawType() {
        int drawType = super.getDrawType();
        if (-1 == drawType){
            if (-1 == type){    ///< -1 - banner
                super.setDrawType(DrawTypeManager.INFO_NEWS_BANNER_TYPE);
            }else{              ///< 0 -  normalItem  1 - 推广  2 - 专题
                super.setDrawType(DrawTypeManager.INFO_NEWS_CONTENT_TYPE);
            }
        }
        return super.getDrawType();
    }

    public String getTitle() {
        return post_title;
    }

    public String getTime() {
        return time_ago;
    }

    public String getImage() {
        return poster;
    }

    public BaseDataModel getBaseDataModel(){
        return this;
    }

    @Override
    public void onBindViewHolder(final BaseViewHolder holder,
                                  final BaseViewHolder.HandleView handleView,
                                  final int position) {
        ///< Bannner轮播数据绑定
        if (-1 == type){    ///< -1 - banner
            banner = (Banner) holder.getView(R.id.finba_banner);
            banner.setBannerTitles(getTitleList());
            banner.setImages(getImageList())
                    .setDelayTime(3000)
                    .isAutoPlay(true)
                    .start();
            ///< 轮播点击事件
            banner.setOnBannerListener(new OnBannerListener() {
                @Override
                public void OnBannerClick(int position) {
                    handleView.onclick(banner, position, getBaseDataModel());
                }
            });
        }
        ///< 正常列表、推广、专题数据绑定
        else{              ///< 0 -  normalItem  1 - 推广  2 - 专题
          ImageView posterIv = holder.getImageView(R.id.fina_posterIv);

           GlideApp.with(holder.getContext())
                        .load(getPoster())
                        .placeholder(R.drawable.pic_default_02)
                        .into(posterIv );
            }

            ///< 点击事件
            holder.getItemView().setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    handleView.onclick(holder.getItemView(), position, getBaseDataModel());
                }
            });
        }
    }

    @Override
    public void releaseResource() {
        ///< TODO 资源释放可以在这里做:停止动画、还原某些动作呀
        if (null != banner){
            banner.stopAutoPlay();
            banner.releaseBanner();
            banner = null;
        }
    }
}

至于适配器的使用,其实如果你数据只是单纯展示,完全不用继承实现,直接可以使用BaseAdapter就行。

这里我们要做点事情,所以就继承扩展一下:

TestAdapter.java

 /**
 * 测试数据适配器
 */
public class TestAdapter extends BaseAdapter {
    public TestAdapter(Context context, List<BaseDataModel> baseMulDataModelList){
        this(context, baseMulDataModelList, new BaseViewHolder.HandleView<TestModel>() {
            @Override
            public void readjust(View view) {
                if (null != view){
                    if (view.getId() == R.id.fina_posterIv){          ///< 正常Item背景图片大小
                        ///< @HC 设置详情预览图片大小
                        HC_Screen.setConstraintLayoutWH(view, 360, 240);
                    }
                    else if (view.getId() == R.id.fina_posterBigIv){  ///< 广告大图大小
                        ///< 屏幕宽高固定比例
                        HC_Screen.setConstraintLayoutWHNoRatio(view,
                                HC_Screen.SCREEN_WIDTH - DensityUtil.dip2px(15),
                                (1 * (HC_Screen.SCREEN_WIDTH - DensityUtil.dip2px(15))) / 4);
                    }else if (view.getId() == R.id.finba_banner){     ///< 轮播图大小
                        Banner banner = (Banner) view;
                        banner.setBannerStyle(BannerConfig.CIRCLE_INDICATOR_TITLE);
                        banner.setImageLoader(new GlideImageLoader());
                    }
                }
            }

            @Override
            public void highLight(View view, String text) {

            }

            @Override
            public void onclick(View view, int postion, TestModel baseDataModel) {
                Log.e("test", "viewId=" + view.getId() + "  postion=" + postion + " id=" + baseDataModel.getId());
            }
        });
    }
    public TestAdapter(Context context, List<BaseDataModel> baseMulDataModelList, BaseViewHolder.HandleView handleView) {
        super(context, baseMulDataModelList, handleView);
    }
}

只是看个样子,需要可以自己参考修改就行。。 总得来说使用还是相对以前简单,而且很多情况下都直接使用BaseAdapter,只需要修改下datamodel就行了。目前来看简单多了一些。

具体直接RecycleView就可以进行使用了,搞个简单布局就OK! 使用之前也说过。

如果在把封装性和扩展做得更好更简洁的话,应该还是比较不错的。另外后面会去参考一些大神的封装,借鉴一下宝贵经验,提升记几个!!!

Ok,到这里小白的封装之路踏出了一步两步好几步....只能加油了....别人的路也是这样走出来的!

相关文章

网友评论

    本文标题:Android-RecyclerView通用适配器BaseAda

    本文链接:https://www.haomeiwen.com/subject/cchklctx.html