package com.hzliuzhi.applet.container import android.graphics.Rect import android.os.Bundle import android.view.View import android.view.ViewGroup import android.view.ViewTreeObserver import android.widget.FrameLayout import androidx.activity.ComponentActivity import androidx.activity.enableEdgeToEdge open class AndroidActivity : ComponentActivity() { /** * 软键盘适配工具 */ private var workaround: AndroidBug5497Workaround? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent() // 设置内容视图,可被子类重写 // 初始化软键盘适配工具 val content = findViewById(android.R.id.content) val child = content?.getChildAt(0) workaround = if (child != null) AndroidBug5497Workaround(child) else null } /** * 生命周期回调,注册软键盘适配监听。 */ override fun onResume() { super.onResume() workaround?.register() } /** * 生命周期回调,移除软键盘适配监听。 */ override fun onPause() { super.onPause() workaround?.unregister() } /** * 设置内容视图,默认启用EdgeToEdge,可被子类重写。 */ open fun setContent() { enableEdgeToEdge() } } /** * 软键盘适配工具类,处理软键盘弹出时的布局自适应。 * 通过监听全局布局变化,动态调整根视图高度,防止内容被软键盘遮挡。 * 参考:https://stackoverflow.com/questions/7417123/android-how-to-adjust-layout-in-full-screen-mode-when-softkeyboard-is-visible * @property rootView 根视图 */ private class AndroidBug5497Workaround(private val rootView: View) { /** 根视图的布局参数 */ private val rootViewLayout: FrameLayout.LayoutParams = rootView.layoutParams as FrameLayout.LayoutParams /** 用于监听布局变化的 ViewTreeObserver */ private var viewTreeObserver: ViewTreeObserver = rootView.viewTreeObserver /** 用于记录窗口内容区域的 Rect */ private val contentAreaOfWindowBounds = Rect() /** 上一次可用高度 */ private var usableHeightPrevious = 0 /** * 全局布局监听器,触发内容区域高度调整。 */ private val listener = ViewTreeObserver.OnGlobalLayoutListener { possiblyResizeChildOfContent() } /** * 注册全局布局监听器。 */ fun register() { if (!viewTreeObserver.isAlive) viewTreeObserver = rootView.viewTreeObserver viewTreeObserver.addOnGlobalLayoutListener(listener) } /** * 移除全局布局监听器,避免内存泄漏。 */ fun unregister() { if (!viewTreeObserver.isAlive) viewTreeObserver = rootView.viewTreeObserver viewTreeObserver.removeOnGlobalLayoutListener(listener) } /** * 判断并调整内容区域高度,适配软键盘弹出等场景。 * 若可用高度发生变化,则重新设置根视图高度并请求布局。 */ private fun possiblyResizeChildOfContent() { (rootView.parent as? ViewGroup)?.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds) val usableHeightNow = contentAreaOfWindowBounds.height() + contentAreaOfWindowBounds.top if (usableHeightNow != usableHeightPrevious) { rootViewLayout.height = usableHeightNow rootView.layout( contentAreaOfWindowBounds.left, contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom ) rootView.requestLayout() usableHeightPrevious = usableHeightNow } } }