输入框定位于软键盘顶部三种方法
2020-04-16 本文已影响0人
书柜里的松鼠
三种方法我们从简单的说起。
方法一很简单,但是效果不太好。
android:windowSoftInputMode=”adjustPan”
或是android:windowSoftInputMode=”adjustResize”
属性
使用起来很简单,只要在在AndroidManifest.xml对应的Activity里添加就好了。
然而,这样设置后,当软键盘弹出时,只是保证你的输入框(严格的说,就是输入光标所在位置)在软键盘的上方,其他的布局就相应的往上提。
那么,这里的坑就是,实际的ui中,输入框往往有些边框、背景等装饰元素,这些元素的底部就会被挡住,实在是不够美观,透着一股敷衍的气息。
方法二是通过获得Activity的可用高度来计算软键盘高度,然后根据软键盘高度设置相应控件的位置以达到需要的效果。
话不多说,放代码:
package com.codepig.common.util;
import android.app.Activity;
import android.graphics.Rect;
import android.os.Build;
import android.view.View;
import android.widget.FrameLayout;
/**
* 解决键盘档住输入框
*/
public class SoftHideKeyBoardUtil {
public static void assistActivity (Activity activity) {
new SoftHideKeyBoardUtil(activity);
}
private View mChildOfContent; private int usableHeightPrevious; private FrameLayout.LayoutParams frameLayoutParams; //为适应华为小米等手机键盘上方出现黑条或不适配
private int contentHeight;//获取setContentView本来view的高度
private boolean isFirst = true;//只用获取一次
private int statusBarHeight;//状态栏高度
private SoftHideKeyBoardUtil(Activity activity) {
//获得Activity的最外层布局控件
FrameLayout content = activity.findViewById(android.R.id.content);
if(content.getChildCount()==0){
return;
}
mChildOfContent = content.getChildAt(0);
//布局设置View树监听,当布局有变化,如键盘弹出或收起时,都会回调此监听
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
if (isFirst) {
contentHeight = mChildOfContent.getHeight();//兼容华为等机型
isFirst = false;
}
//当前布局发生变化时,对布局进行重绘
possiblyResizeChildOfContent();
});
frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();
}
// 获取界面可用高度,如果软键盘弹起后,Activity的可用高度需要减去键盘高度
private void possiblyResizeChildOfContent() {
//获取当前界面可用高度,键盘弹起后,当前界面可用布局会减少键盘的高度
int usableHeightNow = computeUsableHeight();
if (usableHeightNow != usableHeightPrevious) {
//获取布局在当前界面显示的高度
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
//布局的高度-当前可用高度
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
//高度差大于屏幕1/4时,说明键盘弹出
if (heightDifference > (usableHeightSansKeyboard/4)) {
//键盘弹出了,布局高度应当减去键盘高度
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference + statusBarHeight;
} else {
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
}
} else {
frameLayoutParams.height = contentHeight;
}
//重绘Activity布局
mChildOfContent.requestLayout();
usableHeightPrevious = usableHeightNow;
}
}
private int computeUsableHeight() {
Rect r = new Rect();
mChildOfContent.getWindowVisibleDisplayFrame(r);
return (r.bottom - r.top);// 全屏模式下:直接返回r.bottom,r.top其实是状态栏的高度
}
}
然后,只要在Activity
的onCreate
中使用就可以了。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SoftHideKeyBoardUtil.assistActivity(this);
}
但是!!!这个方法也不完美。
因为,这个方法只能在android:windowSoftInputMode=”adjustPan”
或是android:windowSoftInputMode=”adjustResize”
时使用。理论上说这也没什么,但是每次页面重绘都会一跳一跳的,看着还是有点膈应。
而且,实际使用中发现华为、vivo什么的不同厂家,表现出来的位置还不一样,带来了相当大的困扰。
那么,在android:windowSoftInputMode=”adjustNothing”
时怎么办呢?
这个时候是无法获取Activity的可用区域的变化的,因为他就没有变化。
所以,第三种方法种我们需要一个替身。一个宽度为0,看不见的PopupWindow来当我们的尺子。
来,上代码。
package com.codepig.common.view;
import android.app.Activity;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.view.Gravity;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.PopupWindow;
/**
* 用以监听软键盘高度的PopupWindow
*/
public class HeightProvider extends PopupWindow implements ViewTreeObserver.OnGlobalLayoutListener {
private Activity mActivity;
private View rootView;
private HeightListener listener;
private int heightMax; //popup内容区的最大高度
public HeightProvider(Activity activity) {
super(activity);
this.mActivity = activity;
// 基础配置
rootView = new View(activity);
setContentView(rootView);
// 监听全局Layout变化
rootView.getViewTreeObserver().addOnGlobalLayoutListener(this);
setBackgroundDrawable(new ColorDrawable(0));
// 设置宽度为0,高度为全屏
setWidth(0);
setHeight(WindowManager.LayoutParams.MATCH_PARENT);
// 设置键盘弹出方式
setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
}
public HeightProvider init() {
if (!isShowing()) {
final View view = mActivity.getWindow().getDecorView();
// 延迟加载PopupWindow,等待初始化完成
view.post(() -> showAtLocation(view, Gravity.NO_GRAVITY, 0, 0));
}
return this;
}
public HeightProvider setHeightListener(HeightListener listener) {
this.listener = listener;
return this;
}
@Override
public void onGlobalLayout() {
Rect rect = new Rect();
rootView.getWindowVisibleDisplayFrame(rect);
if (rect.bottom > heightMax) {
heightMax = rect.bottom;
}
//键盘高度
int keyboardHeight = heightMax - rect.bottom;
if (listener != null) {
listener.onHeightChanged(keyboardHeight);
}
}
public interface HeightListener {
void onHeightChanged(int height);
}
}
使用也很简单,下面代码中的binding.commentArea
就是需要保持在软键盘顶部的布局。
new HeightProvider(getActivity()).init().setHeightListener(height -> {
if(height>0) {
binding.commentArea.setTranslationY(-height - KeyBoardUtil.getBottomStatusHeight(getActivity()));
}else{
binding.commentArea.setTranslationY(-height);
}
});
本文涉及的代码所在github:
https://github.com/codeqian/MVVMDemo/blob/master/common/src/main/java/com/codepig/common/util/SoftHideKeyBoardUtil.java