Android輸入法彈出時(shí)覆蓋輸入框問題的解決方法
當(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ì)覆蓋輸入框的問題。
什么情況會(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)的頁面上含有輸入框,將會(huì)導(dǎo)致點(diǎn)擊輸入框時(shí)軟鍵盤彈出后鍵盤覆蓋輸入框,導(dǎo)致輸入框看不見。
為什么?
這其實(shí)是因?yàn)樵谌習(xí)r,adjustResize屬性已經(jīng)失效了,該問題是系統(tǒng)的一個(gè)bug,參考鏈接。adjustResize不生效,那有沒有其他方法來解決吶? 這時(shí)我們可以設(shè)置adjust屬性為adjustPan屬性,該屬性不會(huì)失效,但是由于adjustPan會(huì)將頁面整體平移,以留出輸入法空間,會(huì)有一個(gè)抖動(dòng)的效果,體驗(yàn)很差,哪有沒有體驗(yàn)效果更好的方法吶?
解決方案:
如果跟布局采用FrameLayout,則可以復(fù)寫一個(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)了問題,因此在android.support.design.internal下也重寫了FrameLayout來解決該問題,但是該類被標(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); } } }
采用如上其中的任何一種方法就可以解決輸入法彈出后覆蓋輸入框問題。
其他問題?
在我們使用的過程中發(fā)現(xiàn)有用戶反饋,說只要進(jìn)入我們采用該布局的頁面就會(huì)崩潰,我們查看了崩潰日志,發(fā)現(xiàn)有部分手機(jī)都使用了相同的一個(gè)安卓系統(tǒng),并且版本都是19,android4.4.x,一個(gè)被重寫過的系統(tǒng),該系統(tǒng)的代碼加載方式被重寫了。
為什么會(huì)崩潰?
我們代碼使用到了WindowInsets,該類是api 20才提供的,因此19的系統(tǒng)中其實(shí)是沒有該代碼的,但是該系統(tǒng)在xml的inflate的時(shí)候就解析了該類,導(dǎo)致classNotFound。
新的解決方案!
新的解決方案還是采用了上述的方式,不過會(huì)針對(duì)不同的版本寫不一樣的布局,分別為api 20以上與20以下提供不同的布局,這是采用系統(tǒng)的限定符實(shí)現(xiàn)的,之后20以上的原樣采用上述的方式,20以下去掉onApplyWindowInsets復(fù)寫,這樣不同的版本加載不同的代碼就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)完成了,如過有更新的解決方案望大家分享。
- Android中系統(tǒng)默認(rèn)輸入法設(shè)置的方法(輸入法的顯示和隱藏)
- Android 顯示和隱藏輸入法實(shí)現(xiàn)代碼
- Android程序打開和對(duì)輸入法的操作(打開/關(guān)閉)
- Android實(shí)現(xiàn)輸入法彈出時(shí)把布局頂上去和登錄按鈕頂上去的解決方法
- Android 點(diǎn)擊屏幕空白處收起輸入法軟鍵盤(手動(dòng)打開)
- Android中Activity啟動(dòng)默認(rèn)不顯示輸入法解決方法
- Android監(jiān)聽輸入法彈窗和關(guān)閉的實(shí)現(xiàn)方法
- Android開發(fā)教程之獲取系統(tǒng)輸入法高度的正確姿勢
相關(guān)文章
Android 開發(fā)中使用Linux Shell實(shí)例詳解
這篇文章主要介紹了Android 開發(fā)中使用Linux Shell實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-03-03Kotlin實(shí)用語法糖空安全類型轉(zhuǎn)換及相等性判斷
這篇文章主要為大家介紹了Kotlin實(shí)用語法糖空安全類型轉(zhuǎn)換及相等性判斷示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06Android攔截并獲取WebView內(nèi)部POST請(qǐng)求參數(shù)的實(shí)現(xiàn)方法
這篇文章主要介紹了Android攔截并獲取WebView內(nèi)部POST請(qǐng)求參數(shù) 的實(shí)現(xiàn)方法,本文通過兩種方案給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-04-04Android自定義view實(shí)現(xiàn)TextView方形輸入框
這篇文章主要為大家詳細(xì)介紹了Android自定義view實(shí)現(xiàn)TextView方形輸入框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-06-06android端實(shí)現(xiàn)驗(yàn)證碼隨機(jī)生成功能
這篇文章主要為大家詳細(xì)介紹了android端實(shí)現(xiàn)驗(yàn)證碼隨機(jī)生成功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07Android 斷點(diǎn)續(xù)傳的原理剖析與實(shí)例講解
這篇文章主要介紹了Android 斷點(diǎn)續(xù)傳的原理剖析與實(shí)例講解的相關(guān)資料,需要的朋友可以參考下2016-09-09