Android输入法弹出时覆盖输入框问题的解决方法

作者:xueshanhaizi 时间:2023-02-06 06:36:52 

 当一个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,参考链接。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;
   }
 }

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

标签:Android,输入法,输入框
0
投稿

猜你喜欢

  • Java map存放数组并取出值代码详解

    2023-06-27 23:32:18
  • Spring Cloud 网关服务 zuul 动态路由的实现方法

    2022-04-09 20:17:46
  • 梳理总结Java static关键字的方法作用

    2023-06-09 04:06:17
  • Entity Framework主从表的增删改

    2023-10-05 18:56:27
  • C#异步下载文件

    2021-10-05 03:23:40
  • C# WinForm实现图片浏览器

    2022-04-12 23:28:00
  • spring BeanProcessor接口详解

    2021-12-26 07:32:17
  • WinForm通过操作注册表实现限制软件使用次数的方法

    2023-07-27 15:39:57
  • Android短信备份及数据插入实现代码解析

    2023-07-25 02:41:12
  • Struts 2中的constant配置详解

    2023-11-10 08:18:18
  • 初识Java基础之数据类型与运算符

    2021-10-13 12:20:32
  • Java实现双向循环链表

    2023-11-08 04:14:40
  • Java如何根据不同系统动态获取换行符和盘分割符

    2022-02-27 10:33:10
  • java编译器和JVM的区别

    2023-07-18 20:34:48
  • 超炫酷的WPF实现Loading控件效果

    2023-11-19 23:53:20
  • C#实现获取Excel中图片所在坐标位置

    2021-10-14 23:36:18
  • Android View自定义锁屏图案

    2021-11-21 14:48:20
  • Android popupWindow弹出窗体实现方法分析

    2021-08-08 08:07:42
  • Hadoop的安装与环境搭建教程图解

    2022-06-28 16:19:47
  • idea全局搜索快捷键超详细总结(推荐!)

    2021-08-12 20:16:18
  • asp之家 软件编程 m.aspxhome.com