最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue3?Webview轉(zhuǎn)Android虛擬導(dǎo)航欄遮擋問題解決實(shí)錄

 更新時(shí)間:2026年03月08日 08:34:19   作者:huohuopro  
這篇文章主要介紹了Vue3?Webview轉(zhuǎn)Android虛擬導(dǎo)航欄遮擋問題解決的相關(guān)資料,文中通過JavaScript動(dòng)態(tài)估算安全區(qū)域高度,并通過CSS變量傳遞給樣式層,最終解決了這個(gè)問題,需要的朋友可以參考下

問題描述

在 Android 設(shè)備上運(yùn)行 Capacitor 打包的 Vue 3 應(yīng)用時(shí),遇到虛擬導(dǎo)航欄(底部返回鍵、主頁鍵等)和狀態(tài)欄遮擋應(yīng)用內(nèi)容的問題。

問題表現(xiàn)

  • 底部 Tab 導(dǎo)航欄被虛擬導(dǎo)航欄遮擋一部分
  • 頂部內(nèi)容被狀態(tài)欄遮擋
  • 頁面底部內(nèi)容貼近虛擬導(dǎo)航欄,沒有安全間距

問題根源分析

初始狀態(tài)

應(yīng)用使用了沉浸式布局,在 MainActivity.java 中設(shè)置了:

WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
getWindow().setStatusBarColor(Color.TRANSPARENT);
getWindow().setNavigationBarColor(Color.TRANSPARENT);

這使得 WebView 內(nèi)容延伸到狀態(tài)欄和導(dǎo)航欄后面,實(shí)現(xiàn)了全屏顯示。

錯(cuò)誤的假設(shè)

最初嘗試使用 CSS 的環(huán)境變量來解決:

padding-top: env(safe-area-inset-top, 0);
padding-bottom: env(safe-area-inset-bottom, 0);

問題:Android WebView 不支持 CSS 的 env(safe-area-inset-*) 環(huán)境變量

實(shí)驗(yàn)驗(yàn)證

通過添加調(diào)試日志,在控制臺(tái)輸出:

// 檢查 CSS 環(huán)境變量
const rootStyles = getComputedStyle(document.documentElement)
console.log('CSS 環(huán)境變量原始值:', {
  'safe-area-inset-top': rootStyles.getPropertyValue('safe-area-inset-top'),
  'safe-area-inset-bottom': rootStyles.getPropertyValue('safe-area-inset-bottom'),
})

// 檢查實(shí)際效果
console.log('body paddingTop:', getComputedStyle(document.body).paddingTop)
console.log('body paddingBottom:', getComputedStyle(document.body).paddingBottom)

實(shí)驗(yàn)結(jié)果

?? [CSS 環(huán)境變量] 原始值:[object Object]
  - safe-area-inset-top: "" (空字符串)
  - safe-area-inset-bottom: "" (空字符串)
?? [實(shí)驗(yàn)結(jié)論]: ? CSS 環(huán)境變量未生效 - 硬性編碼起作用

結(jié)論

  • CSS env() 環(huán)境變量在 Android WebView 中返回空值
  • 之前看到的 padding 效果是 CSS 中硬性編碼的數(shù)值在起作用
  • 底部的 env(safe-area-inset-bottom, 0) fallback 到 0,導(dǎo)致沒有底部間距

最終解決方案

方案核心:JavaScript 動(dòng)態(tài)估算 + CSS 變量

由于 CSS 環(huán)境變量不生效,改用 JavaScript 在運(yùn)行時(shí)動(dòng)態(tài)計(jì)算安全區(qū)域高度,并通過 CSS 變量傳遞給樣式層。

實(shí)現(xiàn)步驟

1. JavaScript 動(dòng)態(tài)計(jì)算(src/main.ts)

// Capacitor 安全區(qū)域適配
const setupSafeArea = async () => {
  try {
    const { Capacitor } = await import('@capacitor/core')
    
    // 只在原生平臺(tái)上執(zhí)行
    if (!Capacitor.isNativePlatform()) {
      console.log('?? 非原生平臺(tái),跳過設(shè)置')
      return
    }

    console.log('?? [安全區(qū)域] 檢測到原生平臺(tái),開始設(shè)置...')

    // 獲取屏幕尺寸
    const screenWidth = window.screen.width
    const screenHeight = window.screen.height
    
    // 通過屏幕比例估算狀態(tài)欄和導(dǎo)航欄高度
    // 狀態(tài)欄通常是屏幕高度的 3-5% 或固定值(約 24-50dp)
    // 導(dǎo)航欄通常是 48-56dp
    const estimatedStatusBarHeight = Math.min(
      Math.round(screenHeight * 0.04), // 4% 屏幕高度
      50 // 最大 50px
    )
    
    const estimatedNavBarHeight = Math.min(
      Math.round(screenHeight * 0.05), // 5% 屏幕高度
      56 // 最大 56px
    )
    
    console.log('?? [安全區(qū)域] 屏幕尺寸:', screenWidth, 'x', screenHeight)
    console.log('?? [安全區(qū)域] 估算值 - 狀態(tài)欄:', estimatedStatusBarHeight, '導(dǎo)航欄:', estimatedNavBarHeight)
    
    // 設(shè)置 CSS 變量
    const root = document.documentElement
    root.style.setProperty('--sat', `${estimatedStatusBarHeight}px`)
    root.style.setProperty('--sab', `${estimatedNavBarHeight}px`)
    
    // 更新 body 的 padding
    const body = document.body
    body.style.paddingTop = `${estimatedStatusBarHeight}px`
    body.style.paddingBottom = `${estimatedNavBarHeight}px`
    
    console.log('? [安全區(qū)域] 已設(shè)置 - 頂部:', estimatedStatusBarHeight, '底部:', estimatedNavBarHeight)
  } catch (e: any) {
    console.log('?? [安全區(qū)域] 設(shè)置失敗:', e.message)
  }
}

// 應(yīng)用掛載時(shí)執(zhí)行
app.mount('#app').$nextTick(async () => {
  await setupSafeArea()
  // ... 其他初始化邏輯
})

2. CSS 變量應(yīng)用

將所有使用 env(safe-area-inset-*) 的地方替換為 var(--sat, 0)var(--sab, 0)

src/style.css

body {
  /* 頂部和底部安全區(qū)域通過 JS 動(dòng)態(tài)設(shè)置 CSS 變量 */
  padding-top: var(--sat, 0);
  padding-bottom: var(--sab, 0);
  transition: padding-top 0.3s ease, padding-bottom 0.3s ease;
}

/* 頁面容器 */
.page-container {
  padding: 16px;
  padding-top: calc(16px + var(--sat, 0));
  padding-bottom: calc(80px + var(--sab, 0));
  min-height: 100vh;
  min-height: 100dvh;
}

src/App.vue

.app-container {
  width: 100%;
  height: 100%;
  min-height: 100vh;
  min-height: 100dvh;
  position: relative;
  padding-top: var(--sat, 0); /* 頂部安全區(qū)域 */
  padding-bottom: calc(80px + var(--sab, 0)); /* 底部安全區(qū)域 + 導(dǎo)航欄高度 */
  box-sizing: border-box;
}

.tab-bar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: auto;
  min-height: calc(56px + var(--sab, 0));
  display: flex;
  justify-content: space-around;
  align-items: flex-start;
  background: #fff;
  border-top: 1px solid #f0f0f0;
  z-index: 1000;
  padding-top: 10px;
  padding-bottom: calc(10px + var(--sab, 0));
  box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
}

src/views/Login.vue

.login-container {
  padding-top: calc(16px + var(--sat, 0));
  padding-bottom: calc(32px + var(--sab, 0));
}

.login-form {
  padding: 36px 32px;
  padding-top: calc(36px + var(--sat, 0));
  padding-bottom: calc(32px + var(--sab, 0));
}

src/views/Classmates.vue 和 src/views/Tasks.vue

.page-container {
  padding: 16px;
  padding-top: calc(16px + var(--sat, 0));
  padding-bottom: calc(120px + var(--sab, 0));
}

src/views/Tasks.vue (浮動(dòng)按鈕):

.fab-button {
  position: fixed;
  bottom: calc(90px + var(--sab, 0));
  right: 16px;
}

3. Android 原生層配置(MainActivity.java)

保持沉浸式布局,但不隱藏系統(tǒng)欄:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // 啟用邊緣到邊緣顯示 (沉浸式)
    WindowCompat.setDecorFitsSystemWindows(getWindow(), false);

    // 設(shè)置透明狀態(tài)欄和導(dǎo)航欄
    setupTransparentSystemBars();
}

private void setupTransparentSystemBars() {
    // 設(shè)置狀態(tài)欄和導(dǎo)航欄透明
    getWindow().setStatusBarColor(android.graphics.Color.TRANSPARENT);
    getWindow().setNavigationBarColor(android.graphics.Color.TRANSPARENT);

    // 設(shè)置窗口標(biāo)志以實(shí)現(xiàn)沉浸式
    int flags = WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
    getWindow().setFlags(flags, flags);

    // 設(shè)置為邊緣到邊緣布局,但不隱藏系統(tǒng)欄
    // 讓 CSS 的 safe-area-inset 來處理間距
    View decorView = getWindow().getDecorView();
    int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
    decorView.setSystemUiVisibility(uiOptions);
}

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if (hasFocus) {
        // 窗口獲得焦點(diǎn)時(shí),重新應(yīng)用邊緣到邊緣布局
        View decorView = getWindow().getDecorView();
        int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
        decorView.setSystemUiVisibility(uiOptions);
    }
}

關(guān)鍵點(diǎn)

  • 移除了 SYSTEM_UI_FLAG_FULLSCREENSYSTEM_UI_FLAG_HIDE_NAVIGATION
  • 移除了 SYSTEM_UI_FLAG_IMMERSIVE_STICKY
  • 只保留 LAYOUT_FULLSCREENLAYOUT_HIDE_NAVIGATION 實(shí)現(xiàn)邊緣到邊緣布局
  • 不隱藏系統(tǒng)欄,讓內(nèi)容延伸到系統(tǒng)欄后面,通過 padding 留出安全區(qū)域

4. HTML viewport 配置(index.html)

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />

添加 viewport-fit=cover 啟用全面屏適配。

1. 為什么 CSS env() 不生效?

  • CSS 的 env(safe-area-inset-*) 是 iOS Safari 引入的特性
  • Android WebView 和 Chrome 對(duì)此支持不完善
  • 在 Android 上需要手動(dòng)實(shí)現(xiàn)安全區(qū)域適配

2. 為什么使用估算而非精確值?

  • Capacitor 的 StatusBar 和 NavigationBar 插件需要額外安裝
  • 為了減少依賴,使用屏幕比例估算
  • 經(jīng)驗(yàn)值:狀態(tài)欄 ≈ 4% 屏幕高度,導(dǎo)航欄 ≈ 5% 屏幕高度
  • 設(shè)置上限避免極端情況(狀態(tài)欄最大 50px,導(dǎo)航欄最大 56px)

3. CSS 變量的優(yōu)勢

  • 一次設(shè)置,全局使用
  • 支持動(dòng)態(tài)更新
  • 保持樣式層簡潔
  • fallback 機(jī)制:var(--sat, 0) 確保非原生平臺(tái)正常顯示

4. 沉浸式布局的關(guān)鍵

  • WindowCompat.setDecorFitsSystemWindows(getWindow(), false) 啟用邊緣到邊緣
  • 透明狀態(tài)欄和導(dǎo)航欄
  • 使用 LAYOUT_FULLSCREENLAYOUT_HIDE_NAVIGATION 讓內(nèi)容延伸到系統(tǒng)欄后面
  • 不隱藏系統(tǒng)欄,而是通過 padding 留出安全區(qū)域

參考資料

到此這篇關(guān)于Vue3 Webview轉(zhuǎn)Android虛擬導(dǎo)航欄遮擋問題解決實(shí)錄的文章就介紹到這了,更多相關(guān)Vue3 Webview轉(zhuǎn)Android虛擬導(dǎo)航欄遮擋內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue3中使用keepAlive緩存路由組件不生效的問題解決

    vue3中使用keepAlive緩存路由組件不生效的問題解決

    這篇文章主要介紹了vue3中使用keepAlive緩存路由組件不生效的問題解決,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2024-06-06
  • Vue代理報(bào)錯(cuò)404問題及解決(vue配置proxy)

    Vue代理報(bào)錯(cuò)404問題及解決(vue配置proxy)

    這篇文章主要介紹了Vue代理報(bào)錯(cuò)404問題及解決(vue配置proxy),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • 在使用uni-data-select插件時(shí)通過接口獲取值賦給localdata失敗的問題解決方案

    在使用uni-data-select插件時(shí)通過接口獲取值賦給localdata失敗的問題解決方案

    在加載渲染時(shí),調(diào)取接口請求的是一個(gè)異步的操作,在子組件拿到數(shù)據(jù)前就渲染了,才導(dǎo)致子組件watch沒有監(jiān)聽到值的變化,下面給大家介紹在使用uni-data-select插件時(shí)通過接口獲取值賦給localdata失敗的問題,感興趣的朋友一起看看吧
    2024-12-12
  • 前端如何調(diào)用Go語言后端接口(HTML?+?JS?&?Vue)

    前端如何調(diào)用Go語言后端接口(HTML?+?JS?&?Vue)

    這篇文章主要介紹了前端如何調(diào)用Go語言后端接口(HTML?+?JS?&?Vue)的相關(guān)資料,文中通過代碼還討論了跨域問題的解決方法,并對(duì)比了HTML+JS和Vue的不同適用場景,需要的朋友可以參考下
    2026-01-01
  • 基于Vue3定制復(fù)雜組件滾動(dòng)條的實(shí)現(xiàn)

    基于Vue3定制復(fù)雜組件滾動(dòng)條的實(shí)現(xiàn)

    這篇文章主要介紹了如何利用vue3定制復(fù)雜組件的滾動(dòng)條,文中通過示例代碼講解詳細(xì),需要的朋友們下面就跟隨小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Vue3實(shí)現(xiàn)可視化拖拽標(biāo)簽小程序功能

    Vue3實(shí)現(xiàn)可視化拖拽標(biāo)簽小程序功能

    這篇文章主要介紹了Vue3實(shí)現(xiàn)可視化拖拽標(biāo)簽小程序,實(shí)現(xiàn)功能可視化標(biāo)簽拖拽,雙擊標(biāo)簽可修改標(biāo)簽內(nèi)容,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-09-09
  • 在Vue-cli里應(yīng)用Vuex的state和mutations方法

    在Vue-cli里應(yīng)用Vuex的state和mutations方法

    今天小編就為大家分享一篇在Vue-cli里應(yīng)用Vuex的state和mutations方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-09-09
  • vue element-ui實(shí)現(xiàn)el-table表格多選以及回顯方式

    vue element-ui實(shí)現(xiàn)el-table表格多選以及回顯方式

    這篇文章主要介紹了vue element-ui實(shí)現(xiàn)el-table表格多選以及回顯方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • 淺談vue中resetFields()使用注意事項(xiàng)

    淺談vue中resetFields()使用注意事項(xiàng)

    這篇文章主要介紹了淺談vue中resetFields()使用注意事項(xiàng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • Vue3+elementui plus創(chuàng)建項(xiàng)目的方法

    Vue3+elementui plus創(chuàng)建項(xiàng)目的方法

    這篇文章主要介紹了Vue3+elementui plus創(chuàng)建項(xiàng)目的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12

最新評(píng)論

邵武市| 镇巴县| 麦盖提县| 西昌市| 保山市| 云梦县| 时尚| 汝州市| 韶关市| 乌鲁木齐市| 玉龙| 农安县| 房产| 尼玛县| 杭州市| 保定市| 扎兰屯市| 玉龙| 定边县| 新野县| 都昌县| 长宁县| 枞阳县| 天津市| 定安县| 铁力市| 永康市| 随州市| 柯坪县| 含山县| 阳信县| 马边| 金川县| 秦皇岛市| 扬州市| 贵阳市| 陆河县| 沽源县| 宜兰县| 九江市| 华亭县|