View 利用 RxJava 的点击去抖动实现

最近项目里有个点赞的功能需求,发现当用户对一条评论反复点赞的速度过快时,会引起轻微的数据错乱。对一条评论不停的点赞,评论数本应在+1-1之间不断循环的,但是点赞/取消点赞的时候需要进行网络请求,将点击结果上传到服务器,所以点多了之后,由于延时的原因,会出现某些+1-1操作会失效,具体表现为,对一个初始赞数为0的评论进行这样一波疯狂操作后,点赞总数有可能变成负数,这无疑是不允许的。

至于要问有什么用户会这么无聊,跟打地鼠一样疯狂点赞,那当然非我们的测试小哥莫属了。

思考了各种解决方案的优劣后,最终采用了最省事的一个方法,给点赞按钮加个抖动阈值。正好最近写 RxJava 写得不亦乐乎,再加上不想为了这一处地方就引入 RxBinding 库,就想着自己写一下,于是顺手就写出了以下代码用以去除点击抖动:

mBinding.title.setOnClickListener(v -> {
            Observable.create(e -> e.onNext(new Object()))
                    .throttleFirst(600, TimeUnit.MILLISECONDS)
                    .subscribe(o -> {
                        // 进行取消点赞或点赞的网络请求
                    });
        });

自己也没测就直接交给测试小哥了,然后被暴击:“你特么怎么该都没改就给我测了,这表现和之前没区别啊!?”

于是我瞬间懵比,自己一测,确实没解决问题,在代码里面写了俩 Log 一瞧,这去抖动根本没起任何作用啊,然后又找了网上几篇直接用 RxJava 实现的点击去抖动功能,发现我这也没写错啊。

左思右想,最后去看了下 RxBinding 的源码,才发现问题的关键所在。去抖动,需要将点击事件这个“事件”转换成一个Observable对象,而瞧我上面那段代码,直接在点击事件里生成的一个Observable对象,导致的结果就是每点击一次,就产生了一个新的 RxJava 流,所以导致没有达到想要的“只取600ms内第一次点击”的效果。

思考了下,转换成如下写法,总算达成效果了。

Observable.create((ObservableOnSubscribe<View>) e ->
                mBinding.title.setOnClickListener(v -> e.onNext(v))
        )
                .throttleFirst(600, TimeUnit.MILLISECONDS)
                .subscribe(view -> {
                    // 进行取消点赞或点赞的网络请求
                });

虽然 RxBinding 这个库也比较好用,但是我们的自定义控件好像就用不上它的,所以还是只能自己来实现去抖动了。

用 RxJava 实现的点击取抖动虽好,但有些情况还是顾不到,用它实现去抖动,就必须获取它的点击事件,将之转换成Observalbe对象才行,但有些情况这样是行不通的,比如一个页面的批量点击功能的实现:

public void onViewClicked(View view) {
    switch (view.getId()) {
        case R.id.check_for_update_button:
            break;
        case R.id.clear_cache_button:
            break;
        case R.id.save_flow_toggle:
            break;
        case R.id.feedback_button:
            break;
        case R.id.logout_button:
            break;
        default:
    }

在这个里面就根本没法获取到点击事件对象,所以要么将这些控件单独拿出来一个个实现,要么就用土办法,在上次点击后一定时间内让点击事件无效化,可以用如下代码:

private static final int CLICK_INTERVAL = 600;
private static long lastClickTime;

public static boolean clickValid() {
    long currentClickTime = System.currentTimeMillis();
    return currentClickTime - lastClickTime > CLICK_INTERVAL;
}

如果只需要上述所有点击事件共用一个抖动时间,那么将上面这段代码写入统一的工具类,并在switch语句之前调用即可,也可以个性化定制每个View的抖动时间,那就写起来麻烦一点。在我看来是个“傻大黑粗”的方法。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值