3
软键盘高度获取
对于上面的问题1,既然想要EditText单独顶上去,那么就需要知道当前键盘弹出的高度,再设置EditText坐标即可。
问题的关键转变为如何获取键盘的高度。
Activity窗口的构成
通常来说,手机由状态栏、内容区域、导航栏组成。
一般情况下(除去导航栏隐藏,状态栏沉浸)对于我们来说,写的布局文件都会展示在内容区域,这部分是"能看到的"。
当键盘弹出的时候,会遮盖部分内容区域:
因此,需要将被遮住的部分往上移动,移动多少呢?
通过调用方法:
#View.java
publicvoidgetWindowVisibleDisplayFrame( Rect outRect){
...
}
可见区域的位置记录在outRect里,而整个屏幕高度是已知的,因此就可以计算出被遮挡的区域需要顶上去的偏移量。
一个通用的计算方式
根据上面的分析,将计算方法封装一下:
publicclassSoftInputUtil{
privateintsoftInputHeight = 0;
privatebooleansoftInputHeightChanged = false;
privatebooleanisNavigationBarShow = false;
privateintnavigationHeight = 0;
privateView anyView;
privateISoftInputChanged listener;
privatebooleanisSoftInputShowing = false;
publicinterfaceISoftInputChanged{
voidonChanged( booleanisSoftInputShow, intsoftInputHeight, intviewOffset);
}
publicvoidattachSoftInput( finalView anyView, finalISoftInputChanged listener){
if(anyView == null|| listener == null)
return;
//根View
finalView rootView = anyView.getRootView;
if(rootView == null)
return;
navigationHeight = getNavigationBarHeight(anyView.getContext);
//anyView为需要调整高度的View,理论上来说可以是任意的View
this.anyView = anyView;
this.listener = listener;
rootView.addOnLayoutChangeListener( newView.OnLayoutChangeListener {
@Override
publicvoidonLayoutChange(View v, intleft, inttop, intright, intbottom, intoldLeft, intoldTop, intoldRight, intoldBottom){
//对于Activity来说,该高度即为屏幕高度
introotHeight = rootView.getHeight;
Rect rect = newRect;
//获取当前可见部分,默认可见部分是除了状态栏和导航栏剩下的部分
rootView.getWindowVisibleDisplayFrame(rect);
if(rootHeight - rect.bottom == navigationHeight) {
//如果可见部分底部与屏幕底部刚好相差导航栏的高度,则认为有导航栏
isNavigationBarShow = true;
} elseif(rootHeight - rect.bottom == 0) {
//如果可见部分底部与屏幕底部平齐,说明没有导航栏
isNavigationBarShow = false;
}
//cal softInput height
booleanisSoftInputShow = false;
intsoftInputHeight = 0;
//如果有导航栏,则要去除导航栏的高度
intmutableHeight = isNavigationBarShow == true? navigationHeight : 0;
if(rootHeight - mutableHeight > rect.bottom) {
//除去导航栏高度后,可见区域仍然小于屏幕高度,则说明键盘弹起了
isSoftInputShow = true;
//键盘高度
softInputHeight = rootHeight - mutableHeight - rect.bottom;
if(SoftInputUtils. this.softInputHeight != softInputHeight) {
softInputHeightChanged = true;
SoftInputUtils. this.softInputHeight = softInputHeight;
} else{
softInputHeightChanged = false;
}
}
//获取目标View的位置坐标
int[] location = newint[ 2];
anyView.getLocationOnScreen(location);
//条件1减少不必要的回调,只关心前后发生变化的
//条件2针对软键盘切换手写、拼音键等键盘高度发生变化
if(isSoftInputShowing != isSoftInputShow || (isSoftInputShow && softInputHeightChanged)) {
if(listener != null) {
//第三个参数为该View需要调整的偏移量
//此处的坐标都是相对屏幕左上角(0,0)为基准的
listener.onChanged(isSoftInputShow, softInputHeight, location[ 1] + anyView.getHeight - rect.bottom);
}
isSoftInputShowing = isSoftInputShow;
}
}
});
}
//***************STATIC METHOD******************
publicstaticintgetNavigationBarHeight(Context context){
if(context == null)
return0;
Resources resources = context.getResources;
intresourceId = resources.getIdentifier( "navigation_bar_height", "dimen", "android");
intheight = resources.getDimensionPixelSize(resourceId);
returnheight;
}
publicstaticvoidshowSoftInput(View view){
if(view == null)
return;
InputMethodManager inputMethodManager = (InputMethodManager)view.getContext.getSystemService(Context.INPUT_METHOD_SERVICE);
if(inputMethodManager != null) {
inputMethodManager.showSoftInput(view, 0);
}
}
publicstaticvoidhideSoftInput(View view){
if(view == null)
return;
InputMethodManager inputMethodManager = (InputMethodManager)view.getContext.getSystemService(Context.INPUT_METHOD_SERVICE);
if(inputMethodManager != null) {
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken, 0);
}
}
}
使用方式如下:
在 Activity里加如下代码:
privatevoidattachView{
//editText2为需要调整的View
editText2 = findViewById(R.id.et2);
SoftInputUtil softInputUtil = newSoftInputUtil;
softInputUtil.attachSoftInput(editText2, newSoftInputUtil.ISoftInputChanged {
@Override
publicvoidonChanged( booleanisSoftInputShow, intsoftInputHeight, intviewOffset){
if(isSoftInputShow) {
editText2.setTranslationY(et2.getTranslationY - viewOffset);
} else{
editText2.setTranslationY( 0);
}
}
});
}
并且将windowSoftInputMode设置为SOFT_INPUT_ADJUST_RESIZE。
android:windowSoftInputMode= "adjustResize|stateAlwaysHidden"
stateAlwaysHidden为默认不显示键盘。
再来看Activity的布局文件:
< LinearLayoutxmlns:android= "http://schemas.android.com/apk/res/android"
xmlns:tools= "http://schemas.android.com/tools"
android:id= "@+id/myviewgroup"
android:layout_width= "match_parent"
android:layout_height= "match_parent"
android:orientation= "vertical"
android:layout_gravity= "center_vertical"
tools:context= ".MainActivity">
< ImageView
android:id= "@+id/iv"
android:src= "@drawable/test"
android:background= "@color/colorGreen"
android:layout_marginTop= "20dp"
android:layout_marginBottom= "20dp"
android:layout_width= "match_parent"
android:layout_height= "300dp">
ImageView>
< EditText
android:hint= "输入框2"
android:id= "@+id/et2"
android:layout_marginTop= "100dp"
android:background= "@drawable/bg"
android:layout_gravity= "bottom"
android:layout_marginHorizontal= "10dp"
android:layout_width= "match_parent"
android:layout_height= "40dp">
EditText>
LinearLayout>
最终效果如下:
可以看出,EditText被顶上去了,其它布局没有变化。在动态切换导航栏是否展示之间EeditText也能正常显示。
这就回答了上面的问题1:当键盘弹出时,只想EditText顶上去,ImageView保持不动。
当然对于问题1的还有其它更简单的解决方式,在下一篇会分析。
以上是关于软键盘弹出、关闭、是否展示、软键盘高度、软键盘模式等效果的解析。
你可能还有如下疑惑:
SOFT_INPUT_ADJUST_PAN为什么能够将布局顶上去?
SOFT_INPUT_ADJUST_RESIZE为什么能够重新设置布局区域?
SOFT_INPUT_ADJUST_UNSPECIFIED内部逻辑如何判断?
键盘弹起为什么会执行 onLayout?
...
由于篇幅所限,这些问题将在后续推送:Android 软键盘一招搞定(原理篇)分析。
本文基于 Android 10.0。