一、背景说明

在 Android 应用中,键盘弹出时常会遮挡输入框,尤其是嵌套在 PopupWindowWebView 中的页面,系统并不会自动调整布局高度。这时就需要开发者手动监听键盘状态并动态调整布局,达到“键盘避让”的目的。

二、方案目标

  • 动态监听键盘高度变化
  • 判断键盘是否弹出
  • 根据键盘高度动态调整 WebView 布局
  • 支持关闭键盘优化(如部分页面由前端主动抬升)

三、核心思路

  1. 通过ViewTreeObserver.OnGlobalLayoutListener获取当前可视区域,监听高度变化。
  2. 判断当前是否处于键盘弹出状态。
  3. 若处于弹出状态,根据变化的高度计算WebView的偏移量(topMargin / bottomMargin)。
  4. 记录初始布局状态,键盘收起时恢复原始布局。

四、关键变量说明

变量名类型说明
mGlobalLayoutListener监听器全局布局监听器
mShowingKeyBoardBoolean当前是否处于键盘弹出状态
mKeyboardHeightInt当前键盘高度
mInitialWebviewTopMarginIntWebView 初始 topMargin,用于复原
mLastKeyboardOptimizeHeightInt上一次处理的键盘高度,用于防抖
mUseKeyboardOptimizeBoolean是否开启键盘避让模式

五、代码实现

1. 注册监听器:keyboardOptimization()

/**
 * 键盘优化 - 记录当前键盘状态,用于在键盘弹出时,不响应手势下滑
 */
private void keyboardOptimization() {
    mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
        /** 保存键盘调起前的视图 bottom */
        int preBottom;
 
        @Override
        public void onGlobalLayout() {
            Rect rect = new Rect();
            mBrowserView.getWindowVisibleDisplayFrame(rect);
            
            // 初次调用,记录底部位置
            if (preBottom == 0) {
                preBottom = rect.bottom;
                return;
            }
 
            // 计算当前键盘高度
            int keyBoard = preBottom - rect.bottom;
 
            // 键盘状态发生变化
            if ((!mShowingKeyBoard && keyBoard > 0) || (mShowingKeyBoard && keyBoard <= 0)) {
                mShowingKeyBoard = keyBoard > 0;
                mKeyboardHeight = keyBoard;
 
                // 如果关闭优化,并且是弹出键盘,跳过处理
                if (!mUseKeyboardOptimize && mShowingKeyBoard) {
                    return;
                }
 
                LogEx.i(TAG, "【键盘高度】mKeyboardHeight = " + mKeyboardHeight);
                updateWebViewLayoutParams(mKeyboardHeight);
            }
        }
    };
 
    mRootLayout.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
}

2. 更新 WebView 布局:updateWebViewLayoutParams()

/**
 * 更新 WebView 布局参数
 *
 * @param height 抬起高度(键盘高度)
 */
private void updateWebViewLayoutParams(int height) {
    LinearLayout.LayoutParams params = getWebViewLayoutParams();
    if (params == null) {
        return;
    }
 
    if (height > 0) {
        // 防抖:同样高度不重复处理
        if (height == mLastKeyboardOptimizeHeight) {
            return;
        }
 
        // 记录初始 topMargin
        if (mInitialWebviewTopMargin == -1) {
            mInitialWebviewTopMargin = params.topMargin;
        }
 
        // 计算偏移差值
        int delta = height - mLastKeyboardOptimizeHeight;
 
        // 向上抬起 WebView
        params.topMargin -= delta;
        params.bottomMargin += delta;
    } else {
        // 键盘收起,恢复原始布局
        params.topMargin = mInitialWebviewTopMargin;
        params.bottomMargin = 0;
        mInitialWebviewTopMargin = -1;
    }
 
    // 记录上一次处理高度
    mLastKeyboardOptimizeHeight = height;
    setWebViewLayoutParams(params);
}

更新: 2025-09-03 23:46:48
原文: https://www.yuque.com/dongpozhouzi-mshe3/zhm85g/wbe3beshkp2ax2lq


相关笔记