Android优雅地判断软键盘弹出状态
一、为什么要判断软键盘弹出状态
神马笔记在完成笔记编辑时,会对编辑界面进行截图以作为笔记的图标。为了保证图标的一致性,需要在关闭软键盘后进行截图,否则会产生2种尺寸的截图大小。
因此,需要判断软键盘是否弹出。如果软键盘弹出,则先关闭软键盘,用户才能退出编辑。
二、已有的判断方案
http://www.cnblogs.com/shelly-li/p/5639833.html
https://blog.****.net/yijiaodingqiankun/article/details/81085167?utm_source=blogxgwz5
https://blog.****.net/sinat_31311947/article/details/53899166
https://blog.****.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值,从而判断软键盘的状态。
四、实现效果
注意左上角的变化。
软键盘弹出时,显示为“完成”按钮。
软键盘关闭时,显示为“返回”图标。
五、完整代码
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);
}
}
六、核心代码
- 计算最大Bottom值
需要最大Bottom值作为参考值,比较当前bottom及最大bottom,从而判断软键盘状态。
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);
}
- 判断软键盘是否弹出
将当前bottom与最大bottom比较,从而得出结论。
@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】