|
@@ -0,0 +1,110 @@
|
|
|
+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<ViewGroup?>(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
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|