美文网首页程序员
Android - 利用注解加Rxjava解决view点击抖动

Android - 利用注解加Rxjava解决view点击抖动

作者: 进击的包籽 | 来源:发表于2020-08-20 18:00 被阅读0次
  • RxJava也出来很久了,最近学了一招,用Rxjava解决View点击抖动问题(指定时间内防止快速点击),需要自定义Observable来实现。
  • 如果每次都写Rxjava那一堆那代码给View加,那岂不是一堆重复代码,于是就想到用注解,虽然性能上退化了,但好看一些。
  • Android 开发也要掌握的Java知识 - Java注解

1.自定义一个Observable,每次点击view,发送事件去下游

public class RxView {
    static final String TAG = RxView.class.getName();

    /**
     * 放回自定义Observable,里面处理view点击
     * @param view
     * @return
     */
    public static Observable<View> click(View view) {
        return new RxViewObservable(view);
    }
}
public class RxViewObservable extends Observable<View> {
    final View view;

    public RxViewObservable(View view) {
        this.view = view;
    }

    @Override
    protected void subscribeActual(@NonNull Observer<? super View> observer) {
        RxViewClickListener parent = new RxViewClickListener(view, observer);
        observer.onSubscribe(parent);
        view.setOnClickListener(parent);
    }

    /**
     * 实现 View.OnClickListener 接口 控制点击
     * 实现 Disposable 接口 控制是否被销毁,销毁就不再发送点击
     */
    final class RxViewClickListener implements View.OnClickListener, Disposable {
        private final View view;
        private final Observer<? super View> observer;

        //使用原子类判断
        private AtomicBoolean atomicBoolean = new AtomicBoolean();

        RxViewClickListener(View view, Observer<? super View> observer) {
            this.view = view;
            this.observer = observer;
        }

        @Override
        public void onClick(View v) {
            //点击后,observer onNext发送出去
            if (!isDisposed()) {
                observer.onNext(v);
            }
        }

        /**
         * 如果已销毁了
         * 判断是否在主线程
         * 不再回调点击事件
         */
        @Override
        public void dispose() {
            //比较替换
            if (atomicBoolean.compareAndSet(false, true)) {
                if (Looper.myLooper() == Looper.getMainLooper()) {
                    view.setOnClickListener(null);
                } else {
                    AndroidSchedulers.mainThread().scheduleDirect(new Runnable() {
                        @Override
                        public void run() {
                            view.setOnClickListener(null);
                        }
                    });
                }
            }
        }

        @Override
        public boolean isDisposed() {
            return atomicBoolean.get();
        }
    }
}

2.写注解,传入控件Id,时间等参数

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RxViewAnnotation {
    int[] value();

    long time() default 0;

    TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
}

public class RxViewUtils {
    public static void init(Activity activity) {
        //获取类
        Class<? extends Activity> clazz = activity.getClass();

        //获取类的方法
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            //判断方法的注解是不是RxViewAnnotation
            if (method.isAnnotationPresent(RxViewAnnotation.class)) {
                method.setAccessible(true);
                RxViewAnnotation annotation = method.getAnnotation(RxViewAnnotation.class);

                //获取注解参数
                int[] ids = annotation.value();
                long time = annotation.time();
                TimeUnit timeUnit = annotation.timeUnit();

                for (int id : ids) {
                    if (time == 0) { 
                        //如果时间为0,就直接点击回调
                        viewClick(activity, method, id);
                    } else {
                        rxViewClick(activity, method, id, time, timeUnit);
                    }
                }
            }
        }
    }

    /**
     * 普通点击回调
     */
    private static void viewClick(final Activity activity, final Method method, @IdRes int id) {
        activity.findViewById(id).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                try {
                    method.invoke(activity);
                } catch (Exception e) {
                    try {
                        method.invoke(activity, view);
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * 防抖动点击
     *
     * @param activity
     * @param method
     * @param id       view_id
     * @param time     间隔时长
     * @param timeUnit 时长单位
     */
    private static void rxViewClick(final Activity activity, final Method method, @IdRes int id, long time, TimeUnit timeUnit) {
        RxView.click(activity.findViewById(id))
                .throttleFirst(time, timeUnit)
                .subscribe(new Observer<View>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {

                    }

                    @Override
                    public void onNext(@NonNull View view) {
                        try {
                            method.invoke(activity);
                        } catch (Exception e) {
                            try {
                                method.invoke(activity, view);
                            } catch (Exception ex) {
                                ex.printStackTrace();
                            }
                            e.printStackTrace();
                        }
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {

                    }

                    @Override
                    public void onComplete() {

                    }
                });
    }
}

3.使用,3000毫秒,只接受一次点击事件

 @RxViewAnnotation(
        value = [R.id.btn_test3, R.id.btn_test4],
        time = 3000,
        timeUnit = TimeUnit.MILLISECONDS
    )
    private fun onClick2(view: View) {
        when (view.id) {
            R.id.btn_test3 -> {
                Log.d(TAG, "btn_test3")
            }
            R.id.btn_test4 -> {
                Log.d(TAG, "btn_test4")
            }
        }
    }

相关文章

网友评论

    本文标题:Android - 利用注解加Rxjava解决view点击抖动

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