Android View 事件防抖

两种方案

  • 侵入式防抖处理(NoShakeClickListener) implements View.OnClickListener

  • 无侵入式防抖处理(NoShakeClickListener2) not implements View.OnClickListener

侵入式防抖处理(NoShakeClickListener)

1. 既适用于单个View事件防抖, 也适用于AdapterItemView事件防抖

2. 如果事件为跳转到新的Activity, 该Activity启动模型应为android:launchMode="singleTop"

Java 版本

public abstract class NoShakeClickListener implements View.OnClickListener {

    private long mTimeInterval = 500L;
    /**
     * 最近一次点击的时间
     */
    private long mLastClickTime;
    /**
     * 最近一次点击的控件ID
     */
    private int mLastClickViewId;

    public NoShakeClickListener() {
    }

    public NoShakeClickListener(long interval) {
        this.mTimeInterval = interval;
    }

    @Override
    public void onClick(View v) {
        final boolean isFastClick = isFastDoubleClick(v, this.mTimeInterval);
        if (isFastClick) {
            onFastClick(v);
        } else {
            onSingleClick(v);
        }
    }

    /**
     * 是否是快速点击
     *
     * @param v        点击的控件
     * @param interval 时间间期(毫秒)
     * @return true:是,false:不是
     */
    private boolean isFastDoubleClick(View v, long interval) {
        int viewId = v.getId();
        long nowTime = System.currentTimeMillis();
        long timeInterval = Math.abs(nowTime - mLastClickTime);
        if (timeInterval < interval && viewId == mLastClickViewId) {
            // 快速点击事件
            return true;
        } else {
            // 单次点击事件
            mLastClickTime = nowTime;
            mLastClickViewId = viewId;
            return false;
        }
    }
    
    protected void onFastClick(View v) {}
    protected abstract void onSingleClick(View v);
}

可简写为

public abstract class NoShakeListener implements OnClickListener {
    private long mLastClickTime = 0;
    private boolean isFastDoubleClick() {
        long nowTime = System.currentTimeMillis();
        if (Math.abs(nowTime - mLastClickTime) < 500) {
            return true; // 快速点击事件
        } else {
            mLastClickTime = nowTime;
            return false; // 单次点击事件
        }
    }
    @Override
    public void onClick(View v) {
        if (isFastDoubleClick()) {
            onFastClick(v);
        } else {
            onSingleClick(v);
        }
    }
    protected void onFastClick(View v) {
    }
    protected abstract void onSingleClick(View v);
}

Kotlin版本

abstract class NoShakeClickListener @JvmOverloads constructor(interval: Long = 500L) : View.OnClickListener {

    private var mTimeInterval = 500L
    private var mLastClickTime: Long = 0   //最近一次点击的时间
    private var mLastClickViewId = 0       //最近一次点击的控件ID

    init {
        mTimeInterval = interval
    }

    override fun onClick(v: View) {
        if (isFastDoubleClick(v, mTimeInterval)) onFastClick(v) else onSingleClick(v)
    }

    /**
     * 是否是快速点击
     *
     * @param v        点击的控件
     * @param interval 时间间期(毫秒)
     * @return true:是,false:不是
     */
    private fun isFastDoubleClick(v: View, interval: Long): Boolean {
        val viewId = v.id
        val nowTime = System.currentTimeMillis()
        val timeInterval = abs(nowTime - mLastClickTime)
        return if (timeInterval < interval && viewId == mLastClickViewId) {
            // 快速点击事件
            true
        } else {
            // 单次点击事件
            mLastClickTime = nowTime
            mLastClickViewId = viewId
            false
        }
    }

    protected open fun onFastClick(v: View?) {}
    protected abstract fun onSingleClick(v: View?)
}

差点忘了... 相应的扩展函数:

fun View?.noShake(block: (v: View?) -> Unit) {
    this?.apply {
        setOnClickListener(object : NoShakeClickListener() {
            override fun onSingleClick(v: View?) {
                block.invoke(v)
            }
        })
    }
}

RxJava2 clickExt.kt

inline fun <T : View> T.noShake(crossinline listener: T.() -> Unit) = this.noShake(1000, listener)

inline fun <T : View> T.noShake(windowDuration: Long = 500, crossinline listener : T.() -> Unit) =
    RxView.clicks(this)
        .throttleFirst(windowDuration, TimeUnit.MILLISECONDS)
        .subscribe(object : Observer<Any> {
            override fun onSubscribe(d: Disposable) {}
            override fun onError(e: Throwable) {}
            override fun onComplete() {}
            override fun onNext(o: Any) {
                listener()
            }
        })

无侵入式防抖处理(NoShakeClickListener2)

特点:

1 移除了对View.OnClickListener的依赖, 在不破坏原有代码已设置好的OnClickListener基础上继续处理事件防抖;

2 支持简单处理事件和泛型回调

/**
 * 事件防抖
 * 注: 不仅适用于 View , 其他控件如: MenuItem 同样适用
 *
 * 1.既适用于单个`View`事件防抖, 也适用于`Adapter`中`ItemView`事件防抖
 * 2.如果事件为跳转到新的`Activity`, 该`Activity`启动模型应为`android:launchMode="singleTop"`
 */
open class NoShakeClickListener2 @JvmOverloads constructor(interval: Long = 500L) {

    private var mTimeInterval = 500L
    private var mLastClickTime: Long = 0   //最近一次点击的时间
    private var mLastClick: Any? = null    //最近一次点击的控件 View or MenuItem ...

    init {
        mTimeInterval = interval
    }

    fun proceedClick() {
        if (isFastClick(null, mTimeInterval)) onFastClick(null) else onSingleClick(null)
    }

    fun <T> proceedClick(item: T?) {
        if (isFastClick(item, mTimeInterval)) onFastClick(item) else onSingleClick(item)
    }

    /**
     * 是否是快速点击
     *
     * @param item      点击的控件 View or MenuItem ...
     * @param interval 时间间期(毫秒)
     * @return true:是,false:不是
     */
    private fun <T> isFastClick(item: T?, interval: Long): Boolean {
        val nowTime = System.currentTimeMillis()
        val timeInterval = abs(nowTime - mLastClickTime)
        return if (timeInterval < interval && item == mLastClick) {
            // 快速点击事件
            true
        } else {
            // 单次点击事件
            mLastClickTime = nowTime
            mLastClick = item
            false
        }
    }

    protected open fun onFastClick(item: Any?) {}
    protected open fun onSingleClick(item: Any?) {}
}

实用案例

1 简单使用

//快速点击事件
val fastClick=object :NoShakeClickListener2(){
    override fun onFastClick(item: Any?) {
        super.onFastClick(item)
        //此时 item == null
        Log.e("123", "onFastClick Click")
    }
}
BottomNavigationView.setOnNavigationItemSelectedListener {
    switchPage(it.itemId)
    fastClick.proceedClick()
    true
}

2 回传参数

//快速点击事件
val fastClick=object :NoShakeClickListener2(){
    override fun onFastClick(item: Any?) {
        super.onFastClick(item)
        //此时 item == null 为 proceedClick(it) 中的 it
        Log.e("123", "onFastClick Click")
    }
}
BottomNavigationView.setOnNavigationItemSelectedListener {
    switchPage(it.itemId)
    fastClick.proceedClick(it)
    true
}

2021年4月20日 10:39:35 最简单用法

private abstract static class NoShakeListener implements OnClickListener {
   private long mLastClickTime = 0;

   private boolean isFastDoubleClick() {
       long nowTime = System.currentTimeMillis();
       if (Math.abs(nowTime - mLastClickTime) < 500) {
           return true; // 快速点击事件
       } else {
           mLastClickTime = nowTime;
           return false; // 单次点击事件
       }
   }

   @Override
   public void onClick(View v) {
       if (isFastDoubleClick()) {
           onFastClick(v);
       } else {
           onSingleClick(v);
       }
   }

   protected void onFastClick(View v) {
   }

   protected abstract void onSingleClick(View v);
}

本文作者:风之旅人

本文链接:https://www.cnblogs.com/jooy/p/17302054.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   javakam  阅读(0)  评论(0编辑  收藏  举报  
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起