Android 一个比较完善的输入法状态监听 KeyBoardWatcher 实现

3 篇文章 0 订阅
1 篇文章 0 订阅

Android 一个比较完善的软键盘状态监听 KeyBoardWatcher 实现

相信大家都会遇到项目中有EditText控件需要监听输入法软键盘状态的需求


然后你会发现在查找API的时候,并没有这样的监听接口以及靠谱的API

不靠谱的方案:

方案1:

 (context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).isActive

isActive方法是不正确的,看下源码注释 Return true if any view is currently active in the input method. 如果当前view获得焦点则返回true,意思就是说只要edittext获得焦点就返回true,但是你会发现当你关闭输入法的时候edittext还是处于获取焦点状态此方法返回的还是true。

方案2:

return context.window.attributes.softInputMode == WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE

softInputMode是软键盘的Mode,并不是软键盘的状态status,这是API的理解错误。SOFT_INPUT_STATE_VISIBLE的源码注释读一下就明白了。它的值等同于你在xml里面设置设置android:windowSoftInputMode="stateVisible"这样的属性。SoftInputModeFlags类可以看下

如何曲线救国

使用ViewTreeObserver监听视图变化 如果屏幕高度-视图高度>0 则输入法弹出
这个方案仅适用单屏设备。因为我们知道定制的Android设备是有可能双屏的。输入法如果弹在副屏此方案不适用的。

针对此方案写了工具类。

package com.okay.commonbusiness.utils

import android.content.Context
import android.graphics.Rect
import android.os.Build
import android.util.Pair
import android.view.View
import android.view.ViewTreeObserver


/**
 *
 * @author Liang Jx
 *
 * @since 2019/8/5 11:36 AM
 * @version ${VERSION}
 * @desc
 *
 */
class KeyBoardWatcher private constructor() {
    private var globalLayoutListener: GlobalLayoutListener? = null
    private var context: Context? = null
    private var decorView: View? = null

    interface OnKeyboardStateChangeListener {
        /**
         * 监听键盘状态变化监听
         * @param isShow 是否显示
         * @param heightDifference 界面变化的高度差
         */
        fun onKeyboardStateChange(isShow: Boolean, heightDifference: Int)
    }


    /**
     * 监听键盘的状态变化
     * @param context
     * @param decorView
     * @param listener
     * @return
     */
    fun init(context: Context, decorView: View, listener: OnKeyboardStateChangeListener): KeyBoardWatcher {
        this.context = context
        this.decorView = decorView
        this.globalLayoutListener = GlobalLayoutListener(listener)
        addSoftKeyboardChangedListener()
        return this
    }

    /**
     * 释放资源
     */
    fun release() {
        removeSoftKeyboardChangedListener()
        globalLayoutListener = null
    }

    /**
     * 取消软键盘状态变化监听
     */
    private fun removeSoftKeyboardChangedListener() {
        if (globalLayoutListener != null && null != decorView) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                decorView!!.viewTreeObserver.removeOnGlobalLayoutListener(globalLayoutListener)
            } else {
                decorView!!.viewTreeObserver.removeGlobalOnLayoutListener(globalLayoutListener)
            }
        }
    }

    /**
     * 注册软键盘状态变化监听
     */
    private fun addSoftKeyboardChangedListener() {
        if (globalLayoutListener != null && null != decorView) {
            removeSoftKeyboardChangedListener()
            decorView!!.viewTreeObserver.addOnGlobalLayoutListener(globalLayoutListener)
        }
    }

    /**
     * 判断键盘是否显示
     * @param context
     * @param decorView
     * @return
     */

    fun isKeyboardShowing(context: Context?, decorView: View): Pair<Boolean, Int> {
        val outRect = Rect()
        //指当前Window实际的可视区域大小,通过decorView获取到程序显示的区域,包括标题栏,但不包括状态栏。
        decorView.getWindowVisibleDisplayFrame(outRect)
        val displayScreenHeight = DisplayMetricsUtils.getDisplayScreenHeight(context!!)

        //如果屏幕高度和Window可见区域高度差值大于0,则表示软键盘显示中,否则软键盘为隐藏状态。
        val heightDifference = displayScreenHeight - outRect.bottom
        return Pair(heightDifference > 0, heightDifference)
    }

    fun isKeyboardShow(context: Context?, decorView: View):Boolean{
        return isKeyboardShowing(context,decorView).second>0
    }

    inner class GlobalLayoutListener(private val onKeyboardStateChangeListener: OnKeyboardStateChangeListener?) :
        ViewTreeObserver.OnGlobalLayoutListener {
        private var isKeyboardShow = false

        init {
            this.isKeyboardShow = false
        }

        override fun onGlobalLayout() {
            if (null != onKeyboardStateChangeListener && null != decorView) {
                val pair = isKeyboardShowing(context, decorView!!)
                if (pair.first) {
                    onKeyboardStateChangeListener!!.onKeyboardStateChange(isKeyboardShow, pair.second)
                    isKeyboardShow = true
                } else if (isKeyboardShow) {
                    onKeyboardStateChangeListener!!.onKeyboardStateChange(isKeyboardShow, pair.second)
                    isKeyboardShow = false
                }
            }
        }
    }

    companion object {

        fun get(): KeyBoardWatcher {
            return KeyBoardWatcher()
        }
    }
}

注意: 在使用这个方案之后,偶发现它有个难受的问题,我知道如果不remove globalLayoutListener监听,只要你的视图子view有任何的更新,都会导致回调触发,这样你在监听里面做的操作就会频繁刷新。因为项目需求里有在软键盘弹出布局自动scrooltoY的需求,这样会导致手动滚动的时候会自动回弹。为了解决这个问题我在回调里面加了个参数isKeyboardShow,结合isKeyboardShow缓存的状态就可以完美监听软键盘的弹出了。

如何关闭收起软键盘

不做赘述,上代码

    /**
     * 关闭输入法
     *
     * @param context
     * @param editText
     */
    fun softInputHidden(context: Activity) {
        try {
            val softInputManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
            softInputManager.hideSoftInputFromWindow(context.window.peekDecorView().windowToken, InputMethodManager.HIDE_NOT_ALWAYS)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    /**
     * 显示输入法
     *
     * @param context
     */
    fun softInputShow(context: Context) {
        val softInputManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        softInputManager.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS)
    }

这里注意token我是从activity获取的 ,你从view获取也可以,但是就需要多传入view参数

如何控制EditText密文和明文切换
    /**
     * 设置密码 显示和 隐藏
     *
     * @param editeText
     * @param b
     */
    fun changePwdVisiable(editeText: EditText, b: Boolean) {
        if (b) {
            // 显示密码
//            editeText.transformationMethod = HideReturnsTransformationMethod.getInstance()
            editeText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD

        } else {
            // 隐藏密码
//            editeText.transformationMethod = PasswordTransformationMethod.getInstance()
            editeText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
        }
        Selection.setSelection(editeText.text, editeText.text.length)
        editeText.requestFocus()
    }

两种方案,注释的代码是第二种方案,我更喜欢inputType的方案,因为我可以设置text类型。他俩的显示效果有点不太一样。

好啦!结束!经常总结这些工具类,对开发效率有很大的帮助。避免用时百度到一堆坑。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android实现一个用于监听微信通知栏信息的app是可行的。首先需要在AndroidManifest.xml文件中添加相应的权限,如读取通知的权限。 然后可以创建一个后台服务来监听微信的通知栏信息。该服务需要继承自NotificationListenerService类,并重写相应的回调方法,如onNotificationPosted()和onNotificationRemoved()方法。 在onNotificationPosted()方法中,可以获取到微信通知栏的信息,包括通知标题、内容、发送者等。可以通过解析通知栏信息来判断是否为微信的通知,如果是,则可以处理相关逻辑,如将通知内容发送到其他应用或者进行特定的操作。 在onNotificationRemoved()方法中,可以监听微信通知被移除的事件。可以在该方法中进行一些处理,如取消监听或者进行数据统计等操作。 为了保证后台服务的持续运行,可以使用前台服务的方式来启动该服务,并在通知栏显示一个持续运行的通知,提醒用户该服务正在运行。 另外,为了保证通知栏监听的稳定性和兼容性,需要在代码中考虑一些特殊情况,如当微信通知栏的布局发生变化时,需要进行适配并更新相应的解析逻辑。 最后,在设计app界面时,可以添加一些设置选项,允许用户自定义监听的微信通知类型,或者将监听到的通知内容显示出来。 总之,通过Android的通知栏监听机制,结合对微信通知栏信息的解析和处理,就可以实现一个用于监听微信通知栏的app。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值