问题与解决

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

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

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

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

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

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

GitHub地址

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

  • KeyboardHeightObserver.java
/**
* 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
/**
* 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:

@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()中进行初始化

private KeyboardHeightProvider mKeyboardHeightProvider;

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

生命周期处理

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

@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相关博客:我的博客地址

Android开发 - 获取系统输入法高度的正确姿势的更多相关文章

  1. Android开发 - 获取Android设备的唯一标识码(Android 6.0或更高)

    在我们的APP开发中,通常需要获取到设备的唯一标识.在Android6.0之前,有很多方法我们可以方便获取到硬件的唯一标识,但是在Android6.0之后,Android系统大幅限制了我们获取设备的硬 ...

  2. android开发中系统自带语音模块的使用

    android开发中系统自带语音模块的使用需求:项目中需要添加语音搜索模块,增加用户体验解决过程:在网上搜到语音搜索例子,参考网上代码,加入到了自己的项目,完成产品要求.这个问题很好解决,网上能找到很 ...

  3. android开发获取网络状态,wifi,wap,2g,3g.工具类(一)

    android开发获取网络状态整理: package com.gzcivil.utils; import android.content.Context; import android.net.Con ...

  4. Android中获取系统上安装的APP信息

    Version:0.9 StartHTML:-1 EndHTML:-1 StartFragment:00000099 EndFragment:00003259 Android中获取系统上安装的APP信 ...

  5. 基于jeesite+android开发 电子商务系统免费教程

    下载地址: jeesite免费教程 基于jeesite+android开发 电子商务系统免费教程 基于jeesite+android开发 电子商务系统免费教程 这个教程已经录制完很久了,一直没有公开, ...

  6. Android 开发 获取Android设备的屏幕高宽

    获得屏幕的宽度和高度有很多种方法: //1.通过WindowManager获取 DisplayMetrics dm = new DisplayMetrics(); heigth = dm.height ...

  7. android:sharedUserId 获取系统权限

    最近在做的项目,有好大一部分都用到这个权限,修改系统时间啊,调用隐藏方法啊,系统关机重启啊,静默安装升级卸载应用等等,刚开始的时候,直接添加权限,运行就报错,无论模拟器还是真机,在logcat中总会得 ...

  8. Android apk获取系统权限

    Android在apk内部,即通过java代码来进行修改系统文件或者修改系统设置等等,这样需要获取系统权限. 通过直接配置apk运行在System进程内 1. 在应用程序的AndroidManifes ...

  9. Android项目--获取系统通讯录列表

    ----------------- 通讯录列表 ----------------- 按常理来说,获取系统通讯录列表,无非就是将通讯录的数据库打开获取数据,适配,添加即可. Cursor cursor; ...

随机推荐

  1. netty4初步使用

    文件 D:\jp\netty\NtServer.java import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Chan ...

  2. java多线程知识点

    下面是我学习多线程记录的知识点,并没详细讲解每个知识点,只是将重要的知识点记录下来,有时间可以看看,如果有不对的地方,欢迎大家指出,谢谢! 1.多线程的状态和创建方式:     线程的状态:      ...

  3. Twitter 相关APP开发

    首先要获取 Consumer Key (API Key), Consumer Secret (API Secret):最好申请Access Token 和Access Token Secret,不然验 ...

  4. iOS中四种实例变量的范围类型@private@protected@public@package

    文档上记录是这样的 The Scope of Instance Variables Toenforce the ability of an object to hide its data, the c ...

  5. canvas 实现弹跳效果

    一:创建画布 <canvas width="600" height="600" id="canvas"></canvas& ...

  6. kbmMW均衡负载与容灾(2)(转载红鱼儿)

    集中式均衡负载 为实现集中式均衡负载方案,需要实现两个不同的应用服务器,一个是只包含均衡负载组件再无其他内容的应用服务器,可称之为均衡负载应用服务器,下文简称LB Server,另外一个就是包含一个或 ...

  7. ATM自动取款机程序感想

    上周四的Java考试,老师并没有我们考暑假给我们布置的样卷的java程序,而是让我们做一个设计ATM的程序,然而这个对于我们来说好难,因为暑假没有学好java,首先基础知识还没有掌握,输入数据一开始都 ...

  8. 使用注解配置 AOP

    一.使用注解(基于Aspect) 1.spring不会自动去寻找注解,必须告诉spring那个包下的类有注解 1.1 先引入xmlns:context命名空间 <context:componen ...

  9. 2018.10.26 洛谷P4551 最长异或路径(01trie)

    传送门 直接把每个点到根节点的异或距离插入01trie. 然后枚举每个点在01trie上匹配来更新答案就行了. 代码: #include<iostream> #include<cst ...

  10. 2018.10.25 bzoj3928: [Cerc2014] Outer space invaders(区间dp)

    传送门 区间dpdpdp好题. 首先肯定需要把坐标离散化. 然后在数轴上面区间dpdpdp. 对于当前区间,区间中最大的数一定会被选. 于是我们记f[i,j]f[i,j]f[i,j]表示所有左端点在i ...