首页 >> 大全

android全屏/沉浸式状态栏下,各种键盘挡住输入框解决办法[转]

2023-12-25 大全 23 作者:考证青年

*本篇文章已授权微信公众号 (郭霖)独家发布

在开发中,经常会遇到键盘挡住输入框的情况,比如登录界面或注册界面,弹出的软键盘把登录或注册按钮挡住了,用户必须把软键盘收起,才能点击相应按钮,这样的用户体验非常不好。像微信则直接把登录按钮做在输入框的上面,但有很多情况下,这经常满足不了需求。同时如果输入框特别多的情况下,点击输入时,当前输入框没被挡住,但是当前输入框下面的输入框却无法获取焦点,必须先把键盘收起,再去获取下面输入框焦点,这样用户体验也非常不好,那有什么办法呢?

系统的和有什么区别,他们使用时的注意事项,有什么系统要求及蔽端呢?

下面对几种在开发中常用的方法进行总结:

方法一:非透明状态栏下使用和,或是透明状态栏下使用=true属性

主要实现方法:

在.xml对应的里添加

:=””或是:=””属性

这两种属性的区别,官方的解释是:

这两个属性作用都是为了调整界面使键盘不挡住输入框 ,我这里对这两种属性使用场景、优缺点、注意事项进行了全方面总结,不知大家平时使用时是否注意到了。

属性注意事项优缺点失效情况适用情况

需要界面本身可调整尺寸,

如在布局添加,或输入控件属于/某一项

优点:1.不会把标题栏顶出当前布局;

2.有多项输入时,当前输入框下面的输入框可上下滑动输入

缺点:1.需要界面本身可调整尺寸;

2. 全屏时失效

1.主窗口尺寸无法调整;

2.全屏

3..0以上通过style设置沉浸式状态栏模式而不设置为true

非全屏或是非沉浸式状态栏输入界面,输入框比较多

页面不会重新布局,当前输入框和键盘会直接将当前输入框以上界面整体向上平移,这样即使界面包含标题栏,也会被顶上去

优点:使用简单,不需要界面本身可调整尺寸,不会有失效情况

缺点:会把标题栏顶出当前布局;有多项输入时,当前输入框下面的输入框无法输入,必须收起键盘显示输入框再输入

有少量输入项,且输入量居界面上方

如果多个View设置了=”true”,只有初始的view起作用,都是从第一个设置了的view开始计算

优点:使用简单,需要沉浸式状态栏的界面,不需要自己计算状态栏的高度

缺点:使用有限制

1.View 的其他 值被重新改写了

2.手机系统版本>= 4.4

1.界面全屏

2.设置界面主题为沉浸式状态栏

假设原始界面是一个包含若干,如下图所示,在分别使用两种属性时的表现。

1、

整个界面向上平移,使输入框露出,它不会改变界面的布局;界面整体可用高度还是屏幕高度,这个可以通过下面的截图看出,如点击输入框6,输入框会被推到键盘上方,但输入框1被顶出去了,如果界面包含标题栏,也会被顶出去。

2、

需要界面的高度是可变的,或者说主窗口的尺寸是可以调整的,如果不能调整,则不会起作用。

例如:的xml布局中只有一个包含若干,在的.xml中设置:=””属性,点击输入框6, 发现软键盘挡住了输入框6,并没有调整,如下图所示:

但使用这两种属性,我们可以总结以下几点:

1) 使用, 如果需要输入的项比较多时,点击输入框,当前输入项会被顶到软键盘上方,但若当前输入框下面还有输入项时,却需要先收起键盘,再点击相应的输入项才能输入。这样操作太繁琐了,对于用户体验不大好;

屏幕键盘遮挡任务栏_键盘挡住了对话框_

2) 的使用,需要界面本身可显示的窗口内容能调整,可结合使用;

方法二:在界面最外层布局包裹 1、只使用

在相应界面的xml布局中,最外层添加一个,不在.xml中设置任何:属性,此时点击输入框,输入框均不会被软键盘档住。即使当前输入框下方也有输入框,在键盘显示的情况下,也可以通过上下滑动界面来输入,而不用先隐藏键盘,点击下方输入框,再显示键盘输入。

我们可以根据 的 工具来查看界面真正占用的布局高度,工具在

通过该工具,我们看到:

界面真正能用的高度=屏幕高度-状态栏高度-软键盘高度

界面中蓝框是真正界面所用的高度:

2、+

我们再在该类的.xml中设置属性为,

<activity android:name=".TestInputActivity"android:windowSoftInputMode="adjustPan">

发现当前输入框不会被挡住,但是输入框比较多时,在有键盘显示时,界面上下滑动,但只能滑动部分,且如果输入框在界面靠下方时,点击输入框,标题栏也会被顶出去,如下图所示:

我们借助 工具查看此设置布局可用高度,从下图可以看出,此时布局可用高度是屏幕的高度,上下滑动也只是此屏的高度,在输入框9以下的输入框滑不出来,向上滑动,也只能滑到输入框1。

3、+

我们前面说过的使用必须界面布局高度是可变的,如最外层套个或是界面可收缩的,才起作用。这里在该类的.xml中设置属性为,

<activity android:name=".TestInputActivity"android:windowSoftInputMode="adjustResize">

发现效果和1不设置任何属性类似,其使用高度也是:屏幕高度-状态栏高度-软键盘高度

我们再来看看默认属性值:

可以看出,系统将选择合适的状态,也就是在界面最外层包含一层时,设置默认属性值其实就是属性。

但以下两方面无法满足需求:

1) 当设置成全屏模式时或是使用沉浸式状态栏时,界面最外层包裹 ,当输入框超过一屏,当前输入框下面的输入框并不能上下滑动来输入,情况类似于+,只能滑动部分,通过 也可以看到,界面可用高度是整个屏幕高度,并不会进行调整高度。即使设置,也不起作用。

2) 如果是类似于注册界面或是登录界面,键盘会挡住输入框下面的登录按钮。

沉浸式状态栏/透明状态栏情况下

自系统4.4(API>=19)就开始支持沉浸式状态栏,当使用觉 (系统窗口),显示系统一些属性和操作区域,如 最上方的状态及没有实体按键的最下方的虚拟导航栏。

:=“true”会使得屏幕上的可布局空间位于状态栏下方与导航栏上方

方法三:使用方法,当键盘弹起时,让界面整体上移;键盘收起,让界面整体下移

使用场景:针对界面全屏或是沉浸式状态栏,输入框不会被键盘遮挡。主要用于一些登录界面,或是需要把界面整体都顶上去的场景。

1、主要实现步骤:

(1) 获取布局xml的最外层控件,如xml文件如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/main"tools:context="com.example.liubin1.softkeyboardhelper.MainActivity"><EditTextandroid:id="@+id/name"android:hint="请输入用户名:"android:layout_centerInParent="true"android:layout_width="match_parent"android:layout_height="50dp"/><EditTextandroid:id="@+id/pas"android:layout_below="@id/name"android:hint="请输入密码:"android:layout_centerInParent="true"android:layout_width="match_parent"android:layout_height="50dp"/><Buttonandroid:id="@+id/login_btn"android:layout_below="@id/rpas"android:layout_centerHorizontal="true"android:text="登录"android:layout_width="180dp"android:layout_height="50dp" />
RelativeLayout>

先获取到最外层控件

RelativeLayout main = (RelativeLayout) findViewById(R.id.main);

(2) 获取到最后一个控件,如上面的xml文件,最后一个控件是

Button login_btn = (Button) findViewById(R.id.login_btn);

(3) 给最外层控件和最后一个控件添加监听事件

//在Activity的onCreate里添加如下方法
addLayoutListener(main,login_btn);
/** * addLayoutListener方法如下 * @param main 根布局 * @param scroll 需要显示的最下方View */public void addLayoutListener(final View main, final View scroll) {main.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {@Overridepublic void onGlobalLayout() {Rect rect = new Rect();//1、获取main在窗体的可视区域main.getWindowVisibleDisplayFrame(rect);//2、获取main在窗体的不可视区域高度,在键盘没有弹起时,main.getRootView().getHeight()调节度应该和rect.bottom高度一样int mainInvisibleHeight = main.getRootView().getHeight() - rect.bottom;int screenHeight = main.getRootView().getHeight();//屏幕高度//3、不可见区域大于屏幕本身高度的1/4:说明键盘弹起了if (mainInvisibleHeight > screenHeight / 4) {int[] location = new int[2];scroll.getLocationInWindow(location);// 4、获取Scroll的窗体坐标,算出main需要滚动的高度int srollHeight = (location[1] + scroll.getHeight()) - rect.bottom;//5、让界面整体上移键盘的高度main.scrollTo(0, srollHeight);} else {//3、不可见区域小于屏幕高度1/4时,说明键盘隐藏了,把界面下移,移回到原有高度main.scrollTo(0, 0);}}});}
}

2、实现原理:

此方法通过监听最外层布局控件来检测软键盘是否弹出,然后去手动调用控件的方法达到调整布局目的。

具体实现代码见demo中的类。

3、弊端:

此种方法需要在当前界面写比较多的代码,在某些手机上,若输入时,软键盘高度是可变的,如中英文切换,高度变化时,会发现适配的不大好。如下图:

_屏幕键盘遮挡任务栏_键盘挡住了对话框

从上图可以看出,如果键盘高度变化,键盘还是会挡住登录按钮。

方法四:适配键盘高度变化情况,当键盘弹起时,让界面整体上移;键盘收起,让界面整体下移

此方法主要是通过在需要移动的控件外套一层,同时最布局最外层使用自定义view监听键盘弹出状态,计算键盘高度,再进行计算需要移动的位置,这个和方法三有点类似,但能适配键盘高度变化情况。

实现步骤

(1) 先写自定义View,实时临听界面键盘弹起状态,计算键盘高度

public class KeyboardLayout extends FrameLayout {private KeyboardLayoutListener mListener;private boolean mIsKeyboardActive = false; //输入法是否激活private int mKeyboardHeight = 0; // 输入法高度public KeyboardLayout(Context context) {this(context, null, 0);}public KeyboardLayout(Context context, AttributeSet attrs) {this(context, attrs, 0);}public KeyboardLayout(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);// 监听布局变化getViewTreeObserver().addOnGlobalLayoutListener(new KeyboardOnGlobalChangeListener());}public void setKeyboardListener(KeyboardLayoutListener listener) {mListener = listener;}public KeyboardLayoutListener getKeyboardListener() {return mListener;}public boolean isKeyboardActive() {return mIsKeyboardActive;}/** * 获取输入法高度 * * @return */public int getKeyboardHeight() {return mKeyboardHeight;}public interface KeyboardLayoutListener {/** * @param isActive 输入法是否激活 * @param keyboardHeight 输入法面板高度 */void onKeyboardStateChanged(boolean isActive, int keyboardHeight);}private class KeyboardOnGlobalChangeListener implements ViewTreeObserver.OnGlobalLayoutListener {int mScreenHeight = 0;private int getScreenHeight() {if (mScreenHeight > 0) {return mScreenHeight;}mScreenHeight = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getHeight();return mScreenHeight;}@Overridepublic void onGlobalLayout() {Rect rect = new Rect();// 获取当前页面窗口的显示范围((Activity) getContext()).getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);int screenHeight = getScreenHeight();int keyboardHeight = screenHeight - rect.bottom; // 输入法的高度boolean isActive = false;if (Math.abs(keyboardHeight) > screenHeight / 4) {isActive = true; // 超过屏幕五分之一则表示弹出了输入法mKeyboardHeight = keyboardHeight;}mIsKeyboardActive = isActive;if (mListener != null) {mListener.onKeyboardStateChanged(isActive, keyboardHeight);}}}}

(2) xml文件编写,在界面最外层套上自定义view,在需要滚动的控件外层添加

"@+id/main_ll"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@mipmap/login_bg"android:orientation="vertical">"@+id/login_ll"android:layout_width="match_parent"android:layout_height="match_parent">"match_parent"android:layout_height="wrap_content"android:orientation="vertical">"match_parent"android:layout_height="wrap_content"android:layout_marginLeft="50dp"android:layout_marginRight="50dp"android:layout_marginTop="200dp"android:background="@mipmap/login_input_field_icon"android:orientation="horizontal">"wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_marginLeft="20dp"android:background="@mipmap/login_yonghuming_icon" />"@+id/ui_username_input"style="@style/editext_input_style"android:layout_marginLeft="40dp"android:layout_marginRight="20dp"android:background="@null"android:hint="@string/login_hint_username"android:imeOptions="actionNext"android:textColor="@android:color/white"android:textColorHint="@android:color/white"></EditText>LinearLayout>"match_parent"android:layout_height="wrap_content"android:layout_marginLeft="50dp"android:layout_marginRight="50dp"android:layout_marginTop="20dp"android:background="@mipmap/login_input_field_icon"android:orientation="horizontal">"wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_marginLeft="20dp"android:background="@mipmap/login_mima_icon" />"@+id/ui_password_input"style="@style/editext_input_style"android:layout_marginLeft="40dp"android:layout_marginRight="20dp"android:background="@null"android:hint="@string/login_hint_pwd"android:imeOptions="actionDone"android:inputType="textPassword"android:textColor="@android:color/white"android:textColorHint="@android:color/white"></EditText>LinearLayout>"@+id/login_btn"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="50dp"android:layout_marginRight="50dp"android:layout_marginTop="20dp"android:background="@mipmap/login_button_bg_icon"android:text="@string/login"android:textColor="@color/titlebar_main_color"android:textSize="@dimen/font_normal" /></LinearLayout>ScrollView></com.example.smilexie.softboradblockedittext.util.KeyboardLayout>

(3) 调用,自定义view控件添加键盘响应,在键盘变化时调用的去滚动界面

/** * 监听键盘状态,布局有变化时,靠scrollView去滚动界面 */public void addLayoutListener() {bindingView.mainLl.setKeyboardListener(new KeyboardLayout.KeyboardLayoutListener() {@Overridepublic void onKeyboardStateChanged(boolean isActive, int keyboardHeight) {Log.e("onKeyboardStateChanged", "isActive:" + isActive + " keyboardHeight:" + keyboardHeight);if (isActive) {scrollToBottom();}}});}/** * 弹出软键盘时将SVContainer滑到底 */private void scrollToBottom() {bindingView.loginLl.postDelayed(new Runnable() {@Overridepublic void run() {bindingView.loginLl.smoothScrollTo(0, bindingView.loginLl.getBottom() + SoftKeyInputHidWidget.getStatusBarHeight(LoginActivityForDiffkeyboardHeight.this));}}, 100);}

具体实现代码见demo中的类。实现效果如下:

可以看到键盘高度变化了,也不会影响界面布局

方法五:监听顶层View,判断软键盘是否弹起,对界面重新绘制

此方法的实现来自中提出的issue 5497

使用场景:针对界面全屏或是沉浸式状态栏,界面包含比较多输入框,界面即使包裹了一层,在键盘显示时,当前输入框下面的输入不能通过上下滑动界面来输入。

感谢下面提出评论的同学,指出此方法的不适配问题,之前写的博文在华为小米手机上确实有不适配情况,在输入时,键盘有时会错乱,现在已加入适配。

一、实现步骤:

1、把类复制到项目中;

2、在需要使用的的方法中添加:.(this);即可。

二、实现原理:

类具体代码如下:

/** * 解决键盘档住输入框 * Created by SmileXie on 2017/4/3. */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) {//1、找到Activity的最外层布局控件,它其实是一个DecorView,它所用的控件就是FrameLayoutFrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);//2、获取到setContentView放进去的ViewmChildOfContent = content.getChildAt(0);//3、给Activity的xml布局设置View树监听,当布局有变化,如键盘弹出或收起时,都会回调此监听 mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {//4、软键盘弹起会使GlobalLayout发生变化public void onGlobalLayout() {if (isfirst) {contentHeight = mChildOfContent.getHeight();//兼容华为等机型isfirst = false;}//5、当前布局发生变化时,对Activity的xml布局进行重绘possiblyResizeChildOfContent();}});//6、获取到Activity的xml布局的放置参数frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();}// 获取界面可用高度,如果软键盘弹起后,Activity的xml布局可用高度需要减去键盘高度 private void possiblyResizeChildOfContent() {//1、获取当前界面可用高度,键盘弹起后,当前界面可用布局会减少键盘的高度int usableHeightNow = computeUsableHeight();//2、如果当前可用高度和原始值不一样if (usableHeightNow != usableHeightPrevious) {//3、获取Activity中xml中布局在当前界面显示的高度int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();//4、Activity中xml布局的高度-当前可用高度int heightDifference = usableHeightSansKeyboard - usableHeightNow;//5、高度差大于屏幕1/4时,说明键盘弹出if (heightDifference > (usableHeightSansKeyboard/4)) {// 6、键盘弹出了,Activity的xml布局高度应当减去键盘高度if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){frameLayoutParams.height = usableHeightSansKeyboard - heightDifference + statusBarHeight;} else {frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;}} else {frameLayoutParams.height = contentHeight;}//7、 重绘Activity的xml布局mChildOfContent.requestLayout();usableHeightPrevious = usableHeightNow;}}private int computeUsableHeight() {Rect r = new Rect();mChildOfContent.getWindowVisibleDisplayFrame(r);// 全屏模式下:直接返回r.bottom,r.top其实是状态栏的高度return (r.bottom - r.top);}
}

它的实现原理主要是:

(1) 找到的最外层布局控件,我们知道所有的都是,它就是一个控件,该控件id是系统写死叫R.id.,就是我们时,把相应的View放在此控件里

FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);

所以.(0)获取到的,也就是我们用放进去的View。

(2) 给我们的的xml布局View设置一个监听

mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener({ possiblyResizeChildOfContent();
});

View.()可以获取一个对象——它是一个观察者,用以监听当前View树所发生的变化。这里所注册的tener,就是会在当前的View树的全局布局()发生变化、或者其中的View可视状态有变化时,进行通知回调。『软键盘弹出/隐 』都能监听到。

(3) 获取当前界面可用高度

private int computeUsableHeight() {Rect rect = new Rect();mChildOfContent.getWindowVisibleDisplayFrame(rect);// rect.top其实是状态栏的高度,如果是全屏主题,直接 return rect.bottom就可以了return (rect.bottom - rect.top);
}

如下图所示:

(4) 重设高度, 我们计算出的可用高度,是目前在视觉效果上能看到的界面高度。但当前界面的实际高度是比可用高度要多出一个软键盘的距离的。

具体实现代码见demo中的类。

注意:如果既使用了沉浸式状态栏,又加了=true属性,就需要在.xml注册的地方添加上以下属性。因为你两种都用,系统不知道用哪种了。已经有屏幕的作用。

android:windowSoftInputMode="stateHidden|adjustPan"

通过上面的这种方法,一般布局输入键盘挡住输入框的问题基本都能解决。即使界面全屏或是沉浸式状态栏情况。

总结:

下面对上面几种方法进行对比:

综上所述:

1) 当输入框比较少时,界面只有一个输入框时,可以通过方法一设置;

2) 如果对于非全屏/非沉浸式状态栏需求,只需要使用方法二+;

3) 如果对于使用沉浸式状态栏,使用=true属性,按道理系统已经做好适配,键盘不会挡住输入框;

4) 如果全屏/沉浸式状态栏界面,类似于登录界面,有需要把登录键钮或是评论按钮也顶起,如果键盘没有变化需求,可以使用方法三,若需要适配键盘高度变化,则需要使用方法四;

5) 如果界面使用全屏或沉浸式状态栏,没有使用=true属性,一般如需要用到抽屈而且状态栏颜色也需要跟着变化,则选择方法五更恰当。

代码传送门:如果有哪写的不对或是不好的地方,欢迎大家提出宝贵意见,一起探讨,共同进步。

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了