美文网首页
Android两种方式实现延迟搜索

Android两种方式实现延迟搜索

作者: kevinsEegets | 来源:发表于2021-11-17 14:28 被阅读0次

    每个应用都有搜索功能,通常我们在进行搜索时,只要是输入框输入了内容就要执行搜索请求,这样会产生很多次无意义的接口请求,同时用户体验也非常差。遇到这种情况我们的一般的解决思路是:当输入内容超过设定的时间间隔将执行搜索,否则不执行。

    需要依赖的类:

    YqTextChangeListener.kt
    /**
     * Created by wangkai on 2021/01/30 10:31
    
     * Desc TextView事件监听
     */
    open class YqTextChangeListener : TextWatcher {
        override fun afterTextChanged(s: Editable?) {
            logger {
                "afterTextChanged s=$s"
            }
        }
    
        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
            logger {
                "beforeTextChanged s=$s; start=$start; after=$after; count=$count"
            }
        }
    
        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            logger {
                "onTextChanged s=$s; start=$start; before=$before; count=$count"
            }
        }
    
    }
    
    LifecycleOwerExt.kt
    
    /**
     * Context 用于返回LifecycleOwner
     */
    fun Context?.getLifecycleOwner(): LifecycleOwner? {
       return when (this) {
            is Fragment -> {
                return this
            }
            is LifecycleOwner -> {
                return this
            }
            else -> {
                null
            }
        }
    }
    
    EditTextExt.kt
    
    ```kotlin
    
    @file:JvmName("EditTextExt")
    /**
     * 搜索间隔
     */
    fun EditText?.addTextChangedDebounceListener(debounceTimeOutInMills: Long = 300L, onTextChanged: ((String?) -> Unit)? = null) {
    
        this?.let {
            this.addTextChangedListener(YqTextChangeDebounceListener(this, debounceTimeOutInMills) { str: String? ->
                onTextChanged?.invoke(str)
            })
        }
    }
    
    /**
     * 搜索间隔
     */
    
    fun EditText?.addTextChangedCoroutineDebounceListener(debounceTimeOutInMills: Long = 300L, onTextChanged: ((String?) -> Unit)? = null) {
    
        this?.let {
            this.addTextChangedListener(YqTextChangeCoroutineDebounceListener(this, debounceTimeOutInMills) { str: String? ->
                onTextChanged?.invoke(str)
            })
        }
    }
    

    我们使用两种方式显示:

    Handler

    监听EditText的输入,每当文本变化,先检查Handler当前有无未处理的消息,有则移除该消息,然后用sendEmptyMessageDelayed再发送一条延迟消息,如若文本超过延迟时间没有变化,该延迟消息就可以成功执行

    /**
     * Created by wangkai on 2021/01/30 10:31
    
     * Desc EditText事件搜索监听,使用Handler延迟方式实现
     */
    
    open class YqTextChangeDebounceListener(view: View, private val delayMillis: Long = 300L, onTextChanged: ((String?) -> Unit)? = null) : YqTextChangeListener() {
    
        private var handler: Handler? = null
    
        private val rand by lazy {
            Random(100)
        }
    
        companion object {
    
            private var HANDLER_WHAT = 0
    
            private const val BUNDLE_KEY = "bundleKey"
        }
    
        init {
    
            HANDLER_WHAT = rand.nextInt()
    
            view.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
                override fun onViewAttachedToWindow(v: View?) {
                    logger {
                        "onViewAttachedToWindow 感知View状态变化"
                    }
    
                    handler = Handler(Looper.getMainLooper()) {
                        onTextChanged?.invoke(it.peekData().getString(BUNDLE_KEY))
                        false
                    }
                }
    
                override fun onViewDetachedFromWindow(v: View?) {
                    logger {
                        "onViewDetachedFromWindow 感知View状态变化"
                    }
                    handler?.removeCallbacksAndMessages(null)
                }
            })
        }
    
        override fun afterTextChanged(s: Editable?) {
            super.afterTextChanged(s)
            val editText = s.toString().trim()
    
            if (handler?.hasMessages(HANDLER_WHAT) == true) {
                logger {
                    "handler hasMessages"
                }
                handler?.removeMessages(HANDLER_WHAT)
            }
    
            Message().also {
                it.what = HANDLER_WHAT
                it.data = Bundle().apply {
                    putString(BUNDLE_KEY, editText)
                }
            }.let {
                handler?.sendMessageDelayed(it, delayMillis)
            }
        }
    }
    

    注意:因为Handler可能造成内存泄露,所以我使用OnAttachStateChangeListener通过View的状态变化来监听当前界面是否销毁。防止出现内存泄露

    Kotlin协程的StateFlow

    
    /**
     * Created by wangkai on 2021/01/30 10:31
    
     * Desc EditText事件搜索监听,使用协程Coroutine的方式实现
     *
     * 参考自:https://juejin.cn/post/6925304772383735822
     */
    
    @SuppressLint("RestrictedApi")
    open class YqTextChangeCoroutineDebounceListener(val view: View, private val delayMillis: Long = 300L, onTextChanged: ((String?) -> Unit)? = null) : YqTextChangeListener(), LifecycleEventObserver {
    
        //定义一个全局的 StateFlow
        @ExperimentalCoroutinesApi
        private val _etState by lazy { MutableStateFlow("") }
    
        init {
            view.context?.getLifecycleOwner()?.also {
                it.lifecycle.addObserver(this)//注册自己到activity或fragment的LifecycleRegistry中
            }.apply {
                this?.lifecycleScope?.launch {
                _etState
                        .debounce(delayMillis) // 限流,500毫秒
    //                    .filter {
    //                        // 空文本过滤掉
    //                        it.isNotBlank()
    //                    }
                        .collect {
                            // 订阅数据
                            logger {
                                "Thread thread: ${Thread.currentThread()}"
                            }
                            onTextChanged?.invoke(it)
                        }
                }
            }
        }
    
        override fun afterTextChanged(s: Editable?) {
            super.afterTextChanged(s)
            val editText = s.toString().trim()
            // 往流里写数据
            _etState.value = editText
        }
    
        override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
            logger {
                "onStateChanged source: $source; event: $event"
            }
        }
    }
    

    使用

     editText.addTextChangedDebounceListener { str ->
              //do something
     }
    
    editText.addTextChangedCoroutineDebounceListener { str ->
              //do something
     }
    

    相关文章

      网友评论

          本文标题:Android两种方式实现延迟搜索

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