Android开发 - 获取系统输入法高度的正确姿势

问题与解决

在Android应用的开发中,有一些需求需要我们获取到输入法的高度,但是官方的API并没有提供类似的方法,所以我们需要自己来实现。

查阅了网上很多资料,试过以后都不理想。

比如有的方法通过监听布局的变化来计算输入法的高度,这种方式在Activity的配置中配置为”android:windowSoftInputMode=”adjustResize””时没有问题,可以正确获取输入法的高度,因为布局此时确实会动态的调整。

但是当Activity配置为”android:windowSoftInputMode=”adjustNothing””时,布局不会在输入法弹出时进行调整,上面的方式就会扑街。

不过经过一番探索和测试,终于发现了一种方式可以在即使设置为adjustNothing时也可以正确计算高度放方法。

同时也感谢这位外国朋友:
GitHub地址

其实也就两个类,我也做了一些修改,解决了一些问题,这里也贴出来:

  • KeyboardHeightObserver.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* The observer that will be notified when the height of
* the keyboard has changed
*/
public interface KeyboardHeightObserver {

/**
* Called when the keyboard height has changed, 0 means keyboard is closed,
* >= 1 means keyboard is opened.
*
* @param height The height of the keyboard in pixels
* @param orientation The orientation either: Configuration.ORIENTATION_PORTRAIT or
* Configuration.ORIENTATION_LANDSCAPE
*/
void onKeyboardHeightChanged(int height, int orientation);
}
  • KeyboardHeightProvider.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/**
* The keyboard height provider, this class uses a PopupWindow
* to calculate the window height when the floating keyboard is opened and closed.
*/
public class KeyboardHeightProvider extends PopupWindow {

/** The tag for logging purposes */
private final static String TAG = "sample_KeyboardHeightProvider";

/** The keyboard height observer */
private KeyboardHeightObserver observer;

/** The cached landscape height of the keyboard */
private int keyboardLandscapeHeight;

/** The cached portrait height of the keyboard */
private int keyboardPortraitHeight;

/** The view that is used to calculate the keyboard height */
private View popupView;

/** The parent view */
private View parentView;

/** The root activity that uses this KeyboardHeightProvider */
private Activity activity;

/**
* Construct a new KeyboardHeightProvider
*
* @param activity The parent activity
*/
public KeyboardHeightProvider(Activity activity) {
super(activity);
this.activity = activity;

LayoutInflater inflator = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
this.popupView = inflator.inflate(R.layout.keyboard_popup_window, null, false);
setContentView(popupView);

setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_RESIZE | LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);

parentView = activity.findViewById(android.R.id.content);

setWidth(0);
setHeight(LayoutParams.MATCH_PARENT);

popupView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

@Override
public void onGlobalLayout() {
if (popupView != null) {
handleOnGlobalLayout();
}
}
});
}

/**
* Start the KeyboardHeightProvider, this must be called after the onResume of the Activity.
* PopupWindows are not allowed to be registered before the onResume has finished
* of the Activity.
*/
public void start() {

if (!isShowing() && parentView.getWindowToken() != null) {
setBackgroundDrawable(new ColorDrawable(0));
showAtLocation(parentView, Gravity.NO_GRAVITY, 0, 0);
}
}

/**
* Close the keyboard height provider,
* this provider will not be used anymore.
*/
public void close() {
this.observer = null;
dismiss();
}

/**
* Set the keyboard height observer to this provider. The
* observer will be notified when the keyboard height has changed.
* For example when the keyboard is opened or closed.
*
* @param observer The observer to be added to this provider.
*/
public void setKeyboardHeightObserver(KeyboardHeightObserver observer) {
this.observer = observer;
}

/**
* Get the screen orientation
*
* @return the screen orientation
*/
private int getScreenOrientation() {
return activity.getResources().getConfiguration().orientation;
}

/**
* Popup window itself is as big as the window of the Activity.
* The keyboard can then be calculated by extracting the popup view bottom
* from the activity window height.
*/
private void handleOnGlobalLayout() {

Point screenSize = new Point();
activity.getWindowManager().getDefaultDisplay().getSize(screenSize);

Rect rect = new Rect();
popupView.getWindowVisibleDisplayFrame(rect);

// REMIND, you may like to change this using the fullscreen size of the phone
// and also using the status bar and navigation bar heights of the phone to calculate
// the keyboard height. But this worked fine on a Nexus.
int orientation = getScreenOrientation();
int keyboardHeight = screenSize.y - rect.bottom;

if (keyboardHeight == 0) {
notifyKeyboardHeightChanged(0, orientation);
}
else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
this.keyboardPortraitHeight = keyboardHeight;
notifyKeyboardHeightChanged(keyboardPortraitHeight, orientation);
}
else {
this.keyboardLandscapeHeight = keyboardHeight;
notifyKeyboardHeightChanged(keyboardLandscapeHeight, orientation);
}
}

private void notifyKeyboardHeightChanged(int height, int orientation) {
if (observer != null) {
observer.onKeyboardHeightChanged(height, orientation);
}
}
}

使用方法

此处以在Activity中的使用进行举例。

实现接口

引入这两个类后,在当前Activity中实现接口KeyboardHeightObserver:

1
2
3
4
5
@Override
public void onKeyboardHeightChanged(int height, int orientation) {
String or = orientation == Configuration.ORIENTATION_PORTRAIT ? "portrait" : "landscape";
Logger.d(TAG, "onKeyboardHeightChanged in pixels: " + height + " " + or);
}

定义并初始化

在当前Activity定义成员变量,并在onCreate()中进行初始化

1
2
3
4
5
6
7
8
private KeyboardHeightProvider mKeyboardHeightProvider;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
...
mKeyboardHeightProvider = new KeyboardHeightProvider(this);
new Handler().post(() -> mKeyboardHeightProvider.start());
}

生命周期处理

初始化完成后,我们要在Activity中的生命周期中也要进行处理,以免内存泄露。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
protected void onResume() {
super.onResume();
mKeyboardHeightProvider.setKeyboardHeightObserver(this);
}

@Override
protected void onPause() {
super.onPause();
mKeyboardHeightProvider.setKeyboardHeightObserver(null);
}

@Override
protected void onDestroy() {
super.onDestroy();
mKeyboardHeightProvider.close();
}

总结

此时我们就可以正确获取的当前输入法的高度了,即使android:windowSoftInputMode=”adjustNothing”时也可以正确获取到,这正是这个方法的强大之处,利用这个方法可以实现比如类似微信聊天的界面,流畅切换输入框,表情框等。

如有更多疑问,请参考我的其它Android相关博客:我的博客地址

坚持原创技术分享,您的支持将鼓励我继续创作!