欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android软键盘遮挡EditText问题

程序员文章站 2022-04-19 23:10:38
...

最近在调试App过程遇到软件盘遮挡EditText的问题,特记录下问题的解决过程
在4.4版本上,如果EditText设置了gravity=“center|right”其中之一且同时设置android:windowSoftInputMode="stateHidden|adjustPan",就会导致屏幕底部的EditText连续点击弹出键盘时,从第二次开会一直遮挡住EditText。在5.0+版本上不存在该问题,这可能是早期版本的Bug。

输入法的行为还与状态栏的状态有关联,比如说沉浸式、全屏及是否透明。

正常情况下,系统UI会占用app一些空间,例如状态栏、键盘、导航栏等,也就是说我们的app UI不会出现在系统UI之下,但从测试结果来看,为了占用状态栏空间或全屏,设置了上面的一些属性后,就会被系统UI覆盖。

为了规避不同系统版本以及系统状态差异造成的软件盘遮挡控件问题,需要一种统一的解决思路。这里给出一种思路:将UI界面设置为adjustPan, 通过监测输入法的弹出及隐藏来动态调整UI 的rootView的位置,如果输入法弹出时,焦点View(一般是EditText)在输入法上方显示,那么不做处理,如果输入法弹出时,焦点View被输入法遮挡,那么就rootView向上滚动,使输入框正好在输入法上方显示。

具体实现步骤如下:

  1. 定义并实现SoftKeyboardManager管理类,用于监听SoftKeyboard的弹出与隐藏状态,并通过接口方法通知出去。
    界面UI状态的变化通过ViewTreeObserver.OnGlobalLayoutListener接口来监听
public class SoftKeyboardManager  implements ViewTreeObserver.OnGlobalLayoutListener{
    public static final boolean DEBUG = false;
    public interface SoftKeyboardStateListener {
        void onSoftKeyboardOpened(int keyboardHeightInPx);
        void onSoftKeyboardClosed();
    }

    private final List<SoftKeyboardStateListener> listeners = new LinkedList<SoftKeyboardStateListener>();
    private final View activityRootView;
    private int lastSoftKeyboardHeightInPx;
    private boolean isSoftKeyboardOpened;
    
    public SoftKeyboardManager(View activityRootView) {
        this(activityRootView, false);
    }

    public SoftKeyboardManager(View activityRootView, boolean isSoftKeyboardOpened) {
        this.activityRootView = activityRootView;
        this.isSoftKeyboardOpened = isSoftKeyboardOpened;
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(this);

    }

    @Override

    public void onGlobalLayout() {

        final Rect r = new Rect();
        //r will be populated with the coordinates of your view that area still visible.
        activityRootView.getWindowVisibleDisplayFrame(r);
        final int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
        if (DEBUG){
            Log.d("SoftKeyboardStateHelper", "heightDiff:" + heightDiff);
        }
        if (!isSoftKeyboardOpened && heightDiff > 500) { // if more than 100 pixels, its probably a keyboard...
            isSoftKeyboardOpened = true;
            notifyOnSoftKeyboardOpened(heightDiff);
            //if (isSoftKeyboardOpened && heightDiff < 100)
        } else if (isSoftKeyboardOpened && heightDiff < 500) {
            isSoftKeyboardOpened = false;
            notifyOnSoftKeyboardClosed();
        }
    }

    public void setIsSoftKeyboardOpened(boolean isSoftKeyboardOpened) {
        this.isSoftKeyboardOpened = isSoftKeyboardOpened;
    }

    public boolean isSoftKeyboardOpened() {
        return isSoftKeyboardOpened;
    }

    /**
     * Default value is zero (0)
     *
     * @return last saved keyboard height in px
     */

    public int getLastSoftKeyboardHeightInPx() {

        return lastSoftKeyboardHeightInPx;

    }

    public void addSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
        listeners.add(listener);
    }

    public void removeSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
        listeners.remove(listener);
    }

    public void dispose(){
        activityRootView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
    }

    private void notifyOnSoftKeyboardOpened(int keyboardHeightInPx) {
        this.lastSoftKeyboardHeightInPx = keyboardHeightInPx;
        for (SoftKeyboardStateListener listener : listeners) {
            if (listener != null) {
                listener.onSoftKeyboardOpened(keyboardHeightInPx);
            }
        }
    }

    private void notifyOnSoftKeyboardClosed() {
        for (SoftKeyboardStateListener listener : listeners) {
            if (listener != null) {
                listener.onSoftKeyboardClosed();
            }
        }
    }
}
  1. 在主界面Activity或者Fragment中注册监听并做rootView的平移出来。
public class BaseFrameActivity extends FragmentActivity implements SoftKeyboardManager.SoftKeyboardStateListener{
private SoftKeyboardManager softKeyboardManager;
protected LinearLayout rootLl;
@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        super.setContentView(R.layout.root_container);
        rootLl = (LinearLayout)findViewById(R.id.root_rl);
        softKeyboardManager = new SoftKeyboardManager(rootLl);
        softKeyboardManager.addSoftKeyboardStateListener(this);
        
       ......
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        softKeyboardManager.removeSoftKeyboardStateListener(this);
        softKeyboardManager.dispose();
    }
    @Override
    public void onSoftKeyboardOpened(int keyboardHeightInPx) {
        if (SoftKeyboardManager.DEBUG){
            Log.d(TAG, "keyboardOpened, keyboardHeightInPx = "+keyboardHeightInPx);
        }
        Rect rect = new Rect();
        //获取root在窗体的可视区域
        rootLl.getWindowVisibleDisplayFrame(rect);
        //获取root在窗体的不可视区域高度(被其他View遮挡的区域高度)
        int rootInvisibleHeight = rootLl.getRootView().getHeight() - rect.bottom;
        //若不可视区域高度大于100,则键盘显示
        if (rootInvisibleHeight > 100) {
            int[] location = new int[2];
            //获取focusedView在窗体的坐标
            View focusedView = getCurrentFocus();
            if (focusedView instanceof EditText){
                focusedView.getLocationInWindow(location);
                int focusedViewPosY = location[1] + focusedView.getHeight();
                if (SoftKeyboardManager.DEBUG){
                    Log.d(TAG, "rect.bottom= "+rect.bottom+", focusedViewPosY = "+focusedViewPosY);
                    Log.i(TAG, "focused view need scroll up or down");
                }
                int srollHeight = focusedViewPosY - rect.bottom;
                if (SoftKeyboardManager.DEBUG){
                    Log.i(TAG, "srollHeight = "+srollHeight);
                }
                if (srollHeight>0) {//焦点被输入法遮挡,View向上滚动
                    rootLl.scrollTo(0, srollHeight);
                }
            }
        }
    }

    @Override
    public void onSoftKeyboardClosed() {
        if (SoftKeyboardManager.DEBUG){
            Log.d(TAG, "keyboardClosed ");
        }
        //输入法退出,root滚动到初始位置
        rootLl.scrollTo(0, 0);
    }
}

参考文献

softInputMode设置成adjustPan,键盘第二次弹出时会遮挡输入框
键盘弹起引起的Pannel布局发送变化问题解决
Android 软键盘的全面解析,让你不再怕控件被遮盖!

相关标签: Android基础