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

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。

于是,我仿照着给EditText的上下边距增加Padding值,如下:

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

注意高度需要设置为wrap_content,否则需要计算好高度。

我的理解就是: 键盘弹起的时候上端顶的位置由光标的高度和paddingTop和paddingBottom之和决定。也就是

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容