当一个activity中含有输入框时,我们点击输入框,会d出输入法界面,整个界面的变化效果与manifest中对应设置的androID:windowsoftinputMode属性有关,一般可以设置的值如下,
<activity androID:windowsoftinputMode=["stateUnspecifIEd","stateUnchanged”,"stateHIDden","statealwaysHIDden”,"stateVisible","statealwaysVisible”,"adjustUnspecifIEd","adjustResize”,"adjustPan"] …… >
具体怎么设置可以查看官方文档。今天主要解决当输入法d出时会覆盖输入框的问题。
什么情况会覆盖?
当androID的应用中如果一个activity设置了全屏属性theme.light.Notittlebar.Fullscreen或者设置了activity对应的主题中androID:windowTranslucentStatus属性,设置方式为:<item name="androID:windowTranslucentStatus">true</item>,这是如果对应的页面上含有输入框,将会导致点击输入框时软键盘d出后键盘覆盖输入框,导致输入框看不见。
为什么?
这其实是因为在全屏时,adjustResize属性已经失效了,该问题是系统的一个BUG,参考链接。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,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,0); } public ScrimInsetsFrameLayout(Context context,int defStyleAttr) { super(context,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,wIDth,mInsets.top); mInsetForeground.setBounds(mTempRect); mInsetForeground.draw(canvas); // Bottom mTempRect.set(0,height - mInsets.bottom,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,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); } }}
采用如上其中的任何一种方法就可以解决输入法d出后覆盖输入框问题。
其他问题?
在我们使用的过程中发现有用户反馈,说只要进入我们采用该布局的页面就会崩溃,我们查看了崩溃日志,发现有部分手机都使用了相同的一个安卓系统,并且版本都是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,insets.getSystemWindowInsetBottom())); } else { return insets; } }
总结到此整个解决方案已经完成了,如过有更新的解决方案望大家分享。
总结以上是内存溢出为你收集整理的Android输入法d出时覆盖输入框问题的解决方法全部内容,希望文章能够帮你解决Android输入法d出时覆盖输入框问题的解决方法所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)