本文来自网易云社区

作者:孙有军

当一个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+款云产品!

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

相关文章:
【推荐】 Kafka实践、升级和新版本(0.10)特性预研
【推荐】 分布式存储系统可靠性系列五:副本放置算法&CopySetReplication

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

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

    原文连接: http://www.devdiv.com/thread-70159-1-1.html 实现以下三个方法,如果弹出的键盘会遮住输入框 ,整体的界面会向上移动,这样就不会遮住输入框了.自己增 ...

  2. Android如何避免输入法弹出时遮挡住按钮或输入框

    在AndroidManifest.xml中为对应的activity添加android:windowSoftInputMode="adjustResize" 在AndroidMani ...

  3. 修复ios上第三方输入法弹出时输入键盘盖住网页没有进行相应滚动从而盖住表单输入框的问题

    fixIME(); function fixIME(){ scroll_y = 100;  // 如果键盘弹起后 网页window对象的卷起小于此值,说明没有自动卷起 单位:px timer = 50 ...

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

    phonegap android 当页面内容比较多,表单超出屏幕范围时,点击输入,输入法会遮盖住表单框,而且无法向上滑动. 经过测试发现,是由于config.xml中设置了 FullScreen 的全 ...

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

    之前我们在使用vue进行 h5 表单录入的过程中,遇到了Android软键盘弹出,覆盖 h5页面 输入框 问题,在此进行回顾并分享给大家: 系统:Android 条件:当输入框在可视区底部或者偏下的位 ...

  6. Android中软键盘弹出时关于布局的问题

     当在Android的layout设计里面如果输入框过多,则在输入弹出软键盘的时候,下面的输入框会有一部分被软件盘挡住,从而不能获取焦点输入. 解决办法: 方法一:在你的activity中的oncre ...

  7. Android软键盘弹出时布局问题

    最近项目需要做一个类似聊天室的模块,基于Socket实现的,这部分稍后一段时间再做总结,功能上的相关点都实现了小例子也做出来了,最后发现一个比较腻歪的问题就是软键盘弹出时总是会把标题“挤出”屏幕,(无 ...

  8. Android 软键盘弹出时把原来布局顶上去的解决方法

    键盘弹出时,会将布局底部的导航条顶上去. 解决办法: 在mainfest.xml中,在和导航栏相关的activity中加: <activity            android:name=& ...

  9. Android 如何解决dialog弹出时无法捕捉Activity的back事件

    Android 如何解决dialog弹出时无法捕捉Activity的back事件 在一些情况下,我们需要捕捉back键事件,然后在捕捉到的事件里写入我们需要进行的处理,通常可以采用下面三种办法捕捉到b ...

随机推荐

  1. 使用Docx.dll插入图片在Centos 7 上报错:system.DllNotFound:libgdiplus.so

    熬了N天,终于把WORD 文档打开替换.插入图片完好,部署,本机测试没有问题 可是一放到服务器(Centos 7) 就提示下面的错误: 度娘一下: https://www.cnblogs.com/xi ...

  2. eclipse之插件添加

    在ftp.properties文件中,中文出现十六进制显示情况,如下: 解决该问题只需要在eclipse中下载一个插件即可解决, 步骤: help  ---> install new softw ...

  3. Regexp:常用的几个正则表达式

    1.isEmail /** * * @desc 判断是否为邮箱地址 * @param {String} str * @return {Boolean} */ function isEmail(str) ...

  4. C# 获取当前文件、文件夹的路径及操作环境变量

    一.获取当前文件的路径 1.   System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName      获取模块的完整路径,包 ...

  5. 本地eclipse启动tomcat后无法访问

    转自博文:http://blog.csdn.net/wqjsir/article/details/7169838/ 症状: tomcat在eclipse里面能正常启动,而在浏览器中访问http://l ...

  6. 转: ZigBee/Z-Stack CC2530实现低功耗运行的配置简介

    转: ZigBee/Z-Stack CC2530实现低功耗运行的配置简介http://bbs.elecfans.com/jishu_914377_1_1.html(出处: 中国电子技术论坛) 设备支持 ...

  7. win10搜索不到蓝牙设备

    多半是驱动不兼容的问题. 解决方法: 此电脑右键,设备管理器,然后将蓝牙下的驱动,右键.卸载设备. 安装驱动精灵,会自动检测到缺少蓝牙驱动,安装即可.

  8. 为PyCharm添加不同解释器

    正常大家会使用Python官网的安装包,即Python.由于自己尝试了下IronPython,但打开PyCharm查看配置,发现并没有IronPython的解释器,这个需要自己加进去,如图: &quo ...

  9. 【转】iOS学习笔记(八)——iOS网络通信http之NSURLConnection

    移动互联网时代,网络通信已是手机终端必不可少的功能.我们的应用中也必不可少的使用了网络通信,增强客户端与服务器交互.这一篇提供了使用NSURLConnection实现http通信的方式. NSURLC ...

  10. SqlServer2000事件探测器的使用

    由于公司自己开发的项目,需要与第三方软件数据库对接.我们项目用的数据库是MySQL而第三方用的是sqlserver2000.项目框架用的thinkphp5.0,通过thinkPHP新建一个模块,单独连 ...