软键盘问题整理

Posted 且听真言

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了软键盘问题整理相关的知识,希望对你有一定的参考价值。

最近开发中遇到了一些软键盘相关的问题,所以整理下与大家分享:

一、软键盘的开启与关闭

开启:

1、获取InputMethodManager 实例。

2、调用showSoftInput()方法。

fun showSoftInput(view: View?) 
    view?.let 
        val inputMethodManager =
            it.context?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
        inputMethodManager?.showSoftInput(it, 0)
    

/**
 * Synonym for @link #showSoftInput(View, int, ResultReceiver) without
 * a result receiver: explicitly request that the current input method's
 * soft input area be shown to the user, if needed.
 *
 * @param view The currently focused view, which would like to receive
 * soft keyboard input.
 * @param flags Provides additional operating flags.  Currently may be
 * 0 or have the @link #SHOW_IMPLICIT bit set.
 */
public boolean showSoftInput(View view, int flags) 
    // Re-dispatch if there is a context mismatch.
    final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
    if (fallbackImm != null) 
        return fallbackImm.showSoftInput(view, flags);
    

    return showSoftInput(view, flags, null);

showSoftInput需要两个参数,第一个类型是View类型,最好是一个EditText或者其子类

注意:如果传入的View不是一个EditText,那么这个View必须有两个属性:android:focusable="true" 和 android:focusableInTouchMode="true"

第二个参数是flags就是个标志位,flags可以是0、或者是SHOW_IMPLICIT、SHOW_FORCED。

/**
 * Flag for @link #showSoftInput to indicate that this is an implicit
 * request to show the input window, not as the result of a direct request
 * by the user.  The window may not be shown in this case.
 */
public static final int SHOW_IMPLICIT = 0x0001;

/**
 * Flag for @link #showSoftInput to indicate that the user has forced
 * the input method open (such as by long-pressing menu) so it should
 * not be closed until they explicitly do so.
 */
public static final int SHOW_FORCED = 0x0002;

一个非EditText如何打开和关闭软键盘示例:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".TesSoftInputActivity">

    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/soft_input_open_btn"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:gravity="center"
        android:text="打开软键盘"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />


    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/soft_input_close_btn"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:layout_marginTop="20dp"
        android:gravity="center"
        android:text="关闭软键盘"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/soft_input_open_btn" />

    <EditText
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:paddingBottom="20dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
package com.example.myapplication

import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.view.inputmethod.InputMethodManager

class TesSoftInputActivity : AppCompatActivity() 
    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_tes_soft_input)

        val softInpuBtn = findViewById<View>(R.id.soft_input_open_btn)
        softInpuBtn.isFocusable = true
        softInpuBtn.isFocusableInTouchMode = true
        softInpuBtn.requestFocus()
        softInpuBtn.setOnClickListener 
            showSoftInput(it)
        

        val softCloseBtn = findViewById<View>(R.id.soft_input_close_btn)
        softCloseBtn.setOnClickListener 
            hideSoftInput(it)
        
    


    private fun showSoftInput(view: View?) 
        view?.let 
            val inputMethodManager =
                it.context?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
            inputMethodManager?.showSoftInput(it, 0)
        
    


    private fun hideSoftInput(view: View?) 
        view?.let 
            val inputMethodManager =
                it.context?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
            inputMethodManager?.hideSoftInputFromWindow(it.windowToken, 0)
        
    

关闭 

1、获取InputMethodManager 实例。

2、调用hideSoftInputFromWindow()方法。

fun hideSoftInput(view: View?) 
    view?.let 
        val inputMethodManager =
            it.context?.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
        inputMethodManager?.hideSoftInputFromWindow(it.windowToken, 0)
    

二、如何获取软键盘的高度

Activity窗口的构成:当软键盘弹出时,软键盘会覆盖内容区域,剩余的区域就是可见区域。

只需要将屏幕高度-可见区域的高度,就是软键盘的高度。

Rect 类

   public int left;
    public int top;
    public int right;
    public int bottom;

这4个属性描述着这一个“方块”,如下图:

Rect最左侧到屏幕的左侧的距离是 left 
Rect最上面到屏幕上方的距离是 top 
Rect最右侧到屏幕左侧的距离是 right 
Rect最下面到屏幕上方的距离是 bottom

通过 长方形4个点的坐标,可以计算出这个长方形的尺寸

长 = bottom - top 

宽 = right - left

package com.example.myapplication

import android.content.Context
import android.graphics.Rect
import android.util.Log
import android.view.View

/**
 * Created by david on 2022/2/12.
 * @author david
 */
class LiveSoftHelpUtil 
    companion object 
        const val TAG = "LiveSoftHelpUtil"
    

    //记录软键盘的高度
    private var mSoftInputHeight = 0

    //底部NavigationBar的高度
    private var mNavigationBarHeight = 0

    //底部NavigationBar是否展示
    private var mIsNavigationBarShow = false

    //软键盘高度是否发生变化
    private var mSoftInputHeightChanged = false

    //软键盘改变回调
    private var mSoftInputChangedListener: ISoftInputChanged? = null

    //需要移动的EditTextView
    private var mView: View? = null

    //软键盘是否展示
    private var mIsSoftInputShowing = false

    interface ISoftInputChanged 
        //软键盘发生改变
        fun onChanged(isSoftInputShow: Boolean, softInputHeight: Int, viewOffset: Int)
    

    fun attachSoftInput(view: View? = null, listener: ISoftInputChanged? = null) 
        //获取根View
        val rootView = view?.rootView ?: return
        mNavigationBarHeight = getNavigationBarHeight(rootView.context)

        this.mView = view
        this.mSoftInputChangedListener = listener

        rootView.addOnLayoutChangeListener(object : View.OnLayoutChangeListener 
            override fun onLayoutChange(
                v: View?,
                left: Int,
                top: Int,
                right: Int,
                bottom: Int,
                oldLeft: Int,
                oldTop: Int,
                oldRight: Int,
                oldBottom: Int
            ) 
                //获取屏幕高度
                val rootHeight = rootView.height
                Log.v(TAG, "rootHeight:$rootHeight")

                val rect = Rect()
                //获取可见部分的高度(除状态栏和导航栏)
                rootView.getWindowVisibleDisplayFrame(rect)

                if (rootHeight - rect.bottom == mNavigationBarHeight) 
                    //如果可见部分与屏幕底部刚好差值为导航栏的高度,则认为有导航栏
                    mIsNavigationBarShow = true
                 else if (rootHeight - rect.bottom == 0) 
                    //如果可见部分与屏幕底部平齐,说明没有导航栏
                    mIsNavigationBarShow = false
                

                //记录软键盘是否展示
                var isSoftInputShow = false
                //记录软键盘的高度
                var softInputHeight = 0

                //如果有导航栏,需要去除导航栏的高度
                val mutableHeight = if (mIsNavigationBarShow) mNavigationBarHeight else 0
                Log.v(TAG, "mNavigationBarHeight:$mNavigationBarHeight")
                if (rootHeight - mutableHeight > rect.bottom) 
                    //除去导航栏高度后,可见区域 小于 屏幕高度,说明软键盘弹起
                    isSoftInputShow = true
                    //键盘高度
                    softInputHeight = rootHeight - mutableHeight - rect.bottom
                    if (this@LiveSoftHelpUtil.mSoftInputHeight != softInputHeight) 
                        mSoftInputHeightChanged = true
                        this@LiveSoftHelpUtil.mSoftInputHeight = softInputHeight
                     else 
                        mSoftInputHeightChanged = false
                    
                

                //获取EditText坐标
                val location = IntArray(2)
                view.getLocationOnScreen(location)
                if (mIsSoftInputShowing != isSoftInputShow || (isSoftInputShow && mSoftInputHeightChanged)) 
                    listener?.onChanged(
                        isSoftInputShow,
                        softInputHeight,
                        location[1] + view.height - rect.bottom
                    )
                    mIsSoftInputShowing = isSoftInputShow
                
            
        )
    

    /**
     * 获取NavigationBar的高度
     */
    private fun getNavigationBarHeight(context: Context): Int 
        val resources = context.resources
        val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
        return resources.getDimensionPixelSize(resourceId)
    

三、WindowManager.LayoutParams.softInputMode(软键盘的模式)

public static final int SOFT_INPUT_ADJUST_UNSPECIFIED = 0x00;
public static final int SOFT_INPUT_ADJUST_RESIZE = 0x10;
public static final int SOFT_INPUT_ADJUST_PAN = 0x20;
public static final int SOFT_INPUT_ADJUST_NOTHING = 0x30;

1.SOFT_INPUT_ADJUST_PAN 软键盘弹起,布局需要整体移动

设置softInputMode有两种方式:

1.代码设置:

override fun onCreate(savedInstanceState: Bundle?) 
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_test_soft_input_1)

    window.attributes.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN
    ... ...
    

2.xml设置:

<activity
    android:name=".TesSoftInputActivity"
    android:exported="true"
    android:windowSoftInputMode="adjustPan">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

 

效果:布局随着软键盘被顶上去。

2.SOFT_INPUT_ADJUST_UNSPECIFIED 不指定调整方式,系统自行决定使用哪种调整方式

window.attributes.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED
android:windowSoftInputMode="adjustUnspecified"

 

 

效果:布局随着软键盘被顶上去。

3.SOFT_INPUT_ADJUST_RESIZE

window.attributes.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
android:windowSoftInputMode="adjustResize"

 

 

效果:软键盘弹起,布局没有变动。

4.SOFT_INPUT_ADJUST_NOTHING

window.attributes.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING
android:windowSoftInputMode="adjustNothing"

 

效果:软键盘弹起,布局没有变动。

修改布局观看SOFT_INPUT_ADJUST_RESIZE效果

window.attributes.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE

修改方式:将ImageView填充满除EdItText以外的空间。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/iv"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        android:background="@android:color/holo_blue_light"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@+id/et"/>

    <androidx.appcompat.widget.AppCompatEditText
        android:id="@+id/et"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="200dp"
        android:autofillHints="@string/hint"
        android:focusable="true"
        android:hint="@string/hint"
        android:inputType="text"
        android:textColor="@color/black"
        android:textColorHint="@color/black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintBottom_toBottomOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

效果:此时ImageView被压缩了,说明布局文件被重新计算大小。

修改布局观看SOFT_INPUT_ADJUST_UNSPECIFIED效果

ImageView里增加isScrollContainer 属性 android:isScrollContainer="true"

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/iv"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        android:isScrollContainer="true"
        android:background="@android:color/holo_blue_light"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@+id/et"/>

    <androidx.appcompat.widget.AppCompatEditText
        android:id="@+id/et"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="200dp"
        android:autofillHints="@string/hint"
        android:focusable="true"
        android:hint="@string/hint"
        android:inputType="text"
        android:textColor="@color/black"
        android:textColorHint="@color/black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintBottom_toBottomOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

可以看到SOFT_INPUT_ADJUST_UNSPECIFIED 模式下产生的效果可能与SOFT_INPUT_ADJUST_PAN相同,也可能与SOFT_INPUT_ADJUST_RESIZE相同。

以上是关于软键盘问题整理的主要内容,如果未能解决你的问题,请参考以下文章

iOS开发隐藏键盘方法总结

移动端H5软键盘的问题

Android Popwindow和软键盘遮挡问题

ios软键盘弹出再收起,页面布局错乱问题解决

怎样设置软键盘

移动端软键盘弹出影响页面布局(iOS软键盘取消后,页面没有恢复),移动端软键盘监听(弹出,收起),及影响定位布局的问题