美文网首页
android 键盘遮挡、显示隐藏、默认焦点等问题

android 键盘遮挡、显示隐藏、默认焦点等问题

作者: orzangleli | 来源:发表于2017-09-01 19:05 被阅读727次

android 中关于键盘和焦点的问题,有时候处理不好,真的让人抓狂,昨天在实现需求的时候被键盘和焦点的问题搞得难受,今天把昨天遇到的问题总结记录一下。

1. 键盘隐藏和遮挡界面

1.1 键盘隐藏

在一个常见的输入信息的界面(保存联系人信息)为例,界面如图所示。

有时候我们需要在进入界面的时候隐藏输入法键盘,有两种方法:

  1. 在AndroidManifest.xml中对相应的Activity添加一下代码:
android:windowSoftInputMode="stateHidden"
  1. 在java代码中调用:
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);

多说一句:

网上较多的实现方式是通过InputMethodManager实现:

Timer timer=new Timer();
timer.schedule(new TimerTask() {

    public void run() {
        InputMethodManager inputMethodManager=(InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        inputMethodManager.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
    }
}, 2000);

但是在我的测试中,并没有实现我的需求,这个代码会在进入界面时弹出键盘,然后在隐藏键盘。而我的需求是,进入界面时就不能显示键盘,所以不能通过InputMethodManager实现,只能通过windowSoftInputMode实现。

1.2 键盘遮挡

键盘隐藏已经解决了,再看看,又出现一个问题。如果界面中内容较多,比如上图中备注1的输入框,点击弹起键盘时,默认情况下,键盘会把界面中的内容整体平移推到上面。如图,连标题栏也被挤到屏幕外面去了。

以下内容摘自 彻底搞定Android开发中软键盘的常见问题

Android定义了一个属性,名字为windowSoftInputMode, 这个属性用于设置Activity主窗口与软键盘的交互模式,用于避免软键盘遮挡内容的问题。

我们可以在AndroidManifet.xml中对Activity进行设置。如:android:windowSoftInputMode=”stateUnchanged|adjustPan”。

该属性可选的值有两部分,一部分为软键盘的状态控制,控制软键盘是隐藏还是显示,另一部分是Activity窗口的调整,以便腾出空间展示软键盘。
android:windowSoftInputMode的属性设置必须是下面中的一个值,或一个”state”值加一个”adjust”值的组合,各个值之间用 | 分开。

  • stateUnspecified-未指定状态:当我们没有设置android:windowSoftInputMode属性的时候,软件默认采用的就是这种交互方式,系统会根据界面采取相应的软键盘的显示模式。
  • stateUnchanged-不改变状态:当前界面的软键盘状态,取决于上一个界面的软键盘状态,无论是隐藏还是显示。
  • stateHidden-隐藏状态:当设置该状态时,软键盘总是被隐藏,不管是否有输入的需求。
  • stateAlwaysHidden-总是隐藏状态:当设置该状态时,软键盘总是被隐藏,和stateHidden不同的是,当我们跳转到下个界面,如果下个页面的软键盘是显示的,而我们再次回来的时候,软键盘就会隐藏起来。
  • stateVisible-可见状态:当设置为这个状态时,软键盘总是可见的,即使在界面上没有输入框的情况下也可以强制弹出来出来。
  • stateAlwaysVisible-总是显示状态:当设置为这个状态时,软键盘总是可见的,和stateVisible不同的是,当我们跳转到下个界面,如果下个页面软键盘是隐藏的,而我们再次回来的时候,软键盘就会显示出来。
  • adjustUnspecified-未指定模式:设置软键盘与软件的显示内容之间的显示关系。当你跟我们没有设置这个值的时候,这个选项也是默认的设置模式。在这中情况下,系统会根据界面选择不同的模式。
  • adjustResize-调整模式:该模式下窗口总是调整屏幕的大小用以保证软键盘的显示空间;这个选项不能和adjustPan同时使用,如果这两个属性都没有被设置,系统会根据窗口中的布局自动选择其中一个。
  • adjustPan-默认模式:该模式下通过不会调整来保证软键盘的空间,而是采取了另外一种策略,系统会通过布局的移动,来保证用户要进行输入的输入框肯定在用户的视野范围里面,从而让用户可以看到自己输入的内容。

因为,在默认情况下,系统使用的adjustPan,这种方式平移界面中的内容,与之相当应另一种常用的模式是adjustResize,意为调整(压缩)模式,他会把界面中的空间压缩,如果有ScrollView,会让ScrollView自动滚动到合适的位置,以完全显示键盘。

由于我的需求不是使用adjustPan模式,所以我直接使用adjustResize模式,AndroidManifest.xml中的声明如下:

android:windowSoftInputMode="stateHidden|adjustResize"

然后就出现了键盘遮挡输入框的现象,如图所示,之前可以看到[备注1]和[备注2]被挡住了。

为解决这种情况,需要给顶层布局嵌入一个ScrollView,然后再来测试下。

截图如下所示,可以看到“基本正常”了,点击输入框会把空白区域压缩,并滑动ScrollView,没有什么大问题,但是只要仔细观察,还是可以看出一点不合理的地方,键盘顶着输入法光标的下边缘,但是实际上ScrollView可以再往下滑动一点点的。

1.3 解决输入框的一点点遮挡的问题

输入法键盘遮挡了[备注1]的EditText下面一点点,实际上键盘是在光标的正下方紧贴着显示地,但是此时的UI交互体验较差,应该让键盘弹起时,ScrollView往上多滑动一点点。

1.3.1 fullScroll

刚开始使用比较笨的方法,检测EditText获得焦点时,把ScrollView滑动到最底端,在网上搜索看到ScrollView滑动到最底层的代码是:

scrollView.fullScroll(View.FOCUS_DOWN);

完整代码

extra.setOnFocusChangeListener(new View.OnFocusChangeListener() {
    @Override
    public void onFocusChange(View view, boolean b) {
        if (b) {
            scrollView.fullScroll(View.FOCUS_DOWN);
        }
    }
});

执行代码,发现[备注1]的输入框无法输入文字了,来看看fullScroll最终调用的地方:

public boolean fullScroll(int direction) {
    boolean down = direction == View.FOCUS_DOWN;
    int height = getHeight();

    mTempRect.top = 0;
    mTempRect.bottom = height;

    if (down) {
        int count = getChildCount();
        if (count > 0) {
            View view = getChildAt(count - 1);
            mTempRect.bottom = view.getBottom() + mPaddingBottom;
            mTempRect.top = mTempRect.bottom - height;
        }
    }

    return scrollAndFocus(direction, mTempRect.top, mTempRect.bottom);
}

最后会执行scrollAndFocus(direction, mTempRect.top, mTempRect.bottom),

private boolean scrollAndFocus(int direction, int top, int bottom) {
    boolean handled = true;

    int height = getHeight();
    int containerTop = getScrollY();
    int containerBottom = containerTop + height;
    boolean up = direction == View.FOCUS_UP;

    View newFocused = findFocusableViewInBounds(up, top, bottom);
    if (newFocused == null) {
        newFocused = this;
    }

    if (top >= containerTop && bottom <= containerBottom) {
        handled = false;
    } else {
        int delta = up ? (top - containerTop) : (bottom - containerBottom);
        doScrollY(delta);
    }

    if (newFocused != findFocus()) newFocused.requestFocus(direction);

    return handled;
}

会把ScrollView当前区域中的可以获焦的View,请求焦点。导致本来属于[备注1]的焦点又失去了,最终的现象就是[备注1]里无法输入内容。

后来将fullScroll方法替换成scrollTo或者scrollBy方法,直接让ScrollView滑动一个较大值,滑动到底部。后来又发现通过监听输入框焦点变化来调整ScrollView又不符合要求,在第一次点击输入框时,ScrollView可以滑动到指定位置,但是此时将键盘收起,再点击输入框弹起键盘时,因为焦点没有发生变化,所以又不能滑动到指定位置了,在网络上搜索,有一个方法
activity_main.getViewTreeObserver().addOnGlobalLayoutListener将整个布局添加鉴定,键盘弹起来之后可见区域的高度会变小,并认为此现象就是键盘弹起导致的,然后再去滑动ScrollView。看起来是可以了,但是没有从根本上解决问题,可见区域高度变化超过m,就认为是键盘弹起来了,程序中m写的是100,但是不能保证所有的输入法高度都超过100吧?

于是后来放弃了这种监听键盘弹起,再通过代码滑动ScrollView的方法。

通过实验,我发现使用默认的EditText竟然不会出现键盘遮挡一部分输入框的现象,如图所示。

而两者的区别仅仅在于,出问题的EditText设置了背景:

<EditText
    android:id="@+id/extra"
    android:layout_width="0dp"
    android:layout_height="50dp"
    android:layout_weight="1"
    android:background="#aaa"
    android:hint="备注1"
    android:gravity="right|center_vertical" />
<EditText
    android:id="@+id/extra"
    android:layout_width="0dp"
    android:layout_height="50dp"
    android:layout_weight="1"
    android:hint="备注1"
    android:gravity="right|center_vertical" />

通过老王的提示,发现EditText默认使用的背景是:

android:background="@android:drawable/edit_text"

而edit_text.xml中使用的背景是一张.9图片,名为@drawable/textfield_default,在SDK的目录下搜索图片,找到图片,在AS中打开,可以看到.9图片设置的显示的内容区域上面和下面都有padding。

这里理解的不知道对不对,以后在验证一下。

相关文章

网友评论

      本文标题:android 键盘遮挡、显示隐藏、默认焦点等问题

      本文链接:https://www.haomeiwen.com/subject/qbmkjxtx.html