Keyboard 软键盘阻挡输入框爬坑指南

news/2025/1/16 3:53:22/

导读:

日常开发中我们经常会用到EditText输入框,但有时我们的输入框会出现被软键盘界面阻挡,那么我们就会想到设置android:windowSoftInputMode属性

但是,当我们用的正爽的时候,又会出现什么布局上移,ba..ba..ba的bug

因此,本篇将针对个人在开发中遇到的”软键盘阻挡输入框”问题介绍


android:windowSoftInputMode属性说明

属性说明
stateUnspecified软键盘的状态并没有指定,系统将选择一个合适的状态或依赖于主题的设置
stateUnchanged当这个activity出现时,软键盘将一直保持在上一个activity里的状态,无论是隐藏还是显示
stateHidden用户选择activity时,软键盘总是被隐藏
stateAlwaysHidden当该Activity主窗口获取焦点时,软键盘也总是被隐藏的
stateVisible软键盘通常是可见的
stateAlwaysVisible用户选择activity时,软键盘总是显示的状态
adjustUnspecified默认设置,通常由系统自行决定是隐藏还是显示
adjustResize该Activity总是调整屏幕的大小以便留出软键盘的空间
adjustPan当前窗口的内容将自动移动以便当前焦点从不被键盘覆盖和用户能总是看到输入内容的部分
adjustNothing不调整窗口大小或平移窗口,软键盘就默认显示,会覆盖后面内容

  • 这个属性能影响两个事情:

    1. 当有焦点产生时,软键盘是隐藏还是显示
    2. 是否减少活动主窗口大小以便腾出空间放软键盘
  • 它的设置必须是下面的一个值,或者”state…| adjust..”

  • 正常情况来说,如果要防止软键盘挡住输入框如上表设置属性就能解决…

代码实现:


//adjustPan
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);//adjustResize
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);

清单文件对应节点实现:

<!-- 静态设置输入模式 --><!-- android:windowSoftInputMode="adjustResize" --><activity
            android:name=".activity.AdjustResize_KeyboardActivity"android:windowSoftInputMode="adjustResize"/><!-- android:windowSoftInputMode="adjustPan" --><activity
            android:name=".activity.AdjustPan_KeyboardActivity"android:windowSoftInputMode="adjustPan"/>

部分属性应用场景说明:

  1. 使用adjustPan,如果有几个EditText,当点击第一个EditText时,下方的EditText会被软键盘界面覆盖
  2. 使用adjustPan,如果输入框在软键盘后面,软键盘会把输入框以上的布局(包括ToolBar)挤出屏幕外
  3. 使用adjustResize,当使用LinearLayout作为输入框的父布局时,软键盘会覆盖输入框
  4. 使用adjustResize,当控件设置了background属性,或设置了权重的imageView会发生变形,该使用adjustPan

开发中遇到的Bug,以及解决方案:

设置了沉浸式/透明状态栏后,adjustResize失效问题

在根布局或者父布局设置android:fitsSystemWindows=”true”即可解决


“全屏模式”使用adjustResize无效,而且会类似adjustPan把上方布局挤出屏幕外

官方认为”全屏模式”指的是App自己接管了状态栏的控制,如使用了Fullscreen主题、使用了『状态色着色』、『沉浸式状态栏』、『Immersive Mode』等等


个人开发也有一段时间,由于很少遇到需要全屏(状态栏也隐藏)的应用,
真说有的话,那就是游戏里的,不过一旦设置Fullscreen主题一类的全屏,点击EditText输入框会跳转到一个全屏模式的输入界面,
因此这里就不作处理了,有兴趣的同学可以根据以下代码深入研究下,这里就不赘述了.使用:(最终效果类似adjustResize)
1.把AndroidBug5497Workaround类复制到项目中2.在需要填坑的activity的onCreate方法中添加一句AndroidBug5497Workaround.assistActivity(this)即可。----------------------------------------------------/** @本类描述   网上收集的,解决"全屏模式"下,adjustResize失效方案   *            * @内容说明   解决:*             1.非全屏模式下使用adjustPan无效问题*             2.全屏模式下使用adjustPan和adjustResize无效问题* * @补充内容   有兴趣的同学可以学下里面的思路,自己写一个解决方案(*^__^*) 嘻嘻……)** ---------------------------------     * @更新时间   * @新增内容   **/public class AndroidBug5497Workaround {// For more information, see https://code.google.com/p/android/issues/detail?id=5497// To use this class, simply invoke assistActivity() on an Activity that already has its content view set.public static void assistActivity(Activity activity) {new AndroidBug5497Workaround(activity);}private View                     mChildOfContent;private int                      usableHeightPrevious;private FrameLayout.LayoutParams frameLayoutParams;private AndroidBug5497Workaround(Activity activity) {//拿到当前XML文件的根布局FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);//监听当前View的状态,进行通知回调,即"软键盘弹出""mChildOfContent = content.getChildAt(0);mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {public void onGlobalLayout() {possiblyResizeChildOfContent();}});frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();}/*** 重新设置高度* <p>* 把界面高度设置为可用高度*/private void possiblyResizeChildOfContent() {int usableHeightNow = computeUsableHeight();if (usableHeightNow != usableHeightPrevious) {// int usableHeightSansKeyboard = activity.getWindowManager().getDefaultDisplay().getHeight();//获取屏幕尺寸,不包括虚拟功能高度 用这个可以完美解决//findViewById(android.R.id.content).getMeasuredHeight() 也可以解决虚拟按键问题int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();int heightDifference = usableHeightSansKeyboard - usableHeightNow;//排除其他View引起的变化,专注软键盘变化if (heightDifference > (usableHeightSansKeyboard / 4)) {// keyboard probably just became visibleframeLayoutParams.height = usableHeightSansKeyboard - heightDifference;} else {// keyboard probably just became hiddenframeLayoutParams.height = usableHeightSansKeyboard;}mChildOfContent.requestLayout();usableHeightPrevious = usableHeightNow;}}/*** 软键盘弹出后,可以显示内容的高度** @return*/private int computeUsableHeight() {Rect r = new Rect();//这行代码能够获取到去除标题栏和被软键盘挡住的部分,所剩下的矩形区域mChildOfContent.getWindowVisibleDisplayFrame(r);//r.top : 标题栏的高度//屏幕高度-r.bottom : 软键盘的高度//可用高度(全屏模式) : rect.bottom//可用高度(非全屏模式) : rect.bottom - rect.topreturn (r.bottom - r.top);// 全屏模式下: return r.bottom}}

工具类图解

这里写图片描述


一些”奇淫”技巧

有时候,我们需要将想要的Button按钮也显示在软键盘上面,如登录页面,如果我们只设置了adjustPan或adjustResize,那么Button按钮就会被挡住,体验不好.下面介绍几种解决方案

其实最简单的方式是将布局嵌套在ScrollView里,但体验一样不好,操作多了一步嘛

一、工具类方法(监听软键盘弹出位置,让我们的布局滑动到合适的位置)

Utils类


public class NestedScrollView_KeyboardUtils {private static final String TAG = "NestedScrollView_KeyboardUtils";/*** @param activity 上下文* @param viewId   NestedScrollView_ID*/public static void assistActivity(Activity activity, int viewId) {new NestedScrollView_KeyboardUtils(activity, viewId);}private View             mChildOfContent;/**android.support.v4包中,新版的ScrollView**/private NestedScrollView mScrollView;private NestedScrollView_KeyboardUtils(Activity activity, int viewId) {//拿到当前XML文件的根布局FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);//监听当前View的状态,进行通知回调,即"软键盘弹出""mChildOfContent = content.getChildAt(0);mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {@Overridepublic void onGlobalLayout() {possiblyResizeChildOfContent();}});mScrollView = (NestedScrollView) content.findViewById(viewId);}private void possiblyResizeChildOfContent() {int contentHeight = mChildOfContent.getRootView().getHeight();int curDisplayHeight = computeUsableHeight();if (contentHeight - curDisplayHeight > contentHeight / 4) {Log.e(TAG, "possiblyResizeChildOfContent: 1");mScrollView.scrollTo(0, 600);//                mScrollView.fullScroll(ScrollView.FOCUS_DOWN);} else {Log.e(TAG, "possiblyResizeChildOfContent: 2");}}/*** 软键盘弹出后,获取屏幕可显示区域高度** @return*/private int computeUsableHeight() {Rect r = new Rect();//这行代码能够获取到去除标题栏和被软键盘挡住的部分,所剩下的矩形区域mChildOfContent.getWindowVisibleDisplayFrame(r);//r.top : 标题栏的高度//屏幕高度-r.bottom : 软键盘的高度//可用高度(全屏模式) : rect.bottom//可用高度(非全屏模式) : rect.bottom - rect.topreturn r.height();}
}

布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:fitsSystemWindows="true"android:clipToPadding="true"><android.support.v4.widget.NestedScrollView
        android:id="@+id/scroll_view"android:layout_width="match_parent"android:layout_height="match_parent"><LinearLayout
            android:id="@+id/ll_parent"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextView
                android:layout_width="match_parent"android:layout_height="200dp"android:background="#0000ff"/><EditText
                android:id="@+id/account"android:layout_width="match_parent"android:layout_height="match_parent"/><EditText
                android:layout_width="match_parent"android:layout_height="match_parent"/><Button
                android:layout_width="match_parent"android:layout_height="wrap_content"android:text="确定"/></LinearLayout></android.support.v4.widget.NestedScrollView></LinearLayout>

主页面


// 改动过状态栏的状态,记得在XML布局文件根布局设置android:fitsSystemWindows="true"
public class ScrollViewByClassActivity extends AppCompatActivity {private LinearLayout     mParent;private EditText         account;private NestedScrollView scroll;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_scroll_class);//getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); // 设置全屏StatusBar.setStatusBarColor(this, Color.BLUE);mParent = (LinearLayout) findViewById(R.id.ll_parent);account = (EditText) findViewById(R.id.account);scroll = (NestedScrollView) findViewById(R.id.scroll_view);setScroll();}//键盘不遮挡按钮private void setScroll() {NestedScrollView_KeyboardUtils.assistActivity(this, R.id.scroll_view);       //这个是别人给我的工具类,只用这个会有hideKeyboard();scroll.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {         //scroll为parent外面那层布局()最好用NestedScrollView,ScrollView会有版本问题@Overridepublic void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {v.smoothScrollTo(0, 450);     //这个是滑动距离,随便大一点就好}});}/*** EditText失去焦点,隐藏软键盘*/private void hideKeyboard() {mParent.setOnTouchListener(new View.OnTouchListener() {                 //parent为Editext外面那层布局@Overridepublic boolean onTouch(View v, MotionEvent event) {mParent.setFocusable(true);mParent.setFocusableInTouchMode(true);mParent.requestFocus();InputMethodManager imm = (InputMethodManager) ScrollViewByClassActivity.this.getSystemService(Context.INPUT_METHOD_SERVICE);imm.hideSoftInputFromWindow(account.getWindowToken(), 0);  //隐藏键盘,account为Editext,随便一个就好return false;}});}}

效果图

这里写图片描述


二、自定义View作为我们的根布局,监听软键盘弹出状态,将我们的布局滑动到合适位置

自定义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;}/*** 测试弹出输入法时,系统做了什么**/private static int count = 0;@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);Log.e("onSizeChanged " + count++, "=>onResize called! w=" + w + ",h=" + h + ",oldw=" + oldw + ",oldh=" + oldh);}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {super.onLayout(changed, l, t, r, b);Log.e("onLayout " + count++, "=>OnLayout called! l=" + l + ", t=" + t + ",r=" + r + ",b=" + b);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);Log.e("onMeasure " + count++, "=>onMeasure called! widthMeasureSpec=" + widthMeasureSpec + ", heightMeasureSpec=" + heightMeasureSpec);}/*** 获取输入法高度** @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);}}}}

布局文件


<?xml version="1.0" encoding="utf-8"?>
<zs.xmx.view.KeyboardLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/keyboardview"android:layout_width="match_parent"android:layout_height="match_parent"android:fitsSystemWindows="true"><ScrollView
        android:id="@+id/scroll_view"android:layout_width="match_parent"android:layout_height="match_parent"><LinearLayout
            android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextView
                android:layout_width="match_parent"android:layout_height="200dp"android:background="#00f0ff"/><EditText
                android:id="@+id/account"android:layout_width="match_parent"android:layout_height="match_parent"/><EditText
                android:layout_width="match_parent"android:layout_height="match_parent"/><Button
                android:layout_width="match_parent"android:layout_height="wrap_content"android:text="确定"/></LinearLayout></ScrollView></zs.xmx.view.KeyboardLayout>

主页面


public class ScrollViewByLayoutActivity extends AppCompatActivity {private ScrollView     scroll;private KeyboardLayout mKeyboardLayout;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// requestWindowFeature(Window.FEATURE_NO_TITLE); // 去除标题  必须在setContentView()方法之前调用// getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); // 设置全屏setContentView(R.layout.activity_scroll_layout);StatusBar.setStatusBarColor(this, Color.GRAY);mKeyboardLayout = (KeyboardLayout) findViewById(R.id.keyboardview);scroll = (ScrollView) findViewById(R.id.scroll_view);addLayoutListener();}/*** 监听键盘状态,布局有变化时,靠scrollView去滚动界面*/public void addLayoutListener() {mKeyboardLayout.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() {scroll.postDelayed(new Runnable() {@Overridepublic void run() {scroll.smoothScrollTo(0, scroll.getBottom() + getStatusBarHeight(ScrollViewByLayoutActivity.this));}}, 100);}/*** 获取状态栏高度** @param activity* @return*/public static int getStatusBarHeight(Activity activity) {//获取状态栏的高度int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");return activity.getResources().getDimensionPixelSize(resourceId);}
}

效果图:

这里写图片描述


总结:

  1. 如果EditText的位置写死在软键盘下方,会被覆盖.同理如果不想它移动可以写死,但是要注意屏幕适配问题
  2. 有些定制的键盘有精简模式,还有全屏/数字键盘大小不一样,可能有问题(目前没遇到)
  3. 若像我们登录界面,需要把登录按钮也上移,用scrollTo + 布局监听软键盘(即上面两种方案)
  4. 若有多个输入框,没什么特殊要求,使用ScrollView + adjustResize,基本能够满足开发需求
  5. 设置了沉浸式/透明状态栏,记得在布局文件设置android:fitsSystemWindows=”true”属性

本篇测试Demo:

这里写图片描述

Demo源码: keyboard_Demo

Keyboard 相关文章链接

Keyboard 输入框与软键盘联动

keyboard 动态启动或关闭软键盘

本篇文章到此结束,欢迎关注,后续有补充的会即时更新,有问题也欢迎评论,共同成长

待续:深入学下测量原理,写一个适配全屏的工具类


http://www.ppmy.cn/news/745310.html

相关文章

22.React Native避免键盘对TextInput遮挡一

目录 1.KeyboardAvoidingView 2.react-native-keyboard-aware-scroll-view 3.Keyboard Module 4.QQ聊天示例 4.1实现类似QQ聊天效果&#xff0c;点击输入框以后可以继续停留在未弹出键盘的位置&#xff1b; 4.2键盘显示或隐藏总是滚到底部 键盘遮挡 在开发中难免会用于…

在ubuntu中使用keychron/京东京造键盘

echo "options hid_apple fnmode2" | sudo tee /etc/modprobe.d/hid_apple.confsudo apt install initramfs-tools sudo update-initramfs -u -k all sudo dracut --regenerate-all --force重启系统 sudo systemctl reboot键盘模式选择为“Win/Android”&#xff0c…

使用MUI 软键盘弹起挤压页面

在使用mui和H5进行移动端开发的时候&#xff0c;经常会遇见需要用户输入的情况。当input获取焦点弹起软键盘的时候&#xff0c;经常会遇见软键盘挤压页面、软键盘遮挡输入框等一系列问题&#xff1b; 1. 单页面 软键盘弹起挤压页面 如上图所示&#xff1a;当页面中的in…

计算机主板用塑料做的好吗,太震惊!你的键盘是否用了黑心塑料?

01键盘为什么容易坏? [中关村在线键鼠频道原创] 键盘的怎么制造出来的&#xff1f;自己生产和OEM有什么区别&#xff1f;键盘的成本究竟多少&#xff1f;好坏键盘怎么从原料上分辨&#xff1f;这些各大论坛用户争论不休的问题&#xff0c;也许通过本文并不能给你一个满意的答案…

关于移动端input框获取焦点后被键盘遮盖问题修复的最佳方案

Android真机测试通过 ✔iOS真机测试通过 ✔ // input 键盘焦点问题修复window.addEventListener(resize, () > {const activeElement document.activeElementif (activeElement.tagName INPUT || activeElement.tagName TEXTAREA) {setTimeout(() > {activeElement.sc…

Openlayers实战:绘制矩形,正方形,正六边形

Openlayers地图中,绘制图形是非常重要的一个功能。Openlayers主要使用draw类来绘制图形,在实际项目中有时候会绘制矩形和正多边形。 下面的示例是绘制矩形,正方形,正多边形。 效果图 源代码 /* * @Author: 大剑师兰特(xiaozhuanlan),还是大剑师兰特(CSDN) * @此源代…

移动端拉起键盘后遮挡问题

一&#xff0c;问题 移动端&#xff0c;点击input时&#xff0c;部分安卓手机拉起键盘后&#xff0c;会遮挡住input。 二&#xff0c;解决 // 初始化完成后&#xff0c;需要监听键盘的调起 function keyWorld() {window.addEventListener(resize, () > {onFocusAddr();})…

如何解决由引起的IQKeyboardManager部分页面返回的键盘高度比实际小或最后收到键盘隐藏通知却显示了键盘问题

部分页面需要显示一个工具栏&#xff0c;当键盘出现时&#xff0c;这个工具栏显示在键盘上方。由于使用了IQKeyboardManager&#xff0c;导致有的页面iOS12.5.6系统的手机出现首次键盘高度比实际低44像素&#xff0c;而iOS14.7.1系统的手机出现首次键盘高度比实际低44像素并且有…