在Android应用的开发中,有一些产品需求,需要我们获取到输入法的高度。遗憾的是,Android官方并没有提供这样的API。 最近在做的直播项目就有类似的需求,先看一下淘宝的直播页面,当用户点击下方的布局时,弹出输入法的同时,将一个新的EditText置于输入法的正上方,这就需要我们准确的获取到输入法的高度,同时兼顾虚拟按键栏的高度。 同时也看到,在输入法出现时,后面的界面的布局没有受到任何的影响,这显然是android:windowSoftInputMode=”adjustNothing”的效果。关于windowSoftInputMode的各种属性含义,可以参考官方文档。 因此,综上所述,我们需求就是: 由于Android官方并没有提供API让我们准确的获取到输入法的高度,所以我们只能自己想办法实现。 网上的方案一搜一大把,但是效果并不理想,十个里面有九个都是通过监听布局的变化,即监听ViewTreeObserver.OnGlobalLayoutListener这个接口,在弹出输入法后,用屏幕的总高度减去当前页面窗口的显示范围来得到输入法的高度,通常代码如下: 这种方案的劣势很明显: 这种方案的核心原理为,新建一个宽度为0,高度为MATCH_PARENT且支持adjustResize属性的透明PopupWindow,将其盖在当前Activity的content之上,并通过getViewTreeObserver().addOnGlobalLayoutListener的方式,让PopupWindow感知到布局的变化,这样在输入法弹出之后,我们的PopupWindow就会被resize到一个小于屏幕高度的尺寸,用屏幕高度减去该尺寸,便得到了输入法的高度。该方案因为不涉及我们的Activity容器,因此Activity在配置为android:windowSoftInputMode=”adjustNothing”时,该方案同样生效。 首先定义一个接口,onKeyboardHeightChanged()会在输入法的高度发生变化时调用,参数height<=0时,意味着输入法被收起;height>0意味着输入法被打开,同时height值即为输入法的高度。参数orientation为屏幕方向,备用。 因为在我们的应用案例中,不管底部的虚拟操作栏是否存在,都应该准确的将EditText置于输入法的正上方,所以我们同样得关心虚拟操作栏的高度。因此设计另一个方法onVirtualBottomHeight()。参数height即为虚拟操作栏的高度,为0表示全面屏,即虚拟操作栏不存在的情况。 在KeyboardHeightProvider()构造方法中配置PopupWindow的各种参数,比如adjustResize等重要参数; PopupWindow的showAtLocation()时机实现在start()方法里,这里调用的时机很重要,必须在Activity的onResume之后调用,因此在Activity中需要手动post一下; 监听View树的OnGlobalLayoutListener,将屏幕高度减去PopupWindow的可见高度,得到一个diff的高度diff。 1.当diff为负数时,这个数值的相反数刚好等于底部虚拟操作栏的高度,回调给onVirtualBottomHeight()方法使用 2.当diff为0时,表示输入法为收起状态,回调给onKeyboardHeightChanged()方法使用 3.当diff为正数时,表示输入法为打开状态,回调给onKeyboardHeightChanged()方法使用 当计算PopupWindow的可见高度时,用到了getWindowVisibleDisplayFrame()这个方法,这个方法比较复杂,使用时有很多注意点,完全可以另写一篇文章介绍,这里简单介绍四点: 在Activity定义成员变量,并在onCreate()中进行初始化: 为了防止内存泄漏,进行生命周期的相关处理: 最后是在回调方法中,完成数据的使用: 在小米2S机型上出现了一个奇葩的兼容性问题,在PopupWindow中竟然无法触发onGlobalLayout的回调,后确认为系统bug,更新手机系统后解决。 将Activity的adjustNothing属性改成adjustResize后发现功能依然正常,效果同adjustNothing,这就有点奇葩了,我们当初费劲全力使用adjustNothing就是为了不让直播页面Resize,后续经过研究发现:在我们的工程里,为当前直播页面的Activity设置了全屏模式,全屏模式下adjustResize会失效,效果同adjustNothing(但是经过测试弹出输入法后Rect.bottom窗口大小还是变了,adjustNothing模式下是不可能变的)。如果把全面屏代码删掉,adjustResize会让你的界面错乱。这里只能说是歪打正着了,当然全屏模式+adjustNothing肯定也不会有什么问题。 上一条会延伸出一个新的问题:当你在全面屏模式下,需要输入框被顶上来时,即便你设置了adjustResize输入框依然会被输入法遮挡。在本文的业务场景里不需要,如果你有这个需求,可以参考以下三篇参考文章中的解决方案:
0. 前言
1. 通常的解决方案
@Override public void onGlobalLayout() { Rect rect = new Rect(); // 获取当前页面窗口的显示范围 ((Activity) getContext()).getWindow().getDecorView().getWindowVisibleDisplayFrame(rect); int screenHeight = getScreenHeight(); int keyboardHeight = screenHeight - rect.bottom; // 输入法的高度 if (Math.abs(keyboardHeight) > screenHeight / 5) { // 超过屏幕五分之一则表示弹出了输入法 } }
2. 基于“透明Window”的解决方案简介
方案原理简介
3. 基于“透明Window”方案的具体实现
3.1 接口设计
/** * Created by Calvin on 2020/6/1. */ public interface KeyboardHeightObserver { void onKeyboardHeightChanged(int height, int orientation); void onVirtualBottomHeight(int height); }
3.2 PopupWindow的实现
/** * Created by Calvin on 2020/6/1. */ public class KeyboardHeightProvider extends PopupWindow { private KeyboardHeightObserver observer; private int keyboardLandscapeHeight; private int keyboardPortraitHeight; private View popupView; private View parentView; private Activity activity; public KeyboardHeightProvider(Activity activity) { super(activity); this.activity = activity; LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); this.popupView = inflater.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(() -> { if (popupView != null) { handleOnGlobalLayout(); } }); } public void start() { if (!isShowing() && parentView.getWindowToken() != null) { setBackgroundDrawable(new ColorDrawable(0)); showAtLocation(parentView, Gravity.NO_GRAVITY, 0, 0); } } public void close() { this.observer = null; dismiss(); } public void setKeyboardHeightObserver(KeyboardHeightObserver observer) { this.observer = observer; } private int getScreenOrientation() { return activity.getResources().getConfiguration().orientation; } private void handleOnGlobalLayout() { Point screenSize = new Point(); activity.getWindowManager().getDefaultDisplay().getSize(screenSize); Rect rect = new Rect(); popupView.getWindowVisibleDisplayFrame(rect); int orientation = getScreenOrientation(); int keyboardHeight = screenSize.y - rect.bottom; if (keyboardHeight < 0 && observer != null) { observer.onVirtualBottomHeight(-keyboardHeight); } 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); } } }
3.3 Activity中使用
private KeyboardHeightProvider mProvider; //虚拟导航栏的高度, 默认为0 private int mVirtualBottomHeight; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { ... mProvider = new KeyboardHeightProvider(getActivity()); new Handler().post(() -> mProvider.start()); }
@Override protected void onResume() { super.onResume(); mProvider.setKeyboardHeightObserver(this); } @Override protected void onPause() { super.onPause(); mProvider.setKeyboardHeightObserver(null); } @Override protected void onDestroy() { super.onDestroy(); mProvider.close(); }
@Override public void onKeyboardHeightChanged(int height, int orientation) { if (height > 0) { //输入法弹出 //输入法之上的EditText在xml中位置是沉底的,这里显示出来, 并设置MarginBottom //这里的MarginBottom已经包含了虚拟导航栏的高度mVirtualBottomHeight mInputLayout.setVisibility(View.VISIBLE); ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mBinding.inputLayout.getRoot().getLayoutParams(); params.setMargins(0, 0, 0, height + mVirtualBottomHeight); mInputLayout.requestLayout(); } else { //输入法隐藏, 将输入法之上的EditText隐藏 mInputLayout.setVisibility(View.GONE); } } @Override public void onVirtualBottomHeight(int height) { //虚拟导航栏高度赋值 mVirtualBottomHeight = height; }
4. 后续彩蛋
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算