- 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")
}
}
}
网友评论