【Android】键盘挡住输入框
2019-08-06 本文已影响0人
Sraindy
问题描述:
根据(android全屏/沉浸式状态栏下,各种键盘挡住输入框解决办法)[https://blog.csdn.net/smileiam/article/details/69055963]中的详解方法五
思路:
输入法没显示时,手机界面分为两块即状态栏和内容区域。
image.png
输入法显示时,手机界面分为三块即状态栏,内容区域,输入法显示区域
image.png
原理:
1、获取Activity的setContentView里的View
所有的Activity都是DecorView,它就是一个FrameLayout控件,该控件id是系统写死叫R.id.content,就是我们setContentView时,把相应的View放在此FrameLayout控件里。
FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content)
所以content.getChildAt(0)获取到的mChildOfContent,也就是我们用setContentView放进去的View。
2、给Activity的xml布局View设置一个监听视图树布局发生改变
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
//4、软键盘弹起会使GlobalLayout发生变化
public void onGlobalLayout() {
}
});
View.getViewTreeObserver()可以获取一个ViewTreeObserver对象——它是一个观察者,用以监听当前View树所发生的变化。
这里所注册的addOnGlobalLayoutListener,就是会在当前的View树的全局布局(GlobalLayout)发生变化、或者其中的View可视状态有变化时,进行通知回调。『软键盘弹出/隐 』都能监听到。
3、获取内容区域(即当前窗口可视区域)
private int computeUsableHeight() {
Rect r = new Rect();
/**
* 一个窗口中通常都会有多个View,getWindowVisibleDisplayFrame()方法的返回结果和该窗口中选取的View并没有关系。
* 在某个时刻,使用当前窗口中的任意View执行getWindowVisibleDisplayFrame()返回的结果都是一样的。
*/
//获取当前窗口可视区域大小的
mChildOfContent.getWindowVisibleDisplayFrame(r);
Log.i("zss", "----r.bottom:" + r.bottom + " ; r.top:" + r.top);
// 全屏模式下:直接返回r.bottom,r.top其实是状态栏的高度
return (r.bottom - r.top);
}
r.bottom - r.top:表示当前窗口可视区域的高度
4、获取手机屏幕的高度
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
5、输入法的高度
//输入法的高度 = 手机屏幕的高度 - 当前窗口.bottom
int heightDifference = usableHeightSansKeyboard - usableHeightNow;//(含义状态栏的高度)
注意:如果既使用了沉浸式状态栏,又加了fitSystetemWindow=true属性,就需要在AndroidMainfest.xml注册Activity的地方添加上以下属性。因为你两种都用,系统不知道用哪种了。fitSystetemWindow已经有resize屏幕的作用。
android:windowSoftInputMode="stateHidden|adjustPan"
测试代码如下
(1)布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#ff0000"
android:orientation="vertical"></LinearLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="vertical">
<EditText
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="1" />
<EditText
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="2" />
<EditText
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="3" />
<EditText
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="4" />
<EditText
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="5" />
<EditText
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="6" />
<EditText
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="7" />
<EditText
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="8" />
<EditText
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="9" />
<EditText
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="10" />
<EditText
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="11" />
<EditText
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="12" />
<EditText
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="13" />
<EditText
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="14" />
<EditText
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="15" />
<EditText
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="16" />
<EditText
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="17" />
<EditText
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="18" />
<EditText
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="19" />
</LinearLayout>
</ScrollView>
</LinearLayout>
(2)工具类
package demo.com.keyboarddemo;
import android.app.Activity;
import android.graphics.Rect;
import android.os.Build;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
public class SoftHideKeyBoardUtil {
public static void assistActivity(Activity activity) {
new SoftHideKeyBoardUtil(activity);
}
private View mChildOfContent;
private int usableHeightPrevious;
private FrameLayout.LayoutParams frameLayoutParams;
//为适应华为小米等手机键盘上方出现黑条或不适配
private int contentHeight;//获取setContentView本来view的高度
private boolean isfirst = true;//只用获取一次
private int statusBarHeight;//状态栏高度
private SoftHideKeyBoardUtil(Activity activity) {
//1、找到Activity的最外层布局控件,它其实是一个DecorView,它所用的控件就是FrameLayout
FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
//2、获取到setContentView放进去的View
mChildOfContent = content.getChildAt(0);
//3、给Activity的xml布局设置View树监听,当布局有变化,如键盘弹出或收起时,都会回调此监听
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
//4、软键盘弹起会使GlobalLayout发生变化
public void onGlobalLayout() {
if (isfirst) {
contentHeight = mChildOfContent.getHeight();//兼容华为等机型
isfirst = false;
}
//5、当前布局发生变化时,对Activity的xml布局进行重绘
possiblyResizeChildOfContent();
}
});
//6、获取到Activity的xml布局的放置参数
frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();
}
// 获取界面可用高度,如果软键盘弹起后,Activity的xml布局可用高度需要减去键盘高度
private void possiblyResizeChildOfContent() {
//1、获取当前界面可用高度,键盘弹起后,当前界面可用布局会减少键盘的高度
int usableHeightNow = computeUsableHeight();
//2、如果当前可用高度和原始值不一样
if (usableHeightNow != usableHeightPrevious) {
//3、获取Activity中xml中布局在当前界面显示的高度
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
Log.i("zss", "------ 获取Activity中xml中布局在当前界面显示的高度:" + usableHeightSansKeyboard);
//4、Activity中xml布局的高度-当前可用高度
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
Log.i("zss", "------ Activity中xml布局的高度-当前可用高度:" + heightDifference + " 状态栏:" + statusBarHeight);
//5、高度差大于屏幕1/4时,说明键盘弹出
if (heightDifference > (usableHeightSansKeyboard / 4)) {
// 6、键盘弹出了,Activity的xml布局高度应当减去键盘高度
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
} else {
frameLayoutParams.height = contentHeight;
}
//7、 重绘Activity的xml布局
mChildOfContent.requestLayout();
usableHeightPrevious = usableHeightNow;
}
}
private int computeUsableHeight() {
Rect r = new Rect();
/**
* 一个窗口中通常都会有多个View,getWindowVisibleDisplayFrame()方法的返回结果和该窗口中选取的View并没有关系。
* 在某个时刻,使用当前窗口中的任意View执行getWindowVisibleDisplayFrame()返回的结果都是一样的。
*/
//获取当前窗口可视区域大小的
mChildOfContent.getWindowVisibleDisplayFrame(r);
Log.i("zss", "----r.bottom:" + r.bottom + " ; r.top:" + r.top);
// 全屏模式下:直接返回r.bottom,r.top其实是状态栏的高度
// return (r.bottom - r.top);
return r.bottom;
}
}
(3)使用
package demo.com.keyboarddemo;
import android.app.Activity;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class EditViewTestActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_edit_view_test);
SoftHideKeyBoardUtil.assistActivity(this);
}
}