最近开发中遇到了一些软键盘相关的问题,所以整理下与大家分享:
开启:
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如何打开和关闭软键盘示例:
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(R.id.soft_input_open_btn)
softInpuBtn.isFocusable = true
softInpuBtn.isFocusableInTouchMode = true
softInpuBtn.requestFocus()
softInpuBtn.setOnClickListener {
showSoftInput(it)
}
val softCloseBtn = findViewById(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 ([email protected] != softInputHeight) {
mSoftInputHeightChanged = true
[email protected] = 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)
}
}
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设置:
效果:布局随着软键盘被顶上去。
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以外的空间。
效果:此时ImageView被压缩了,说明布局文件被重新计算大小。
修改布局观看SOFT_INPUT_ADJUST_UNSPECIFIED效果
ImageView里增加isScrollContainer 属性 android:isScrollContainer="true"
可以看到SOFT_INPUT_ADJUST_UNSPECIFIED 模式下产生的效果可能与SOFT_INPUT_ADJUST_PAN相同,也可能与SOFT_INPUT_ADJUST_RESIZE相同。