Android输入法弹出时覆盖输入框问题

本文来自网易云社区

作者:孙有军

当一个activity中含有输入框时,我们点击输入框,会弹出输入法界面,整个界面的变化效果与manifest中对应设置的android:windowSoftInputMode属性有关,一般可以设置的值如下,

<activity android:windowSoftInputMode=["stateUnspecified","stateUnchanged”, 
"stateHidden",
"stateAlwaysHidden”, 
"stateVisible","stateAlwaysVisible”, 
"adjustUnspecified",
"adjustResize”, 
"adjustPan"] …… >
   具体怎么设置可以查看官方文档。今天主要解决当输入法弹出时会覆盖输入框的问题。

什么情况会覆盖?

   当android的应用中如果一个activity设置了全屏属性Theme.Light.NotittleBar.Fullscreen或者设置了activity对应的主题中android:windowTranslucentStatus属性,设置方式为:`<item name="android:windowTranslucentStatus">true</item>`,这是如果对应的页面上含有输入框,将会导致点击输入框时软键盘弹出后键盘覆盖输入框,导致输入框看不见。

为什么?

   这其实是因为在全屏时,adjustResize属性已经失效了,该问题是系统的一个bug,[参考链接](http://code.google.com/p/android/issues/detail?id=5497)。adjustResize不生效,那有没有其他方法来解决呐? 这时我们可以设置adjust属性为adjustPan属性,该属性不会失效,但是由于adjustPan会将页面整体平移,以留出输入法空间,会有一个抖动的效果,体验很差,哪有没有体验效果更好的方法呐?

解决方案:

   如果跟布局采用FrameLayout,则可以复写一个自定义FrameLayout,同时设置FrameLayout的android:fitsSystemWindows属性为true。xml设置如下
<com.sample.ui.widget.InsetFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
                   android:layout_width="match_parent"
                   android:layout_height="match_parent"
                   android:fitsSystemWindows="true”>
   我们自定义该FrameLayout为InsetFrameLayout,InsetFrameLayout 代码如下:
public final class InsetFrameLayout extends FrameLayout {    private int[] mInsets = new int[4];    public InsetFrameLayout(Context context) {        super(context);
    }    public InsetFrameLayout(Context context, AttributeSet attrs) {        super(context, attrs);
    }    public InsetFrameLayout(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);
    }    public final int[] getInsets() {        return mInsets;
    }    @Override
    protected final boolean fitSystemWindows(Rect insets) {        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {            // Intentionally do not modify the bottom inset. For some reason,
            // if the bottom inset is modified, window resizing stops working.

            mInsets[0] = insets.left;
            mInsets[1] = insets.top;
            mInsets[2] = insets.right;

            insets.left = 0;
            insets.top = 0;
            insets.right = 0;
        }        return super.fitSystemWindows(insets);
    }    @Override
    public final WindowInsets onApplyWindowInsets(WindowInsets insets) {        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
            mInsets[0] = insets.getSystemWindowInsetLeft();
            mInsets[1] = insets.getSystemWindowInsetTop();
            mInsets[2] = insets.getSystemWindowInsetRight();            return super.onApplyWindowInsets(insets.replaceSystemWindowInsets(0, 0, 0,
                    insets.getSystemWindowInsetBottom()));
        } else {            return insets;
        }
    }

}

官方解决方案:

   官方其实也发现了问题,因此在android.support.design.internal下也重写了FrameLayout来解决该问题,但是该类被标记了hide。
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */package android.support.design.internal;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Rect;import android.graphics.drawable.Drawable;import android.support.annotation.NonNull;import android.support.design.R;import android.support.v4.view.ViewCompat;import android.support.v4.view.WindowInsetsCompat;import android.util.AttributeSet;import android.view.View;import android.widget.FrameLayout;/**
 * @hide
 */public class ScrimInsetsFrameLayout extends FrameLayout {    private Drawable mInsetForeground;    private Rect mInsets;    private Rect mTempRect = new Rect();    public ScrimInsetsFrameLayout(Context context) {        this(context, null);
    }    public ScrimInsetsFrameLayout(Context context, AttributeSet attrs) {        this(context, attrs, 0);
    }    public ScrimInsetsFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        final TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.ScrimInsetsFrameLayout, defStyleAttr,
                R.style.Widget_Design_ScrimInsetsFrameLayout);
        mInsetForeground = a.getDrawable(R.styleable.ScrimInsetsFrameLayout_insetForeground);
        a.recycle();
        setWillNotDraw(true); // No need to draw until the insets are adjusted

        ViewCompat.setOnApplyWindowInsetsListener(this,                new android.support.v4.view.OnApplyWindowInsetsListener() {                    @Override
                    public WindowInsetsCompat onApplyWindowInsets(View v,
                            WindowInsetsCompat insets) {                        if (null == mInsets) {
                            mInsets = new Rect();
                        }
                        mInsets.set(insets.getSystemWindowInsetLeft(),
                                insets.getSystemWindowInsetTop(),
                                insets.getSystemWindowInsetRight(),
                                insets.getSystemWindowInsetBottom());
                        setWillNotDraw(mInsets.isEmpty() || mInsetForeground == null);
                        ViewCompat.postInvalidateOnAnimation(ScrimInsetsFrameLayout.this);                        return insets.consumeSystemWindowInsets();
                    }
                });
    }    @Override
    public void draw(@NonNull Canvas canvas) {        super.draw(canvas);        int width = getWidth();        int height = getHeight();        if (mInsets != null && mInsetForeground != null) {            int sc = canvas.save();
            canvas.translate(getScrollX(), getScrollY());            // Top
            mTempRect.set(0, 0, width, mInsets.top);
            mInsetForeground.setBounds(mTempRect);
            mInsetForeground.draw(canvas);            // Bottom
            mTempRect.set(0, height - mInsets.bottom, width, height);
            mInsetForeground.setBounds(mTempRect);
            mInsetForeground.draw(canvas);            // Left
            mTempRect.set(0, mInsets.top, mInsets.left, height - mInsets.bottom);
            mInsetForeground.setBounds(mTempRect);
            mInsetForeground.draw(canvas);            // Right
            mTempRect.set(width - mInsets.right, mInsets.top, width, height - mInsets.bottom);
            mInsetForeground.setBounds(mTempRect);
            mInsetForeground.draw(canvas);

            canvas.restoreToCount(sc);
        }
    }    @Override
    protected void onAttachedToWindow() {        super.onAttachedToWindow();        if (mInsetForeground != null) {
            mInsetForeground.setCallback(this);
        }
    }    @Override
    protected void onDetachedFromWindow() {        super.onDetachedFromWindow();        if (mInsetForeground != null) {
            mInsetForeground.setCallback(null);
        }
    }

}
   采用如上其中的任何一种方法就可以解决输入法弹出后覆盖输入框问题。

其他问题?

   在我们使用的过程中发现有用户反馈,说只要进入我们采用该布局的页面就会崩溃,我们查看了崩溃日志,发现有部分手机都使用了相同的一个安卓系统,并且版本都是19,android4.4.x,一个被重写过的系统,该系统的代码加载方式被重写了。

为什么会崩溃?

   我们代码使用到了WindowInsets,该类是api 20才提供的,因此19的系统中其实是没有该代码的,但是该系统在xml的inflate的时候就解析了该类,导致classNotFound。

新的解决方案!

   新的解决方案还是采用了上述的方式,不过会针对不同的版本写不一样的布局,分别为api 20以上与20以下提供不同的布局,这是采用系统的限定符实现的,之后20以上的原样采用上述的方式,20以下去掉onApplyWindowInsets复写,这样不同的版本加载不同的代码就OK了。
 @Override
    public final WindowInsets onApplyWindowInsets(WindowInsets insets) {        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
            mInsets[0] = insets.getSystemWindowInsetLeft();
            mInsets[1] = insets.getSystemWindowInsetTop();
            mInsets[2] = insets.getSystemWindowInsetRight();            return super.onApplyWindowInsets(insets.replaceSystemWindowInsets(0, 0, 0,
                    insets.getSystemWindowInsetBottom()));
        } else {            return insets;
        }
    }

总结

   到此整个解决方案已经完成了,如过有更新的解决方式望大家分享。

网易云免费体验馆,0成本体验20+款云产品!

更多网易研发、产品、运营经验分享请访问网易云社区

相关文章:
【推荐】 SpringBoot入门(四)——自动配置
【推荐】 网易云易盾朱星星:最容易被驳回的10大APP过检项

原文地址:https://www.cnblogs.com/163yun/p/9698962.html

时间: 2024-10-12 08:34:16

Android输入法弹出时覆盖输入框问题的相关文章

输入法弹出时原先布局的变化控制

1.让输入法弹出时输入框被顶上去,listView不做任何变化,此时edittext与listview是线性垂直布局 activity属性:stateHidden是不自动弹出键盘 android:windowSoftInputMode="adjustResize|stateHidden" Listview属性: android:transcriptMode="normal" Done!

5、输入法弹出时布局错乱的解决方案

现在有这样一个布局: 1 <?xml version="1.0" encoding="utf-8"?> 2 3 <com.clientMenu.CustomScrollView 4 xmlns:android="http://schemas.android.com/apk/res/android" 5 android:layout_width="match_parent" 6 android:layout_h

UITextField控件处理键盘弹出时遮住输入框的问题

原文连接: http://www.devdiv.com/thread-70159-1-1.html 实现以下三个方法,如果弹出的键盘会遮住输入框 ,整体的界面会向上移动,这样就不会遮住输入框了.自己增加UITextFieldDelegate委托. 只适合iPhone,如果想要支持iPad,只要把216改成iPad上面键盘的高度即可. 1 - (void)keyboardWillShow:(NSNotification *)noti 2 { 3 //键盘输入的界面调整 4 //键盘的高度 5 fl

phonegap android 输入法弹出会遮盖表单框的解决办法

phonegap android 当页面内容比较多,表单超出屏幕范围时,点击输入,输入法会遮盖住表单框,而且无法向上滑动. 经过测试发现,是由于config.xml中设置了 FullScreen 的全屏设置造成的.只需将 res/xml/config.xml中的 <preference name="fullscreen" value="true" /> 改为 <preference name="fullscreen" value

Android软键盘弹出,覆盖h5页面输入框问题

之前我们在使用vue进行 h5 表单录入的过程中,遇到了Android软键盘弹出,覆盖 h5页面 输入框 问题,在此进行回顾并分享给大家: 系统:Android 条件:当输入框在可视区底部或者偏下的位置 触发条件:输入框获取焦点,弹出软键盘 表现:软键盘 覆盖 h5页面中的输入框 问题分析: 1.发现问题:当前页面中box为flex布局,内容为上下固定高,中间自适应(中间区域内容过多会出现滚动条,input框在wrapper的底部),input获取焦点,手机键盘弹出,input未上移到可视区内,

Android软键盘弹出时布局问题

最近项目需要做一个类似聊天室的模块,基于Socket实现的,这部分稍后一段时间再做总结,功能上的相关点都实现了小例子也做出来了,最后发现一个比较腻歪的问题就是软键盘弹出时总是会把标题“挤出”屏幕,(无论标题是写在布局中还是仿照theme的方式添加到style中),输入时有失观赏如下图:      隐隐感觉之前项目一直有类似问题,只不过一般只在登陆界面,无伤大雅,用户输入后一掠而过,可以忽略,但这个页面这么处理确实不太美观. 查了下此类问题大致两种思路解决:         一.调整布局在底层使用

Android强制弹出,隐藏输入法.

当我们弹出一个Dialog时候,假设这个Dialog须要输入数据,然后确定后又须要关闭输入法,一般系统的hide,跟show方法总会有各种问题,最霸道的解决方法就是写一个定时器,定时弹出或者关闭输入法. import java.util.Timer; import java.util.TimerTask; import android.content.Context; import android.view.View; import android.view.inputmethod.InputM

Android 软键盘弹出时布局内指定内容上移实现及问题解决

Android SDK目前提供的软键盘弹出模式接口只有两种: 一是弹出时自动回冲界面,将所有元素上顶, 一种则是不重绘界面,直接将控件元素遮住,   没有其他模式,如果想实现其他效果,光使用系统接口是不行的.   解决方法:   第一步:给想要被顶上去的内容嵌套一个 ScrollView :   <ScrollView     android:layout_width="match_parent"     android:layout_height="0dp"

转载:Android 如何解决dialog弹出时无法捕捉Activity的back事件

在一些情况下,我们需要捕捉back键事件,然后在捕捉到的事件里写入我们需要进行的处理,通常可以采用下面三种办法捕捉到back事件: 1)重写onKeyDown或者onKeyUp方法 2)重写onBackPressed方法 3)重写dispatchKeyEvent方法 这三种办法有什么区别在这里不进行阐述,有兴趣的朋友可以查阅相关资料. 然而在有dialog弹出时,想捕捉back键的事件的话,上述三种办法都无法实现.因为上述方法是重写在activity里面的,也就是说当activity是当前焦点时