博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android输入法弹出时覆盖输入框问题
阅读量:6504 次
发布时间:2019-06-24

本文共 7900 字,大约阅读时间需要 26 分钟。

本文来自网易云社区

作者:孙有军

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

复制代码
具体怎么设置可以查看官方文档。今天主要解决当输入法弹出时会覆盖输入框的问题。复制代码

什么情况会覆盖?

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

为什么?

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

解决方案:

如果跟布局采用FrameLayout,则可以复写一个自定义FrameLayout,同时设置FrameLayout的android:fitsSystemWindows属性为true。xml设置如下复制代码
 
我们自定义该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+款云产品!

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

相关文章:

【推荐】

转载地址:http://puqyo.baihongyu.com/

你可能感兴趣的文章
多线程问题(JVM重排序)
查看>>
LeetCode 459 Repeated Substring Pattern
查看>>
POJ 3268 Silver Cow Party
查看>>
进程线程及堆栈关系的总结
查看>>
Android Camera开发:使用TextureView和SurfaceTexture预览Camera 基础拍照demo
查看>>
EMLS项目推进思考
查看>>
Eclipse快捷键 10个最有用的快捷键
查看>>
2018-2019-1 20165302 实验五 通讯协议设计
查看>>
快速寻找满足条件的两个数
查看>>
Java开发者必读的10篇精选优秀技术文章
查看>>
Oracle宣布提供新的Java支持价格体系
查看>>
IP路由与转发
查看>>
专访朱诗雄:Apache Spark中的全新流式引擎Structured Streaming
查看>>
AI+社交,快手商业化落地之道
查看>>
re:Invent大会第四天:为什么Lambda值得你更多关注?
查看>>
11个Visual Studio代码性能分析工具
查看>>
vue2.0一起在懵逼的海洋里越陷越深(四)
查看>>
为什么Python发展得如此之快?
查看>>
Kubernetes日志分析利器:Elassandra部署使用指南
查看>>
与Susan Fowler探讨生产就绪微服务之问答
查看>>