根因: Tauri targetSdk 37 强制 edge-to-edge, MainActivity 不处理 IME Insets, 系统不再因键盘缩小窗口, 清单 adjustResize 失效, 键盘直接盖住 WebView。 - 新增 scripts/ci/android-softinput.sh: init 后注入 windowSoftInputMode=adjustResize, 并重写 MainActivity.kt 监听 IME Insets, 键盘可见时把高度加到内容容器底部 (API 30+), 让 WebView 真正变矮, 所有 bottom 弹层自动上移 - CI (android-build.yml) 与本地 build-apk.sh 在 init 之后、build 之前调用该脚本 (幂等) - 新增全局 useKeepInputVisible: 输入框聚焦后用 scroll-into-view-if-needed 滚入可视区, 覆盖页面滚动与 Popup 内部滚动 - 弹层补 max-height + 内部滚动, 防止键盘弹出后顶部被顶出屏幕 - calibrateSafeTop 在键盘弹出时保留旧值, 避免误判导致整页跳动
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
import { useEffect } from 'react'
|
||||
import scrollIntoView from 'scroll-into-view-if-needed'
|
||||
|
||||
// 全局兜底: 输入框获得焦点后保证它滚进可视区。
|
||||
//
|
||||
// 原生层 (scripts/ci/android-softinput.sh) 已经把软键盘高度从 WebView 里扣掉,
|
||||
// 弹层 (position: fixed; bottom: 0) 会自动上移; 这里只负责把被聚焦的控件
|
||||
// 滚到可视区中间, 覆盖页面滚动、Popup 内部滚动等所有场景。
|
||||
//
|
||||
// 用 scroll-into-view-if-needed (成熟库) 而不是手写滚动计算:
|
||||
// 它会自动处理嵌套滚动容器, 并在目标已可见时不做多余滚动。
|
||||
const FIELD_SELECTOR = 'input, textarea, [contenteditable="true"]'
|
||||
|
||||
function isField(el) {
|
||||
return !!el && typeof el.matches === 'function' && el.matches(FIELD_SELECTOR)
|
||||
}
|
||||
|
||||
export default function useKeepInputVisible() {
|
||||
useEffect(() => {
|
||||
let timer = 0
|
||||
|
||||
const reveal = (el, behavior) => {
|
||||
if (!isField(el)) return
|
||||
scrollIntoView(el, {
|
||||
behavior,
|
||||
block: 'center',
|
||||
inline: 'nearest',
|
||||
scrollMode: 'if-needed',
|
||||
})
|
||||
}
|
||||
|
||||
const onFocusIn = (e) => {
|
||||
const el = e.target
|
||||
if (!isField(el)) return
|
||||
// 先滚一次 (部分机型聚焦后立刻可用), 等键盘动画结束后再校正一次
|
||||
reveal(el, 'smooth')
|
||||
clearTimeout(timer)
|
||||
timer = setTimeout(() => reveal(el, 'smooth'), 260)
|
||||
}
|
||||
|
||||
// 键盘弹出/收起会改变可视区高度 (原生层已让 WebView 变矮)
|
||||
const onViewportResize = () => {
|
||||
const el = document.activeElement
|
||||
if (!isField(el)) return
|
||||
reveal(el, 'auto')
|
||||
}
|
||||
|
||||
document.addEventListener('focusin', onFocusIn)
|
||||
window.addEventListener('resize', onViewportResize)
|
||||
window.visualViewport?.addEventListener('resize', onViewportResize)
|
||||
|
||||
return () => {
|
||||
clearTimeout(timer)
|
||||
document.removeEventListener('focusin', onFocusIn)
|
||||
window.removeEventListener('resize', onViewportResize)
|
||||
window.visualViewport?.removeEventListener('resize', onViewportResize)
|
||||
}
|
||||
}, [])
|
||||
}
|
||||
Reference in New Issue
Block a user