Android輸入法彈出時(shí)覆蓋輸入框問(wèn)題的解決方法
當(dāng)一個(gè)activity中含有輸入框時(shí),我們點(diǎn)擊輸入框,會(huì)彈出輸入法界面,整個(gè)界面的變化效果與manifest中對(duì)應(yīng)設(shè)置的android:windowSoftInputMode屬性有關(guān),一般可以設(shè)置的值如下,
<activity android:windowSoftInputMode=[ "stateUnspecified", "stateUnchanged”, "stateHidden", "stateAlwaysHidden”, "stateVisible", "stateAlwaysVisible”, "adjustUnspecified", "adjustResize”, "adjustPan"] …… >
具體怎么設(shè)置可以查看官方文檔。今天主要解決當(dāng)輸入法彈出時(shí)會(huì)覆蓋輸入框的問(wèn)題。
什么情況會(huì)覆蓋?
當(dāng)android的應(yīng)用中如果一個(gè)activity設(shè)置了全屏屬性Theme.Light.NotittleBar.Fullscreen或者設(shè)置了activity對(duì)應(yīng)的主題中android:windowTranslucentStatus屬性,設(shè)置方式為:<item name="android:windowTranslucentStatus">true</item>,這是如果對(duì)應(yīng)的頁(yè)面上含有輸入框,將會(huì)導(dǎo)致點(diǎn)擊輸入框時(shí)軟鍵盤(pán)彈出后鍵盤(pán)覆蓋輸入框,導(dǎo)致輸入框看不見(jiàn)。
為什么?
這其實(shí)是因?yàn)樵谌習(xí)r,adjustResize屬性已經(jīng)失效了,該問(wèn)題是系統(tǒng)的一個(gè)bug,參考鏈接。adjustResize不生效,那有沒(méi)有其他方法來(lái)解決吶? 這時(shí)我們可以設(shè)置adjust屬性為adjustPan屬性,該屬性不會(huì)失效,但是由于adjustPan會(huì)將頁(yè)面整體平移,以留出輸入法空間,會(huì)有一個(gè)抖動(dòng)的效果,體驗(yàn)很差,哪有沒(méi)有體驗(yàn)效果更好的方法吶?
解決方案:
如果跟布局采用FrameLayout,則可以復(fù)寫(xiě)一個(gè)自定義FrameLayout,同時(shí)設(shè)置FrameLayout的android:fitsSystemWindows屬性為true。xml設(shè)置如下
<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;
}
}
}
官方解決方案:
官方其實(shí)也發(fā)現(xiàn)了問(wèn)題,因此在android.support.design.internal下也重寫(xiě)了FrameLayout來(lái)解決該問(wèn)題,但是該類被標(biāo)記了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);
}
}
}
采用如上其中的任何一種方法就可以解決輸入法彈出后覆蓋輸入框問(wèn)題。
其他問(wèn)題?
在我們使用的過(guò)程中發(fā)現(xiàn)有用戶反饋,說(shuō)只要進(jìn)入我們采用該布局的頁(yè)面就會(huì)崩潰,我們查看了崩潰日志,發(fā)現(xiàn)有部分手機(jī)都使用了相同的一個(gè)安卓系統(tǒng),并且版本都是19,android4.4.x,一個(gè)被重寫(xiě)過(guò)的系統(tǒng),該系統(tǒng)的代碼加載方式被重寫(xiě)了。
為什么會(huì)崩潰?
我們代碼使用到了WindowInsets,該類是api 20才提供的,因此19的系統(tǒng)中其實(shí)是沒(méi)有該代碼的,但是該系統(tǒng)在xml的inflate的時(shí)候就解析了該類,導(dǎo)致classNotFound。
新的解決方案!
新的解決方案還是采用了上述的方式,不過(guò)會(huì)針對(duì)不同的版本寫(xiě)不一樣的布局,分別為api 20以上與20以下提供不同的布局,這是采用系統(tǒng)的限定符實(shí)現(xiàn)的,之后20以上的原樣采用上述的方式,20以下去掉onApplyWindowInsets復(fù)寫(xiě),這樣不同的版本加載不同的代碼就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;
}
}
總結(jié)到此整個(gè)解決方案已經(jīng)完成了,如過(guò)有更新的解決方案望大家分享。
- Android中系統(tǒng)默認(rèn)輸入法設(shè)置的方法(輸入法的顯示和隱藏)
- Android 顯示和隱藏輸入法實(shí)現(xiàn)代碼
- Android程序打開(kāi)和對(duì)輸入法的操作(打開(kāi)/關(guān)閉)
- Android實(shí)現(xiàn)輸入法彈出時(shí)把布局頂上去和登錄按鈕頂上去的解決方法
- Android 點(diǎn)擊屏幕空白處收起輸入法軟鍵盤(pán)(手動(dòng)打開(kāi))
- Android中Activity啟動(dòng)默認(rèn)不顯示輸入法解決方法
- Android監(jiān)聽(tīng)輸入法彈窗和關(guān)閉的實(shí)現(xiàn)方法
- Android開(kāi)發(fā)教程之獲取系統(tǒng)輸入法高度的正確姿勢(shì)
相關(guān)文章
手把手教你用ViewPager自定義實(shí)現(xiàn)Banner輪播
這篇文章主要手把手教你用ViewPager自定義實(shí)現(xiàn)Banner輪播,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09
Android編程實(shí)現(xiàn)Dialog窗體監(jiān)聽(tīng)的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)Dialog窗體監(jiān)聽(tīng)的方法,結(jié)合實(shí)例形式分析了Android針對(duì)Dialog對(duì)話框窗體事件監(jiān)聽(tīng)與響應(yīng)相關(guān)操作技巧,需要的朋友可以參考下2017-03-03
Android中利用NetworkInfo判斷網(wǎng)絡(luò)狀態(tài)時(shí)出現(xiàn)空指針(NullPointerException)問(wèn)題的解決
這篇文章主要介紹了Android中利用NetworkInfo判斷網(wǎng)絡(luò)狀態(tài)時(shí)出現(xiàn)空指針(NullPointerException)問(wèn)題的解決方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11
擁抱kotlin之如何習(xí)慣使用kotlin高階函數(shù)
這篇文章主要給大家介紹了關(guān)于擁抱kotlin之如何習(xí)慣使用kotlin高階函數(shù)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用kotlin具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
如何在android中制作一個(gè)方向輪盤(pán)詳解
這篇文章主要給大家介紹了關(guān)于如何在android中制作一個(gè)方向輪盤(pán)的相關(guān)資料,這個(gè)是在手游領(lǐng)域中很常見(jiàn)的用于控制方向的輪盤(pán),文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-09-09
Android實(shí)現(xiàn)隨手指移動(dòng)小球
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)隨手指移動(dòng)小球,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-08-08

