一、自动弹出键盘或是自动不弹出
在进入界面时,有时会需要自动弹出键盘,实现方式如下:
- 在清单文件中设置android:windowSoftInputMode属性 ``` 在 AndroidManifest.xml文件中的activity节点下添加: android:windowSoftInputMode=”stateHidden|adjustPan”
stateVisibile:显示键盘 stateHidden:是隐藏软键盘的
adjustPan:是保证控件不会因为输入法的弹出而发生形变的。 adjustResize: 会因为弹出键盘调整布局
- 2. 代码中调用 setSoftInputMode()方法进行设置
OnCreate方法里面加下面这句代码 ,很管用,而且再点EditBox也能让输入法正常弹出。。 getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
<a name="7Qbeh"></a>
## 二、键盘遮挡布局处理
<a name="MJCV3"></a>
#### 2.1 对于非滚动布局
- 如果设置了adjustResize,键盘会顶起整个界面
没有键盘时的界面:<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1624725/1604849402465-13bc035c-5530-4916-a893-9693da935654.png#align=left&display=inline&height=493&margin=%5Bobject%20Object%5D&name=image.png&originHeight=986&originWidth=628&size=52663&status=done&style=none&width=314)<br />有了键盘后的界面:<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1624725/1604849424905-97c166e2-ed53-49d3-8c65-13b1d9d8e32b.png#align=left&display=inline&height=510&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1020&originWidth=614&size=72580&status=done&style=none&width=307)<br />处理方法:<br />在没有滚动布局时,尽量设置不改动界面布局,在清单文件中设置adjustPan,保证布局完整性。
<a name="z80OV"></a>
### 2.2 在滚动布局中时
- 1. 无论设置了 adjustPan 或是 adjustResize,都可以做到顶起布局,键盘不会挡到输入的文字。但是效果却不一样,下面是 adjustPan的效果:
![image.png](https://cdn.nlark.com/yuque/0/2020/png/1624725/1605452047339-b291c87e-c3e5-480e-9c2b-ca5504fae52e.png#align=left&display=inline&height=521&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1042&originWidth=616&size=62985&status=done&style=none&width=308)<br />会将整个布局顶起。
- 2. adjustResize 效果,不会顶起标题。
![image.png](https://cdn.nlark.com/yuque/0/2020/png/1624725/1605452683385-17a26bed-6e18-4b66-819c-8ccb30e01068.png#align=left&display=inline&height=523&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1046&originWidth=616&size=66100&status=done&style=none&width=308)
<a name="Lcgng"></a>
## 三、封装工具的使用
showSoftInput : 显示软键盘 hideSoftInput : 隐藏软键盘 toggleSoftInput : 切换键盘显示与否状态 isSoftInputVisible : 判断软键盘是否可见 registerSoftInputChangedListener : 注册软键盘改变监听器 unregisterSoftInputChangedListener: 注销软键盘改变监听器 fixAndroidBug5497 : 修复安卓 5497 BUG fixSoftInputLeaks : 修复软键盘内存泄漏 clickBlankArea2HideSoftInput : 点击屏幕空白区域隐藏软键盘 setSoftInputAdjustNothing : 软键盘以覆盖当前界面的形式出现 setSoftInputAdjustResize : 软键盘以顶起当前界面的形式出现, 注意这种方式会使得当前布局的高度发生变化,触发当前布局onSizeChanged方法回调,这里前后高度差就是软键盘的高度了 setSoftInputAdjustPan : 软键盘以上推当前界面的形式出现, 注意这种方式不会改变布局的高度 onDisableBackKeyDown : 禁用物理返回键 dispatchTouchEvent : 点击屏幕空白区域隐藏软键盘
KeyboardUtils:
package com.kiwilss.lutils.res
import android.R import android.app.Activity import android.app.Dialog import android.content.Context import android.graphics.Rect import android.os. import android.util.Log import android.view. import android.view.ViewTreeObserver.OnGlobalLayoutListener import android.view.inputmethod.InputMethodManager import android.widget.EditText import android.widget.FrameLayout import com.kiwilss.lutils.LUtilsConfig
/* @FileName: KeyboardUtils *@author : Lss kiwilss
- @e-mail : kiwilss@163.com
- @time : 2020/11/15
@desc : {DESCRIPTION} */ object KeyboardUtils { private var millis: Long = 0 private var sDecorViewDelta = 0 private const val TAG_ON_GLOBAL_LAYOUT_LISTENER = -8
//显示软键盘相关 /**
Show the soft input. */ fun showSoftInput() { val imm = LUtilsConfig.getContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager ?: return imm.toggleSoftInput(
InputMethodManager.SHOW_FORCED,
InputMethodManager.HIDE_IMPLICIT_ONLY
) }
/**
Show the soft input. */ fun showSoftInput(activity: Activity) { if (!isSoftInputVisible(activity)) {
toggleSoftInput()
} }
/**
- Show the soft input. *
@param view The view. */ fun showSoftInput(view: View) { showSoftInput(view, 0) }
/**
- Show the soft input. *
- @param view The view.
- @param flags Provides additional operating flags. Currently may be
- 0 or have the [InputMethodManager.SHOW_IMPLICIT] bit set.
*/
fun showSoftInput(view: View, flags: Int) {
val imm =
view.isFocusable = true view.isFocusableInTouchMode = true view.requestFocus() imm.showSoftInput(view, flags, object : ResultReceiver(Handler()) {LUtilsConfig.getContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager ?: return
}) imm.toggleSoftInput(override fun onReceiveResult(resultCode: Int, resultData: Bundle) {
if (resultCode == InputMethodManager.RESULT_UNCHANGED_HIDDEN
|| resultCode == InputMethodManager.RESULT_HIDDEN
) {
toggleSoftInput()
}
}
) }InputMethodManager.SHOW_FORCED,
InputMethodManager.HIDE_IMPLICIT_ONLY
/**
* Toggle the soft input display or not.即使已经显示了键盘,调用以后会先隐藏键盘再显示
*/
fun toggleSoftInput() {
val imm =
LUtilsConfig.getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
?: return
imm.toggleSoftInput(0, 0)
}
/**
* Hide the soft input.
*
* @param activity The activity.
*/
fun hideSoftInputByToggle(activity: Activity?) {
val nowMillis = SystemClock.elapsedRealtime()
val delta = nowMillis - millis
if (Math.abs(delta) > 500 && isSoftInputVisible(activity!!)) {
toggleSoftInput()
}
millis = nowMillis
}
/**
* Hide the soft input.
*
* @param activity The activity.
*/
fun hideSoftInput(activity: Activity) {
hideSoftInput(activity.window)
}
/**
* Hide the soft input.
*
* @param window The window.
*/
fun hideSoftInput(window: Window) {
var view = window.currentFocus
if (view == null) {
val decorView = window.decorView
val focusView =
decorView.findViewWithTag<View>("keyboardTagView")
if (focusView == null) {
view = EditText(window.context)
view.setTag("keyboardTagView")
(decorView as ViewGroup).addView(view, 0, 0)
} else {
view = focusView
}
view.requestFocus()
}
hideSoftInput(view)
}
/**
* Hide the soft input.
*
* @param view The view.
*/
fun hideSoftInput(view: View?) {
if (view == null) return
val imm =
LUtilsConfig.getContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
?: return
imm.hideSoftInputFromWindow(view.windowToken, 0)
}
/**
* Return whether soft input is visible.
*
* @param activity The activity.
* @return `true`: yes<br></br>`false`: no
*/
fun isSoftInputVisible(activity: Activity): Boolean {
return getDecorViewInvisibleHeight(activity.window) > 0
}
private fun getDecorViewInvisibleHeight(window: Window): Int {
val decorView = window.decorView
val outRect = Rect()
decorView.getWindowVisibleDisplayFrame(outRect)
val delta = Math.abs(decorView.bottom - outRect.bottom)
if (delta <= UtilsBridge.getNavBarHeight() + UtilsBridge.getStatusBarHeight()) {
sDecorViewDelta = delta
return 0
}
return delta - sDecorViewDelta
}
/**
* Register soft input changed listener.
*
* @param activity The activity.
* @param listener The soft input changed listener.
*/
fun registerSoftInputChangedListener(
activity: Activity,
listener: OnSoftInputChangedListener
) {
registerSoftInputChangedListener(activity.window, listener)
}
/**
* Register soft input changed listener.
*
* @param window The window.
* @param listener The soft input changed listener.
*/
fun registerSoftInputChangedListener(
window: Window,
listener: OnSoftInputChangedListener
) {
val flags = window.attributes.flags
if (flags and WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS != 0) {
window.clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)
}
val contentView = window.findViewById<FrameLayout>(R.id.content)
val decorViewInvisibleHeightPre =
intArrayOf(getDecorViewInvisibleHeight(window))
val onGlobalLayoutListener = OnGlobalLayoutListener {
val height = getDecorViewInvisibleHeight(window)
if (decorViewInvisibleHeightPre[0] != height) {
listener.onSoftInputChanged(height)
decorViewInvisibleHeightPre[0] = height
}
}
contentView.viewTreeObserver.addOnGlobalLayoutListener(onGlobalLayoutListener)
contentView.setTag(TAG_ON_GLOBAL_LAYOUT_LISTENER, onGlobalLayoutListener)
}
/**
* Unregister soft input changed listener.
*
* @param window The window.
*/
fun unregisterSoftInputChangedListener(window: Window) {
val contentView =
window.findViewById<View>(R.id.content) ?: return
val tag = contentView.getTag(TAG_ON_GLOBAL_LAYOUT_LISTENER)
if (tag is OnGlobalLayoutListener) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
contentView.viewTreeObserver
.removeOnGlobalLayoutListener(tag)
}
}
}
/**
* Fix the bug of 5497 in Android.
*
* Don't set adjustResize
*
* @param activity The activity.
*/
fun fixAndroidBug5497(activity: Activity) {
fixAndroidBug5497(activity.window)
}
/**
* Fix the bug of 5497 in Android.
*
* It will clean the adjustResize
*
* @param window The window.
*/
fun fixAndroidBug5497(window: Window) {
val softInputMode = window.attributes.softInputMode
window.setSoftInputMode(softInputMode and WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE.inv())
val contentView = window.findViewById<FrameLayout>(R.id.content)
val contentViewChild = contentView.getChildAt(0)
val paddingBottom = contentViewChild.paddingBottom
val contentViewInvisibleHeightPre5497 =
intArrayOf(getContentViewInvisibleHeight(window))
contentView.viewTreeObserver
.addOnGlobalLayoutListener {
val height = getContentViewInvisibleHeight(window)
if (contentViewInvisibleHeightPre5497[0] != height) {
contentViewChild.setPadding(
contentViewChild.paddingLeft,
contentViewChild.paddingTop,
contentViewChild.paddingRight,
paddingBottom + getDecorViewInvisibleHeight(window)
)
contentViewInvisibleHeightPre5497[0] = height
}
}
}
private fun getContentViewInvisibleHeight(window: Window): Int {
val contentView =
window.findViewById<View>(R.id.content) ?: return 0
val outRect = Rect()
contentView.getWindowVisibleDisplayFrame(outRect)
Log.d(
"KeyboardUtils", "getContentViewInvisibleHeight: "
+ (contentView.bottom - outRect.bottom)
)
val delta = Math.abs(contentView.bottom - outRect.bottom)
return if (delta <= UtilsBridge.getStatusBarHeight() + UtilsBridge.getNavBarHeight()) {
0
} else delta
}
/**
* Fix the leaks of soft input.
*
* @param activity The activity.
*/
fun fixSoftInputLeaks(activity: Activity) {
fixSoftInputLeaks(activity.window)
}
/**
* Fix the leaks of soft input.
*
* @param window The window.
*/
fun fixSoftInputLeaks(window: Window) {
val imm =
LUtilsConfig.getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
?: return
val leakViews =
arrayOf("mLastSrvView", "mCurRootView", "mServedView", "mNextServedView")
for (leakView in leakViews) {
try {
val leakViewField =
InputMethodManager::class.java.getDeclaredField(
leakView
)
if (!leakViewField.isAccessible) {
leakViewField.isAccessible = true
}
val obj = leakViewField[imm] as? View ?: continue
if (obj.rootView === window.decorView.rootView) {
leakViewField[imm] = null
}
} catch (ignore: Throwable) { /**/
}
}
}
// interface
///////////////////////////////////////////////////////////////////////////
interface OnSoftInputChangedListener {
fun onSoftInputChanged(height: Int)
}
/**
* 软键盘以覆盖当前界面的形式出现
*
* @param activity
*/
fun setSoftInputAdjustNothing(activity: Activity) {
activity.window.setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING
or WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
)
}
/**
* 软键盘以顶起当前界面的形式出现, 注意这种方式会使得当前布局的高度发生变化,触发当前布局onSizeChanged方法回调,这里前后高度差就是软键盘的高度了
*
* @param activity
*/
fun setSoftInputAdjustResize(activity: Activity) {
activity.window.setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
or WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
)
}
/**
* 软键盘以上推当前界面的形式出现, 注意这种方式不会改变布局的高度
*
* @param activity
*/
fun setSoftInputAdjustPan(activity: Activity) {
activity.window.setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN
or WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
)
}
/**
* 禁用物理返回键
*
*
* 使用方法:
*
* 需重写 onKeyDown
*
* @param keyCode
* @return
* @Override public boolean onKeyDown(int keyCode, KeyEvent event) {
* return KeyboardUtils.onDisableBackKeyDown(keyCode) && super.onKeyDown(keyCode, event) ;
* }
*/
fun onDisableBackKeyDown(keyCode: Int): Boolean {
when (keyCode) {
KeyEvent.KEYCODE_BACK -> return false
KeyEvent.KEYCODE_HOME -> return false
else -> {
}
}
return true
}
/**
* 点击屏幕空白区域隐藏软键盘
*
* 根据 EditText 所在坐标和用户点击的坐标相对比,来判断是否隐藏键盘
*
* 需重写 dispatchTouchEvent
*
* @param ev
* @param activity 窗口
* @return
*/
fun dispatchTouchEvent(ev: MotionEvent?, activity: Activity) {
if (ev == null) return
if (ev.action == MotionEvent.ACTION_DOWN) {
val v = activity.currentFocus
if (isShouldHideKeyboard(v, ev)) {
hideSoftInput(v)
}
}
}
/**
* 点击屏幕空白区域隐藏软键盘
*
* 根据 EditText 所在坐标和用户点击的坐标相对比,来判断是否隐藏键盘
*
* 需重写 dispatchTouchEvent
*
* @param ev
* @param dialog 窗口
* @return
*/
fun dispatchTouchEvent(ev: MotionEvent?, dialog: Dialog) {
if (ev == null) return
if (ev.action == MotionEvent.ACTION_DOWN) {
val v = dialog.currentFocus
if (isShouldHideKeyboard(v, ev)) {
hideSoftInput(v)
}
}
}
/**
* 点击屏幕空白区域隐藏软键盘
*
* 根据 EditText 所在坐标和用户点击的坐标相对比,来判断是否隐藏键盘
*
* 需重写 dispatchTouchEvent
*
* @param ev
* @param focusView 聚焦的view
* @return
*/
fun dispatchTouchEvent(ev: MotionEvent?, focusView: View?) {
if (ev == null) return
if (ev.action == MotionEvent.ACTION_DOWN) {
if (isShouldHideKeyboard(focusView, ev)) {
hideSoftInput(focusView)
}
}
}
/**
* 点击屏幕空白区域隐藏软键盘
*
* 根据 EditText 所在坐标和用户点击的坐标相对比,来判断是否隐藏键盘
*
* 需重写 dispatchTouchEvent
*
* @param ev
* @param window 窗口
* @return
*/
fun dispatchTouchEvent(ev: MotionEvent?, window: Window) {
if (ev == null) return
if (ev.action == MotionEvent.ACTION_DOWN) {
val v = window.currentFocus
if (isShouldHideKeyboard(v, ev)) {
hideSoftInput(v)
}
}
}
/**
* 根据 EditText 所在坐标和用户点击的坐标相对比,来判断是否隐藏键盘
*
* @param v
* @param event
* @return
*/
private fun isShouldHideKeyboard(v: View?, event: MotionEvent): Boolean {
if (v != null && v is EditText) {
val l = intArrayOf(0, 0)
v.getLocationInWindow(l)
val left = l[0]
val top = l[1]
val bottom = top + v.getHeight()
val right = left + v.getWidth()
return !(event.x > left && event.x < right && event.y > top && event.y < bottom)
}
return false
}
}
UtilsBridge:
object UtilsBridge { val statusBarHeight: Int get() { val resources = getContext().resources val resourceId = resources.getIdentifier(“status_bar_height”, “dimen”, “android”) return resources.getDimensionPixelSize(resourceId) }
val navBarHeight: Int
get() {
val res =
getContext().resources
val resourceId = res.getIdentifier("navigation_bar_height", "dimen", "android")
return if (resourceId != 0) {
res.getDimensionPixelSize(resourceId)
} else {
0
}
}
} ```