前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >DialogFragment自动弹出软键盘,消失时关闭软键盘

DialogFragment自动弹出软键盘,消失时关闭软键盘

作者头像
玖柒的小窝
修改2021-11-05 14:55:16
4.9K2
修改2021-11-05 14:55:16
举报
文章被收录于专栏:各类技术文章~各类技术文章~

弹出和关闭软键盘

  • 弹出软键盘
代码语言:javascript
复制
    private val imm: InputMethodManager? by lazy { activity?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager? }

    private fun showSoftInput() {
        imm?.let {
            binding.apply {
                etChat.requestFocus()
                it.showSoftInput(etChat, InputMethodManager.SHOW_FORCED)
            }
        }
    }
复制代码
  • 关闭软键盘
代码语言:javascript
复制
    private fun hideSoftInput() {
        imm?.hideSoftInputFromWindow(binding.etChat.windowToken, 0)
    }
复制代码

在DialogFragment显示时弹出软键盘

在DialogFragment显示时有两种方式弹出软键盘:

1、在onViewCreated中发送一个延时任务

代码语言:javascript
复制
   etChat.postDelayed({ showSoftInput() }, 200)
复制代码

注意:如果直接调用显示键盘不会起作用,因为这个时候view没有显示出来

2、设置dialog的style属性 android:windowSoftInputMode

代码语言:javascript
复制
    <style name="live_editTextDialogStyle" parent="@android:style/Theme.Dialog">
        <!-- 背景透明 -->
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowContentOverlay">@null</item>
        <!-- 浮于Activity之上 -->
        <item name="android:windowIsFloating">true</item>
        <!-- 边框 -->
        <item name="android:windowFrame">@null</item>
        <!-- Dialog以外的区域模糊效果 -->
        <item name="android:backgroundDimEnabled">false</item>
        <!-- 无标题 -->
        <item name="android:windowNoTitle">true</item>
        <!-- 半透明 -->
        <item name="android:windowIsTranslucent">true</item>
        <!-- 显示软键盘 -->
        <item name="android:windowSoftInputMode">stateAlwaysVisible</item>
    </style>
复制代码

在DialogFragment消失时关闭软键盘

dialog关闭分为几种情况,处理方式不一样:

1.用户手动调用DialogFragment.dismiss()

这时可以重写dismiss方法,在调用之前关闭软键盘。

代码语言:javascript
复制
    override fun dismiss() {
        hideSoftInput()
        super.dismiss()
    }
复制代码

2.用户点击空白处关闭dialog

DialogFragment本身没有监听关闭之前的方法,只有两个相关方法onCancel(dialog: DialogInterface)和onDismiss(dialog: DialogInterface)

重写onCancel(dialog: DialogInterface)

代码语言:javascript
复制
    override fun onCancel(dialog: DialogInterface) {
        hideSoftInput()
        super.onCancel(dialog)
    }
复制代码

当这样处理时发现软键盘没有关闭,可以看下流程: 看看InputMethodManager的hideSoftInputFromWindow方法

代码语言:javascript
复制
    public boolean hideSoftInputFromWindow(IBinder windowToken, int flags,
            ResultReceiver resultReceiver) {
        checkFocus();
        synchronized (mH) {
            if (mServedView == null || mServedView.getWindowToken() != windowToken) {
                return false;
            }

            try {
                return mService.hideSoftInput(mClient, flags, resultReceiver);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
    }
复制代码

可以看到关闭软键盘的代码为mService.hideSoftInput 断点发现当在onCancel中关闭软键盘时 mServedView为null,所以走不到关闭代码,看一下mServedView在哪赋值为null的

代码语言:javascript
复制
void finishInputLocked() {
        mNextServedView = null;
        if (mServedView != null) {
            if (DEBUG) Log.v(TAG, "FINISH INPUT: mServedView=" + dumpViewInfo(mServedView));
            if (mCurrentTextBoxAttribute != null) {
                try {
                    mService.finishInput(mClient);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            mServedView = null;
            mCompletions = null;
            mServedConnecting = false;
            clearConnectionLocked();
        }
    }
复制代码

finishInputLocked调用有两处地方

代码语言:javascript
复制
	private boolean checkFocusNoStartInput(boolean forceNewFocus) {
		// 已省略其余代码
        if (mNextServedView == null) {
                finishInputLocked();
                // 此方法会调用 mService.hideSoftInput,所以可以排除
                closeCurrentInput();
                return false;
            }
         return true;
    }

    public void windowDismissed(IBinder appWindowToken) {
        checkFocus();
        synchronized (mH) {
            if (mServedView != null &&
                    mServedView.getWindowToken() == appWindowToken) {
                finishInputLocked();
            }
        }
    }
复制代码

所以可以判断当回调到onCancel的时候,windowDismissed方法已经调用,所以无法关闭软键盘。此方式排除,再来看看onDismiss方法

重写onDismiss(dialog: DialogInterface)

代码语言:javascript
复制
    override fun onDismiss(dialog: DialogInterface) {
        hideSoftInput()
        super.onDismiss(dialog)
    }
复制代码

在onDismiss调用时发现 hideSoftInputFromWindow()中的 mServedView不为null,但是 windowToken == null,看一下这个参数的获取

代码语言:javascript
复制
imm?.hideSoftInputFromWindow(binding.etChat.windowToken, 0)

// View.java ->
    public IBinder getWindowToken() {
        return mAttachInfo != null ? mAttachInfo.mWindowToken : null;
    }
复制代码

mAttachInfo会在 dispatchDetachedFromWindow()中置为null。因为我们传入的etchat所在窗口已经关闭,所以获取的 windowToken为null。

再来看 mServedView不为null的值,发现是我 DialogFragment 依附的 activity的布局控件,可以理解为当前获取焦点的控件,因此可以传入activity中当前焦点所在的view试试,代码改为:

代码语言:javascript
复制
    override fun onDismiss(dialog: DialogInterface) {
        val view = activity?.window?.currentFocus
        view?.let { imm?.hideSoftInputFromWindow(it.windowToken, 0) }
        super.onDismiss(dialog)
    }
复制代码

运行后软键盘正常关闭,OK,问题解决。

本以为问题已解决,但是在操作的时候发现会有偶发的关闭失效,发现又是 mServedView == null,原因未知,没办法,这种方式不够保险。

自定义Dialog在dismiss之前通知 DialogFragment 关闭软键盘

转变思路,既然在 DialogFragment 中无法提前监听dialog关闭,那就自定义Dialog重写dismiss方法,在Dialog关闭之前告知 DialogFragment 关闭软键盘

代码语言:javascript
复制
    class EditDialog(context: Context?, theme: Int) : Dialog(context, theme) {
        override fun dismiss() {
            onDismissListener?.invoke()
            super.dismiss()
        }

        var onDismissListener: (() -> Unit)? = null
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val dialog = EditDialog(context, R.style.live_editTextDialogStyle)
        dialog.onDismissListener = { hideSoftInput() }
        return dialog
    }
复制代码

在DialogFragment的onCreateDialog中创建自定义的Dialog,设置关闭回调。

最后,还有一种方式就是使用全屏dialog,在原先空白区域加一个透明的View,设置view的点击事件去关闭软键盘和弹窗,这样就避免了点击空白处关闭的问题。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 弹出和关闭软键盘
  • 在DialogFragment显示时弹出软键盘
  • 在DialogFragment消失时关闭软键盘
    • 1.用户手动调用DialogFragment.dismiss()
      • 2.用户点击空白处关闭dialog
        • 重写onDismiss(dialog: DialogInterface)
        • 自定义Dialog在dismiss之前通知 DialogFragment 关闭软键盘
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档