AndroidAndroid开发Android知识

可能是迄今最完美的RecyclerView的Adapter封装(

2017-08-03  本文已影响301人  秦小怪

本篇主要介绍SuperRecyclerAdapter之ViewHodler的封装

SuperRecyclerAdapter介绍

具体源码:https://github.com/qinxiaoguai/SuperRecyclerViewAdapter

RecyclerView的通用Adapter,在线上项目中使用很长时间,无bug产生,非常完美,代码很简单,没有那么复杂,实现的比较巧妙,特别是实现支持多类型item的时候,不需要设置item的type,不需要add各种东西,可以直接兼容RecyclerView横向布局,网格布局等的单类型item和多类型item,几乎兼容所有的列表需求,也可以实现楼层那种复杂列表,类似淘宝首页那种多种楼层(当然淘宝应该是使用vLayout来实现的吧,vLayout对于这种非常复杂的楼层列表性能更好,以后有时间会去研究一下vLayout源码),也很合适拿来学习,接下来两篇文章写的会很详细,一些简单的细节也会进行解释,希望不要因为解释太多而被嫌弃太啰嗦了。

       //就这段代码直接支持RecyclerView横向布局,网格布局等的单类型item和多类型item
        mAdapter = new SuperRecyclerAdapter<TicketEntity>(this, list) {
            @Override public void convert(SuperRecyclerHolder holder, TicketEntity entity, int viewType, int position) {
                
            }

            @Override public int getLayoutAsItemViewType(TicketEntity entity, int position) {
                return 0;
            }
        };

上边的简单的代码就是一个new就创建了一个兼容各种情况的Adapter,是不是很强大,而且兼容多布局,demo就暂时还没写

Adapter的具体封装要在下一篇博客推出了,还是时间不够用啊,没法一次性发出来了,本篇先进行介绍Adapter中的hodler的封装

PS:这段时间确实特别的忙,项目忙着进行联调和提测,以及交互UI各种改,没有抽下时间来写篇博客,现在终于闲下来了,这段时间也学到了很多的东西,准备系统学习一下Material design的东西,以及自定义Behavior,想彻底搞明白这些东西,这段时间正好在实现一个微博个人信息页面和支付宝个人信息页面那种相结合的特效,用Behavior实现,也是醉了,有志同道合的同学可以关注我一起学习呀

前先说说链式调用

        Flowable//
            .range(1, 10)
            .parallel()
            .runOn(Schedulers.computation())
            .map(v -> v * v)
            .sequential()
            .blockingSubscribe(System.out::println);

上边这段代码是我在Rxjava的github上直接拷贝过来的一段代码,这种可以一直.方法进行调用操作,这种方式写出的代码真的很漂亮,在这里引出RxJava并不是说RxJava的实现和我接下来说的相同,注意仅仅作为格式参考

接下来说说怎么实现链式调用吧,其实确实也很简单,我们平时也在一直使用使用StringBufferAlertDialog举例

        //StringBuffer并不陌生吧,每行后边添加注释的的//是为了格式化的时候不进行换行,排版
        StringBuffer buffer = new StringBuffer();
        buffer//
            .append("1111")//
            .append("2222")//
            .append("3333");

        //创建AlertDialog并show,每行后边添加注释的的//是为了格式化的时候不进行换行,排版
        new AlertDialog//
            .Builder(this)//
            .setTitle("提示")
            .setMessage("弹窗")
            .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                @Override public void onClick(DialogInterface dialog, int which) {
                    //确定点击的事件
                }
            })
            .setNegativeButton("取消", (dialog, which) -> {
                //这里是使用拉姆达表达式的写法,取消事件的点击事件
            })
            .create()
            .show();

点开append以及setTitle方法的源码如下:


        public synchronized StringBuffer append (String str){
            super.append(str);
            return this;
        }

        public Builder setTitle (@Nullable CharSequence title){
            P.mTitle = title;
            return this;
        }

看源码我们发现,其实就是把当前的实例返回回去,返回this就好了,确实很简单的,在写一些setLitsner,或者set各种属性的时候都可以使用这种方式,和拉姆达表达式以前使用可以美观很多 PS:我这段时间实现的一个需求就是仿支付宝截图后弹出意见反馈的情况(请自行脑补支付宝页面截图情况,还是有点东西可以分享的,过段时间分享出来),先注册,再监听,就采用该方式,学以致用

PS:注意:可能很多人使用以后,使用自动格式化后,发现排版很乱,使用rxjava也是,我刚开始使用的也是感觉很乱,所以要是真的一行这样使用的话,那不是美观反而更丑了


        buffer.append("1111").append("2222").append("3333");
        //解决,就是在需要换行的地方添加上注释//就好了,自动排版就不会自动换行了
        buffer//
            .append("1111")//
            .append("2222")//
            .append("3333");

也很好解决,就是在需要换行的地方添加上注释//就好了,自动排版就不会自动换行了

SuperRecyclerHodler封装

先上具体使用的代码:

        holder//
            .setText(R.id.tv_title, ticket.getTitle())
            .setText(R.id.tv_time, time)
            .setImageResource(R.id.iv_bg, resBg)
            .setVisibility(R.id.tv_look_ticket, type == ticket.getType)
            .setOnClickListenner(R.id.tv_look_ticket, new View.OnClickListener() {
                @Override public void onClick(View v) {

                }
            })
            .setOnItemClickListenner(new View.OnClickListener() {
                @Override public void onClick(View v) {
                    
                }
            });

使用SparseArray对View进行缓存操作,如果在缓存获取不到就去findViewById,然后再保存到缓存

    /**
     * 通过viewId从缓存中获取View
     * <p>
     * 对View进行缓存处理
     */
    private <T extends View> T retrieveView(@IdRes int viewId) {
        View retrieveView = mViewArray.get(viewId);
        if (retrieveView == null) {
            retrieveView = getItemView().findViewById(viewId);
            mViewArray.put(viewId, retrieveView);
        }
        return (T) retrieveView;
    }

具体就是让holder实现View,TextView,以及一些基类的set方法,传入View的Id去进行我们想要的操作,注意使用@StringRes,@ColorInt,@IdInt等一些注解限制


    public SuperRecyclerHolder setTextColor(@IdRes int viewId, @ColorInt int color) {
        TextView textView = retrieveView(viewId);
        textView.setTextColor(color);
        return this;
    }

    /**
     * 设置颜色,直接传入colorRes,在方法内部去转换
     */
    public SuperRecyclerHolder setTextColorResource(@IdRes int viewId, @ColorRes int colorRes) {
        TextView textView = retrieveView(viewId);
        textView.setTextColor(ContextCompat.getColor(getContext(), colorRes));
        return this;
    }

加入一些方便使用的方法,原则是方便使用,通用的方法可以都写进来,特别是使用框架加载图片的操作,可以加入

    /**
     * 该方法使用频率非常高,而且大多时候,是从网络加载的数据,所有可能会出现空指针异常
     * StringUtils.obtainNoNullText转换以后,确保不会出现空指针异常,网络请求的数据不需要再次进行判空操作
     *
     * 省去每次都要TextUti
     * ls.isEmpty操作,不用关心数据是否为空,可设置默认值
     */
    public SuperRecyclerHolder setText(@IdRes int viewId, String content, String defaultContent) {
        TextView textView = retrieveView(viewId);
        textView.setText(StringUtils.obtainNoNullText(content, defaultContent));
        return this;
    }
    /**
     * 传入是否显示,true显示,false Gone掉,该方法使用频率非常高,单独抽离出来
     */
    public SuperRecyclerHolder setVisibility(@IdRes int viewId, boolean isVisibility) {
        retrieveView(viewId).setVisibility(isVisibility ? View.VISIBLE : View.GONE);
        return this;
    }
    
    /**
     * 保留方法,自己根据项目进行修改
     * 也可以添加设置圆角的图片url等等,根据需求添加
     */
    public SuperRecyclerHolder setImageUrl(@IdRes int viewId, String url) {
        if (TextUtils.isEmpty(url)) {
            return this;
        }
        ImageView imageView = retrieveView(viewId);
        //TODO 请根据自己项目使用的图片加载框架来加载
        return this;
    }   

可以private私有化构造方法,禁止使用new去创建实例,限制使用自己的static方法去创建

    /**
     * 创建ViewHolder实例
     */
    public static SuperRecyclerHolder createViewHolder(View itemView, Context ctx) {
        return new SuperRecyclerHolder(itemView, ctx);
    }

    /**
     * 方法用private,私有化构造方法,限制只允许使用SuperRecyclerHolder.createViewHolder()来创建实例
     */
    private SuperRecyclerHolder(View itemView, Context ctx) {
        super(itemView);
        mCtx = ctx;
    }

此时整个SuperRecyclerHodler就封装完成了,下边整个类的结构和一些有说明的方法



/**
 * 通用的RecyclerView的ViewHolder
 * 采用链式调用
 * <p>
 * Created by 秦小怪 on 2017/8/1.
 */
public class SuperRecyclerHolder extends RecyclerView.ViewHolder {
    private Context mCtx;
    private SparseArray<View> mViewArray = new SparseArray<>();

    /**
     * 创建ViewHolder实例
     */
    public static SuperRecyclerHolder createViewHolder(View itemView, Context ctx) {
        return new SuperRecyclerHolder(itemView, ctx);
    }

    /**
     * 方法用private,私有化构造方法,限制只允许使用SuperRecyclerHolder.createViewHolder()来创建实例
     */
    private SuperRecyclerHolder(View itemView, Context ctx) {
        super(itemView);
        mCtx = ctx;
    }

    public Context getContext() {
        return mCtx;
    }

    public View getItemView() {
        return itemView;
    }

    public View getViewById(@IdRes int viewId) {
        return retrieveView(viewId);
    }

    /**
     * 整个item的点击事件
     */
    public SuperRecyclerHolder setOnItemClickListenner(@Nullable View.OnClickListener listener) {
        getItemView().setOnClickListener(listener);
        return this;
    }

    /**
     * 整个item的点击事件,可以根据条件来禁止某些符合条件的点击事件
     * <p>
     * 这个需求是在是开发的时候发现的,才加上去的,能不能点击根据后端的返回来确定的,可以使用该方法
     * 比如:列表显示了很多好友的用户名,在线的可以点击,不在线的不能点击,
     */
    public SuperRecyclerHolder setOnItemClickListenner(boolean isListener,
        @Nullable View.OnClickListener listener) {
        getItemView().setOnClickListener(isListener ? listener : null);
        return this;
    }
    
    /**
     * 传入是否显示,true显示,false Gone掉,该方法使用频率非常高,单独抽离出来
     */
    public SuperRecyclerHolder setVisibility(@IdRes int viewId, boolean isVisibility) {
        retrieveView(viewId).setVisibility(isVisibility ? View.VISIBLE : View.GONE);
        return this;
    }

    /**
     * setVisibility扩展,isDefGone是只消失是采用View.GONE还是采用View.INVISIBLE进行消失
     */
    public SuperRecyclerHolder setVisibility(@IdRes int viewId, boolean isVisibility,
        boolean isDefGone) {
        retrieveView(viewId).setVisibility(
            isVisibility ? View.VISIBLE : isDefGone ? View.GONE : View.INVISIBLE);
        return this;
    }
    
    ........
    
    省略了很多的set方法,完整代码请到github
    
    ........
    
    
    /**
     * AppCompatCheckBox, AppCompatCheckedTextView, AppCompatRadioButton,CheckBox, CheckedTextView,
     * CompoundButton, RadioButton, Switch, SwitchCompat, ToggleButton都可以使用
     *
     * 最常使用的是CheckBox,RadioButton,SwitchCompat设置check
     */
    public SuperRecyclerHolder setChecked(@IdRes int viewId, boolean checked) {
        Checkable iCheckable = retrieveView(viewId);
        iCheckable.setChecked(checked);
        return this;
    }

    /**
     * CheckBox, RadioButton, Switch, SwitchCompat, ToggleButton
     * ,AppCompatCheckBox, AppCompatRadioButton等都可以使用
     * 凡是继承CompoundButton的都可以使用
     *
     * 最常使用的是CheckBox,RadioButton,SwitchCompat设置监听
     */
    public SuperRecyclerHolder setOnCheckedChangeListener(@IdRes int viewId,
        CompoundButton.OnCheckedChangeListener onCheckedChangeListener) {
        CompoundButton checkBox = retrieveView(viewId);
        checkBox.setOnCheckedChangeListener(onCheckedChangeListener);
        return this;
    }

    /**
     * 该方法使用频率非常高,而且大多时候,是从网络加载的数据,所有可能会出现空指针异常
     * StringUtils.obtainNoNullText转换以后,确保不会出现空指针异常,网络请求的数据不需要再次进行判空操作
     *
     * 省去每次都要TextUtils.isEmpty操作,不用关心数据是否为空
     */
    public SuperRecyclerHolder setText(@IdRes int viewId, String content) {
        return setText(viewId, content, "");
    }

    /**
     * 该方法使用频率非常高,而且大多时候,是从网络加载的数据,所有可能会出现空指针异常
     * StringUtils.obtainNoNullText转换以后,确保不会出现空指针异常,网络请求的数据不需要再次进行判空操作
     *
     * 省去每次都要TextUtils.isEmpty操作,不用关心数据是否为空,可设置默认值
     */
    public SuperRecyclerHolder setText(@IdRes int viewId, String content, String defaultContent) {
        TextView textView = retrieveView(viewId);
        textView.setText(StringUtils.obtainNoNullText(content, defaultContent));
        return this;
    }
    
    /**
     * 设置颜色,直接传入colorRes,在方法内部去转换
     */
    public SuperRecyclerHolder setTextColorResource(@IdRes int viewId, @ColorRes int colorRes) {
        TextView textView = retrieveView(viewId);
        textView.setTextColor(ContextCompat.getColor(getContext(), colorRes));
        return this;
    }
    
    /**
     * 保留方法,自己根据项目进行修改
     * 也可以添加设置圆角的图片url等等,根据需求添加
     */
    public SuperRecyclerHolder setImageUrl(@IdRes int viewId, String url) {
        if (TextUtils.isEmpty(url)) {
            return this;
        }
        ImageView imageView = retrieveView(viewId);
        //TODO 请根据自己项目使用的图片加载框架来加载
        return this;
    }
    
    /**
     * 通过viewId从缓存中获取View
     * <p>
     * 对View进行缓存处理
     */
    private <T extends View> T retrieveView(@IdRes int viewId) {
        View retrieveView = mViewArray.get(viewId);
        if (retrieveView == null) {
            retrieveView = getItemView().findViewById(viewId);
            mViewArray.put(viewId, retrieveView);
        }
        return (T) retrieveView;
    }
}

最后

下一篇会写具体SuperRecyclerAdapter的核心封装以及各种兼容场景的demo效果

整个源码还没有封装完成,只完成了部分等着,先把项目的github地址贴出来吧:

具体源码:https://github.com/qinxiaoguai/SuperRecyclerViewAdapter

关于我

如果感觉对你有帮助,可以关注我,或者关注我的微信公众号,工作以及学习中的问题,分享我个人的技术成长过程,工作中的心得和开发等问题,坚持原创,我本人是一个Andrid技术的热爱狂。
虽然知道贴出微信公众号的可能很少有人去扫描(PS:貌似大家都用手机也没法扫描,确实想运营一个微信订阅号,用心去运营)还是贴出来吧,或者微信公众号搜索:秦小怪,就可以找到我了

微信搜索公众号:秦小怪 也可以找到我
上一篇 下一篇

猜你喜欢

热点阅读