Android优雅地判断软键盘弹出状态

Android优雅地判断软键盘弹出状态

一、为什么要判断软键盘弹出状态

神马笔记在完成笔记编辑时,会对编辑界面进行截图以作为笔记的图标。为了保证图标的一致性,需要在关闭软键盘后进行截图,否则会产生2种尺寸的截图大小。

因此,需要判断软键盘是否弹出。如果软键盘弹出,则先关闭软键盘,用户才能退出编辑。

二、已有的判断方案

http://www.cnblogs.com/shelly-li/p/5639833.html

https://blog.csdn.net/yijiaodingqiankun/article/details/81085167?utm_source=blogxgwz5

https://blog.csdn.net/sinat_31311947/article/details/53899166

https://blog.csdn.net/javazejian/article/details/52126391

所有方案一致指向,根据布局变化来判断软键盘是否弹出是最稳妥的实现方案。

三、设计新的方案

首先,需要设置Activity的windowSoftInputMode属性为adjustResize

当软键盘状态发生变化时,布局会相应地发生变化。再根据布局变化来判断软键盘状态。

其次,监听Activity的android.R.id.content控件的布局变化。

android.R.id.content是每个Activity的用户控件的容器,存在于每一个Activity中,所以我们监听android.R.id.content的变化可以适应所有的Activity。

再来,使用LifecycleObserver来感知Activity的状态变化,从而决定何时启动布局变化监听。

最后,使用ActivityLifecycleCallbacks关心其他Activity的变化,获取Bottom的最大值,比较当前Bottom值以及最大Bottom值,从而判断软键盘的状态。

四、实现效果

注意左上角的变化。

软键盘弹出时,显示为“完成”按钮。

软键盘关闭时,显示为“返回”图标。

五、完整代码

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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
package club.andnext.helper;

import android.app.Activity;
import android.app.Application;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;

/**
*
*/
public class SoftInputHelper implements LifecycleObserver,
Application.ActivityLifecycleCallbacks,
ViewTreeObserver.OnGlobalLayoutListener {

private static final String TAG = SoftInputHelper.class.getSimpleName();

int bottom;

boolean visible;

Rect rect;

OnSoftInputListener onSoftInputListener;

FragmentActivity context;

public SoftInputHelper(FragmentActivity context) {
this.context = context;

this.bottom = 0;
this.visible = false;

this.rect = new Rect();

context.getLifecycle().addObserver(this);
}

@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
public void onCreate() {
context.getApplication().registerActivityLifecycleCallbacks(this);
}

@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onStart() {
ViewTreeObserver observer = getViewTreeObserver(context);
if (observer != null && observer.isAlive()) {
observer.addOnGlobalLayoutListener(this);
}
}

@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onStop() {
ViewTreeObserver observer = getViewTreeObserver(context);
if (observer != null && observer.isAlive()) {
observer.removeOnGlobalLayoutListener(this);
}
}

@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onDestroy() {
context.getApplication().unregisterActivityLifecycleCallbacks(this);

context.getLifecycle().removeObserver(this);
}

@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {

}

@Override
public void onActivityStarted(Activity activity) {

}

@Override
public void onActivityResumed(Activity activity) {

}

@Override
public void onActivityPaused(Activity activity) {

}

@Override
public void onActivityStopped(Activity activity) {
this.updateBottom(activity);
}

@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

}

@Override
public void onActivityDestroyed(Activity activity) {

}

@Override
public void onGlobalLayout() {

{
this.updateBottom(context);
}

View target = getView(context);
if (target != null) {

target.getGlobalVisibleRect(rect);
boolean result = rect.bottom < bottom;

if (this.visible ^ result) {
this.visible = result;

if (onSoftInputListener != null) {
onSoftInputListener.onSoftInputChanged(this, visible);
}
}

}
}

public int getBottom() {
return bottom;
}

public void setOnSoftInputListener(OnSoftInputListener listener) {
this.onSoftInputListener = listener;
}

void updateBottom(Activity activity) {
View view = getView(activity);
if (view != null) {
view.getGlobalVisibleRect(rect);

bottom = (bottom < view.getBottom())? view.getBottom(): bottom;
bottom = (bottom < rect.bottom)? rect.bottom: bottom;
}

Log.v(TAG, "bottom = " + bottom);
}

View getView(Activity activity) {
View view = activity.getWindow().getDecorView().findViewById(android.R.id.content);
return view;
}

ViewTreeObserver getViewTreeObserver(Activity activity) {
View view = getView(activity);
if (view == null) {
return null;
}

return view.getViewTreeObserver();
}

/**
*
*/
public interface OnSoftInputListener {

void onSoftInputChanged(SoftInputHelper helper, boolean visible);

}
}

六、核心代码

  1. 计算最大Bottom值

需要最大Bottom值作为参考值,比较当前bottom及最大bottom,从而判断软键盘状态。

1
2
3
4
5
6
7
8
9
10
11
void updateBottom(Activity activity) {
View view = getView(activity);
if (view != null) {
view.getGlobalVisibleRect(rect);

bottom = (bottom < view.getBottom())? view.getBottom(): bottom;
bottom = (bottom < rect.bottom)? rect.bottom: bottom;
}

Log.v(TAG, "bottom = " + bottom);
}
  1. 判断软键盘是否弹出

将当前bottom与最大bottom比较,从而得出结论。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Override
public void onGlobalLayout() {

{
this.updateBottom(context);
}

View target = getView(context);
if (target != null) {

target.getGlobalVisibleRect(rect);
boolean result = rect.bottom < bottom;

if (this.visible ^ result) {
this.visible = result;

if (onSoftInputListener != null) {
onSoftInputListener.onSoftInputChanged(this, visible);
}
}

}
}

七、下载地址

神马笔记最新版本:【whatsnote_lastest.apk