今天遇到一個特別棘手的問題,android 內部原生嵌套webview h5頁面時,軟鍵盤被遮擋問題,苦尋半天,找不到是我這邊的問題,還是前端的問題,最後這個網址在google瀏覽器打開,並且打開軟鍵盤,鍵盤頂起是正常的先上效果圖:
原始的位置:
正常不處理時軟鍵盤彈起:
處理後軟鍵盤彈起效果如下
在確定了問題是我的原因之後,開始找答案:
找到了一個大牛寫的
AndroidBug5497Workaround
public class AndroidBug5497Workaround {
public static void assistActivity(Activity activity) {
new AndroidBug5497Workaround(activity);
}
private View mChildOfContent;
private int usableHeightPrevious;
private FrameLayout.LayoutParams frameLayoutParams;
private int contentHeight;
private boolean isfirst = true;
private Activity activity;
private int statusBarHeight;
private AndroidBug5497Workaround(Activity activity) {
//獲取狀態欄的高度
int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");
statusBarHeight = activity.getResources().getDimensionPixelSize(resourceId);
this.activity = activity;
FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
mChildOfContent = content.getChildAt(0);
//界面出現變動都會調用這個監聽事件
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
if (isfirst) {
contentHeight = mChildOfContent.getHeight();//兼容華爲等機型
isfirst = false;
}
possiblyResizeChildOfContent();
}
});
frameLayoutParams = (FrameLayout.LayoutParams)
mChildOfContent.getLayoutParams();
}
//重新調整跟佈局的高度
private void possiblyResizeChildOfContent() {
int usableHeightNow = computeUsableHeight();
//當前可見高度和上一次可見高度不一致 佈局變動
if (usableHeightNow != usableHeightPrevious) {
//int usableHeightSansKeyboard2 = mChildOfContent.getHeight();//兼容華爲等機型
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
if (heightDifference > (usableHeightSansKeyboard / 4)) {
// keyboard probably just became visible
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
//frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference + statusBarHeight;
} else {
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
}
} else {
frameLayoutParams.height = contentHeight;
}
mChildOfContent.requestLayout();
usableHeightPrevious = usableHeightNow;
}
}
/**
* 計算mChildOfContent可見高度 ** @return
*/
private int computeUsableHeight() {
Rect r = new Rect();
mChildOfContent.getWindowVisibleDisplayFrame(r);
return (r.bottom - r.top);
}
}
用了之後發現問題,在一些虛擬鍵盤手機上,虛擬鍵會遮擋下面的佈局
最後在上面進行了改良
kotlin代碼
class GlobalLayoutUtils(activity: Activity, private var isImmersed: Boolean = true) {
// 當前界面根佈局,就是我們設置的 setContentView()
private var mChildOfContent: View
private var frameLayoutParams: FrameLayout.LayoutParams
// 變化前的試圖高度
private var usableHeightPrevious = 0
init {
val content: FrameLayout = activity.findViewById(android.R.id.content)
mChildOfContent = content.getChildAt(0)
// 添加布局變化監聽
mChildOfContent.viewTreeObserver.addOnGlobalLayoutListener {
possiblyResizeChildOfContent(activity)
}
frameLayoutParams = mChildOfContent.layoutParams as FrameLayout.LayoutParams
}
private fun possiblyResizeChildOfContent(activity: Activity) {
// 當前可視區域的高度
val usableHeightNow = computeUsableHeight()
// 當前高度值和之前的進行對比,變化將進行重繪
if (usableHeightNow != usableHeightPrevious) {
// 獲取當前屏幕高度
// Ps:並不是真正的屏幕高度,是當前app的窗口高度,分屏時的高度爲分屏窗口高度
var usableHeightSansKeyboard = mChildOfContent.rootView.height
// 高度差值:屏幕高度 - 可視內容高度
var heightDifference = usableHeightSansKeyboard - usableHeightNow
// 差值爲負,說明獲取屏幕高度時出錯,寬高狀態值反了,重新計算
if (heightDifference < 0) {
usableHeightSansKeyboard = mChildOfContent.rootView.width
heightDifference = usableHeightSansKeyboard - usableHeightNow
}
when {
// keyboard probably just became visible
// 如果差值大於屏幕高度的 1/4,則認爲輸入軟鍵盤爲彈出狀態
heightDifference > usableHeightSansKeyboard / 4 ->
// 設置佈局高度爲:屏幕高度 - 高度差
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference
// keyboard probably just became hidden
// 虛擬導航欄顯示
heightDifference >= getNavigationBarHeight(activity) ->
frameLayoutParams.height = usableHeightSansKeyboard - getNavigationBarHeight(activity)
// 其他情況直接設置爲可視高度即可
else -> frameLayoutParams.height = usableHeightNow
}
}
// 刷新佈局,會重新測量、繪製
mChildOfContent.requestLayout()
// 保存高度信息
usableHeightPrevious = usableHeightNow
}
/**
* 獲取可視內容區域的高度
*/
private fun computeUsableHeight(): Int {
val r = Rect()
// 當前窗口可視區域,不包括通知欄、導航欄、輸入鍵盤區域
mChildOfContent.getWindowVisibleDisplayFrame(r)
return if (isImmersed) {
// 沉浸模式下,底部座標就是內容有效高度
r.bottom
} else {
// 非沉浸模式下,去掉通知欄的高度 r.top(可用於通知欄高度的計算)
r.bottom - r.top
}
}
// 獲取導航欄真實的高度(可能未顯示)
private fun getNavigationBarHeight(context: Context): Int {
var result = 0
val resources = context.resources
val resourceId =
resources.getIdentifier("navigation_bar_height", "dimen", "android")
if (resourceId > 0) {
result = resources.getDimensionPixelSize(resourceId)
}
return result
}
}
java代碼
public class GlobalLayoutUtil {
private Activity activity;
private boolean isImmersed = false;
private View mChildOfContent;
private FrameLayout.LayoutParams frameLayoutParams;
private int usableHeightPrevious = 0;
public GlobalLayoutUtil(Activity activity) {
this.activity = activity;
init();
}
public void init() {
FrameLayout content = activity.findViewById(android.R.id.content);
mChildOfContent = content.getChildAt(0);
//添加布局變化監聽
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
possiblyResizeChildOfContent(activity);
});
frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();
}
private void possiblyResizeChildOfContent(Activity activity) {
//當前可視區域的高度
int usableHeightNow = computeUsableHeight();
//當前高度值和之前的進行對比,變化將進行重新繪製
if (usableHeightNow != usableHeightPrevious) {
//獲取當前屏幕高度
//Ps: 並不是真正的屏幕高度,是當前app的窗口高度,分屏時的高度爲分屏窗口高度
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
// 高度差值:屏幕高度 - 可視內容高度
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
// 差值爲負,說明獲取屏幕高度時出錯,寬高狀態值反了,重新計算
if (heightDifference < 0) {
usableHeightSansKeyboard = mChildOfContent.getRootView().getWidth();
heightDifference = usableHeightSansKeyboard - usableHeightNow;
}
// keyboard probably just became visible
// 如果差值大於屏幕高度的 1/4,則認爲輸入軟鍵盤爲彈出狀態
if (heightDifference > usableHeightSansKeyboard / 4) {
// 設置佈局高度爲:屏幕高度 - 高度差
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
} else if (heightDifference >= getNavigationBarHeight(activity)) {
// keyboard probably just became hidden
// 虛擬導航欄顯示
frameLayoutParams.height = usableHeightSansKeyboard - getNavigationBarHeight(activity);
} else {// 其他情況直接設置爲可視高度即可
frameLayoutParams.height = usableHeightNow;
}
}
// 刷新佈局,會重新測量、繪製
mChildOfContent.requestLayout();
// 保存高度信息
usableHeightPrevious = usableHeightNow;
}
/**
* 獲取可視內容區域的高度
*/
private int computeUsableHeight() {
Rect rect = new Rect();
mChildOfContent.getWindowVisibleDisplayFrame(rect);
if (isImmersed)
return rect.bottom;
else
return rect.bottom - rect.top;
}
/**
* 獲取導航欄的真實高度
*
* @param context:
* @return: 導航欄高度
*/
private int getNavigationBarHeight(Context context) {
int result = 0;
Resources resources = context.getResources();
int resourceId =
resources.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0)
result = resources.getDimensionPixelSize(resourceId);
return result;
}
}