鸿蒙OS开发实例:【Web网页】

news2024/11/28 14:37:35

 背景

HarmonyOS平台通过Web控件可支持网页加载展示,Web在中是作为专项参考的。

本篇文章将从Android和iOS平台研发角度出发来实践学习API功能

说明

  1. 整个示例是以HarmonyOS开发文档网址作为加载目标
  2. 页面布局增加了三个按钮“后退”,“前进”, “刷新”

效果

Screenshot_20231130120249783.png

准备

  1. 请参照
鸿蒙OS开发更多内容↓点击HarmonyOS与OpenHarmony技术
鸿蒙技术文档开发知识更新库gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md在这。或+mau123789学习,是v喔

熟读HarmonyOS Web组件指导

搜狗高速浏览器截图20240326151547.png

2.创建一个Demo工程,选择Stage模型。

实践总结

  1. UA可以设置,但无法通过API拿到自己设置的UA值
  2. 文件可以下载,但用户没有控制权
  3. 用户无法控制定位权限申请
  4. Web控件当前需要将UA设置为Android或者iOS特征的UA,大部分主流网站没有适配鸿蒙Web
  5. 鸿蒙UA特征不明显 Mozilla/5.0 (X11; Linux aarch64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.88 Mobile Safari/537.36

开始

页面容器设置为沉浸式

import UIAbility from '@ohos.app.ability.UIAbility';
import hilog from '@ohos.hilog';
import window from '@ohos.window';

export default class EntryAbility extends UIAbility {

  onWindowStageCreate(windowStage: window.WindowStage) {
    // 1.获取应用主窗口。
    let windowClass: window.Window = null;
    windowStage.getMainWindow((err, data) => {
      if (err.code) {
        console.error('Failed to obtain the main window. Cause: ' + JSON.stringify(err));
        return;
      }
      windowClass = data;
      console.info('Succeeded in obtaining the main window. Data: ' + JSON.stringify(data));
      // 2.实现沉浸式效果:设置导航栏、状态栏显示。

      windowClass.setWindowSystemBarEnable(['status','navigation'], (err) => {
        if (err.code) {
          console.error('Failed to set the system bar to be visible. Cause:' + JSON.stringify(err));
          return;
        }
        console.info('Succeeded in setting the system bar to be visible.');
      });

    })

    //获取当前应用内最后显示的窗口,使用callback异步回调
    window.getLastWindow(this.context).then((result: window.Window) => {
      result.setWindowSystemBarEnable(['status', 'navigation'])
      result.setWindowLayoutFullScreen(true);
    })

    windowStage.loadContent('pages/Index', (err, data) => {
      if (err.code) {
        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
        return;
      }
      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
    });
  }

}

创建WebView组件

文件路径

根目录/ets/entry/src/main/pages/WebView.ts

注册页面 main_pages.json

{
  "src": [
    "pages/Index"
    ,"pages/WebView"
  ]
}

功能实现

Cookie管理指导

网页调试

功能介绍
  1. 支持多窗口
  2. 多窗口返回关闭
  3. 加载进度提示
  4. 警告框,确认框,提示框
  5. 权限申请
  6. 输出调试日志
  7. 非http或https协议拦截
import web_webview from '@ohos.web.webview';
import router from '@ohos.router';
import common from '@ohos.app.ability.common';
import Url from '@ohos.url'

web_webview.once("webInited", () => {
  console.log("setCookie")
  web_webview.WebCookieManager.setCookie("https://developer.harmonyos.com/", "author=harvey")
})

//在同一page页有两个web组件。在WebComponent新开窗口时,会跳转到NewWebViewComp。
@CustomDialog
struct NewWebViewComp {
  private controller?: CustomDialogController
  private webviewController: web_webview.WebviewController = new web_webview.WebviewController()

  build() {
    Column() {
      Web({ src: "", controller: this.webviewController })
        .javaScriptAccess(true)
        .multiWindowAccess(false)
        .domStorageAccess(true)
        .onWindowExit(() => {
          console.info("NewWebViewComp onWindowExit")
          if (this.controller) {
            this.controller.close()
          }
        })
    }
  }
}

@Entry
@Component
struct Index {
  //www.useragentinfo.com

  // @State webURL: string = 'https://m.bilibili.com/'    //'https://developer.harmonyos.com/'
  // @State webURL: string = 'https://www.baidu.com'
  @State webURL: string = 'https://developer.harmonyos.com/cn/docs/documentation/doc-guides-V3/start-overview-0000001478061421-V3?catalogVersion=V3'
  @State back: boolean = true
  @State forward: boolean = false
  @State showProgress: boolean = false
  @State currentProgress: number = 0
  @State buttonColorFocusColor: number = Color.Black
  @State buttonColorDisableColor: number = Color.Gray
  @State currentButtonColor: number = this.buttonColorFocusColor
  private webviewController: web_webview.WebviewController = new web_webview.WebviewController();
  private context = getContext(this) as common.UIAbilityContext;
  dialogController: CustomDialogController | null = null

  aboutToAppear() {
    web_webview.WebviewController.setWebDebuggingAccess(true)

    let params = router.getParams()
    if (params) {
      this.webURL = params['targetUrl'];
    }
  }

  build() {
    Column() {
      Stack() {
        Web({ src: this.webURL, controller: this.webviewController })
          .width('100%')
          .height('100%')
          .userAgent('Mozilla/5.0 (Linux; Android 8.0.0; SM-G955U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36 HarveyHarmonyOS/1.0.0')
          .multiWindowAccess(true)
          .javaScriptAccess(true)
          .geolocationAccess(true)
          .imageAccess(true)
          .onlineImageAccess(true)
          .domStorageAccess(true)
          .fileAccess(true)
          .mediaPlayGestureAccess(true)
          .mixedMode(MixedMode.Compatible)
          .onTitleReceive((info) => {
            console.log('标题栏: ' + info.title)
          })
          .onProgressChange((progress) => {
            console.log('当前加载进度 ' + progress.newProgress)

            this.currentProgress = progress.newProgress

            if (progress.newProgress >= 0 && progress.newProgress < 100) {
              this.showProgress = true
            } else if (progress.newProgress == 100) {
              this.showProgress = false
            }

            if (this.webviewController.accessForward()) {
              this.forward = true
              this.currentButtonColor = this.buttonColorFocusColor
            } else {
              this.forward = false
              this.currentButtonColor = this.buttonColorDisableColor
            }

            console.log('userAgent: ' + this.webviewController.getUserAgent())
          })
          .onErrorReceive((error) => {
            console.log(error.request.getRequestUrl())
            console.log(JSON.stringify(error.error))
          })
          .onHttpErrorReceive((error) => {
            console.log(JSON.stringify(error.response))
          })
          .onSslErrorEventReceive((info) => {

          })
          .onRenderExited(() => {
             console.log('onRenderExited')
          })
          .onUrlLoadIntercept((info) => {
            if(!info.data.toString().toLowerCase().startsWith("https://") || !info.data.toString().toLowerCase().startsWith("https://")){
              console.log('拦截信息: ' + JSON.stringify(info))
              return true;
            }
            console.log('信息: ' + JSON.stringify(info))
            //false : 不拦截   true: 拦截
            return false
          })
          .onDownloadStart( (event) => {
            AlertDialog.show({
              title: event.url,
              message: event.url,
              primaryButton: {
                value: 'cancel',
                action: () => {

                }
              }
            })
          })
          .onAlert((event) => {
            AlertDialog.show({
              title: event.url,
              message: event.message,
              confirm: {
                value: 'onAlert',
                action: () => {
                  event.result.handleConfirm()
                }
              },
              cancel: () => {
                event.result.handleCancel()
              }
            })
            return true
          })
          .onConfirm((event) => {
            AlertDialog.show({
              title: event.url,
              message: event.message,
              confirm: {
                value: 'onConfirm',
                action: () => {
                  event.result.handleConfirm()
                }
              },
              cancel: () => {
                event.result.handleCancel()
              }
            })
            return true;
          })
          .onPrompt((event) => {
            AlertDialog.show({
              title: event.url,
              message: event.message,
              primaryButton: {
                value: 'cancel',
                action: () => {
                  event.result.handleCancel()
                }
              },
              secondaryButton: {
                value: 'ok',
                action: () => {
                  event.result.handleConfirm()
                }
              },
              cancel: () => {
                event.result.handleCancel()
              }
            })
            return true;
          })
          .onConsole((msg) => {
            console.error('网页日志:' + JSON.stringify(msg.message.getMessage()))
            return true
          })
          .onWindowNew((event) => {
            console.log('新开window')

            if (!event.isAlert) {
              router.pushUrl({ url: 'custompages/WebView', params: {
                "targetUrl": event.targetUrl
              } })
                .then(() => {
                  console.info('Succeeded in jumping to the second page.')
                }).catch((error) => {
                console.log(error)
              })
            } else {
              if (this.dialogController) {
                this.dialogController.close()
              }
              let popController: web_webview.WebviewController = new web_webview.WebviewController()
              this.dialogController = new CustomDialogController({
                builder: NewWebViewComp({ webviewController: popController })
              })
              this.dialogController.open()
              //将新窗口对应WebviewController返回给Web内核。
              //如果不需要打开新窗口请调用event.handler.setWebController接口设置成null。
              //若不调用event.handler.setWebController接口,会造成render进程阻塞。
              event.handler.setWebController(popController)
            }

          })
          .onWindowExit(() => {
            console.log('已推出window')
          })
          .onGeolocationHide(() => {
             console.log('geo隐藏')
          })
          .onGeolocationShow((info) => {
            info.geolocation.invoke(info.origin, false, false)
             console.log(info.origin + ' 有定位需求')
          })
          .onPageBegin((info) => {
            console.error(info.url)
            let host = Url.URL.parseURL(info.url).host
            try {
              let cookie = web_webview.WebCookieManager.getCookie(host)
              console.log('Bcookie: ' + cookie)
            } catch (e) {
              console.error(e)
            }

          })
          .onPageEnd((info) => {

            let host = Url.URL.parseURL(info.url).host
            try {
              let cookie = web_webview.WebCookieManager.getCookie(host)
              console.log('Bcookie: ' + cookie)
            } catch (e) {
              console.error(e + ' ' + info.url)
            }

          })
          .onBeforeUnload((info) => {
            return false
          })
          .onRefreshAccessedHistory((info) => {

          })
          .onResourceLoad(() => {

          })
          .onFullScreenEnter((info) => {

          })
          .onFullScreenExit(() => {

          })
          .onPermissionRequest((event) => {

            AlertDialog.show({
              title: 'title',
              message: event.request.getAccessibleResource()[0],
              primaryButton: {
                value: 'deny',
                action: () => {
                  event.request.deny()
                }
              },
              secondaryButton: {
                value: 'onConfirm',
                action: () => {
                  event.request.grant(event.request.getAccessibleResource())
                }
              },
              cancel: () => {
                event.request.deny()
              }

            })

          })
          .onInterceptKeyEvent((info) => {
            console.log(info.keyCode + ' ' + info.keyText)
            return false
          })
          .onPageVisible((info) => {
            console.log(info.url)
          })


        if (this.showProgress) {
          Progress({ value: this.currentProgress, total: 100, type: ProgressType.Linear })
            .width('100%').height(45)
        }

      }.height('93%').alignContent(Alignment.TopStart)

      Row() {
        Text('后退')
          .fontSize(18)
          .enabled(this.back)
          .onClick(() => {
            if (this.webviewController.accessBackward()) {
              this.webviewController.backward()
            } else {
              if ("1" === router.getLength()) {
                this.context.terminateSelf()
              } else {
                router.back()
              }
            }
          })
          .width('30%')
          .height('100%')
          .textAlign(TextAlign.Center)
        Text('前进')
          .fontSize(18)
          .fontColor(this.currentButtonColor)
          .onClick(() => {
            if (this.webviewController.accessForward()) {
              this.webviewController.forward()
            }
          })
          .width('30%')
          .height('100%')
          .textAlign(TextAlign.Center)
        Text('刷新')
          .fontSize(18)
          .fontColor(Color.Black)
          .onClick(() => {
              this.webviewController.refresh()
          })
          .width('30%')
          .height('100%')
          .textAlign(TextAlign.Center)
      }.width('100%').height('5%')
      .backgroundColor(Color.White)
      .justifyContent(FlexAlign.SpaceBetween)

    }.width('100%').height('100%')
    .padding({ top: px2vp(111) })

  }
}

鸿蒙知识持续更新中,关注我点赞不迷路喔!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1560431.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

StableDiffusion Web UI开启FP8,极大节约显存

升级了Pytorch后&#xff0c;StableDiffusion最新版本就可以有使用FP8的基础了&#xff0c;因此把秋叶的LINUX包也升级到了最新的版本。 升级Pytorch参考我的升级记录&#xff1a; ComfyUI SDWebUI升级pytorch随记-CSDN博客 然后下一步就是如何开启FP8了。与ComfyUI不同&…

内容营销新手指南:Kompas.ai的智能起步策略

内容营销是当今企业获取客户、建立品牌认知和忠诚度的关键手段。然而&#xff0c;对于刚刚踏入这一领域的新手来说&#xff0c;内容营销的世界可能会显得既广阔又复杂。从内容创作到发布&#xff0c;再到分析和优化&#xff0c;每一步都充满了挑战。本文旨在为内容营销新手提供…

PMP怎么免试申请CSPM啊?

CSPM证书现在还在红利期&#xff0c;有 PMP 证书的不用参加考试就能申请增持 CSPM-2 证书&#xff0c;有 PgMP 证书可以免考增持 CSPM-3 证书&#xff0c;有 PMP 跟 PgMP 证书的不要错过哦~ 如果没有PMP证书跟 PgMP 证书的话&#xff0c;就只能参加考试了。 一、PMP证书免考增持…

Java | Leetcode Java题解之第3题无重复字符的最长子串

题目&#xff1a; 题解&#xff1a; class Solution {public int lengthOfLongestSubstring(String s) {// 哈希集合&#xff0c;记录每个字符是否出现过Set<Character> occ new HashSet<Character>();int n s.length();// 右指针&#xff0c;初始值为 -1&#…

sk-learn 特征数据预处理方式汇总

数据集及基本操作 1&#xff09;数据集的组成 数据集由特征(feature)与标签(label)构成。 特征是输入数据。 什么是特征&#xff08;Features&#xff09;: 机器学习中输入数据&#xff0c;被称为特征。通常特征不止1个&#xff0c;可以用 n 维向量表示n个特征。 Features 数…

vue3全局控制Element plus所有组件的文字大小

项目框架vue-右上角有控制全文的文字大小 实现&#xff1a; 只能控制element组件的文字及输入框等大小变化&#xff0c;如果是自行添加div,text, span之类的控制不了。 配置流程 APP.vue 使用element的provide&#xff0c;包含app <el-config-provider :locale"loca…

【JVM】关于JVM垃圾回收

文章目录 &#x1f334;死亡对象的判断算法&#x1f338;引用计数算法&#x1f338;可达性分析算法 &#x1f333;垃圾回收算法&#x1f338;标记-清除算法&#x1f338;复制算法&#x1f338;标记-整理算法&#x1f338;分代算法&#x1f338;哪些对象会进入新生代&#xff1f…

R使用netmeta程序包实现二分类数据的频率学网状meta分析

该研究检索了Cochrane对照试验中心注册&#xff0c;CINAHL&#xff0c;Embase&#xff0c;LILACS数据库&#xff0c;MEDLINE&#xff0c;MEDLINEIn-Process&#xff0c;PsycINFO&#xff0c;监管机构网站&#xff0c;以及从一开始就发布和未发表的双盲随机对照试验的国际注册20…

[HackMyVM]靶场Pipy

难度:easy kali:192.168.56.104 靶机:192.168.56.141 端口扫描 ┌──(root㉿kali2)-[~/Desktop] └─# nmap 192.168.56.141 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-03-31 20:10 CST Nmap scan report for 192.168.56.141 Host is up (0.00…

分布式文件系统引擎

文件系统基本概念 文件系统接口 文件系统&#xff1a; 一种把数据组织成文件和目录的存储方式&#xff0c;提供了基于文件的存取接口&#xff0c;并通过文件权限控制访问。 存储的基本单位 扇区&#xff1a; 磁盘的最小存储存储单位&#xff08;Sector&#xff09;。一般每个…

SOC内部集成网络MAC外设+ PHY网络芯片方案:PHY芯片基础知识

一. 简介 本文简单了解一下 "SOC内部集成网络MAC外设 PHY网络芯片方案" 这个网络硬件方案中涉及的 PHY网络芯片的基础知识。 二. PHY芯片基础知识 PHY 是 IEEE 802.3 规定的一个标准模块。 1. IEEE规定了PHY芯片的前 16个寄存器功能是一样的 前面说了&#xf…

ZooKeeper 的持久化机制

持久化的定义&#xff1a; 数据&#xff0c;存到磁盘或者文件当中。机器重启后&#xff0c;数据不会丢失。内存 -> 磁盘的映射&#xff0c;和序列化有些像。 ZooKeeper 的持久化&#xff1a; SnapShot 快照&#xff0c;记录内存中的全量数据TxnLog 增量事务日志&#xff…

Taro关于多个数组同时根据时间展示倒计时的代码组件

我们通常在做秒杀活动时&#xff0c;会有活动开始或者活动结束倒计时 而在活动列表中&#xff0c;需要做统一处理 以下为做的关于倒计时的组件~ primaryColor可忽略&#xff0c;是关于倒计时时间的主题色 startTime活动开始时间 endTime活动结束时间 refresh方法为其中一个倒计…

【嵌入式智能产品开发实战】(七)—— 政安晨:通过ARM-Linux掌握基本技能【环境准备:树莓派】

目录 Raspberry Pi OS 下载系统镜像 使用SSH客户端登陆 升级更新 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: 嵌入式智能产品开发实战 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正…

iptables 与 firewalld 防火墙

iptables iptables 是一款基于命令行的防火墙策略管理工具 四种防火墙策略&#xff1a; ACCEPT&#xff08;允许流量通过&#xff09; 流量发送方会看到响应超时的提醒&#xff0c;但是流量发送方无法判断流量是被拒绝&#xff0c;还是接收方主机当前不在线 REJECT&#xff08…

区间预测 | Matlab实现带有置信区间的BP神经网络时间序列未来趋势预测

区间预测 | Matlab实现带有置信区间的BP神经网络时间序列未来趋势预测 目录 区间预测 | Matlab实现带有置信区间的BP神经网络时间序列未来趋势预测预测效果基本介绍研究回顾程序设计参考资料预测效果 基本介绍 BP神经网络(Backpropagation neural network)是一种常用的人工神…

实验报告学习——gdb的使用

gdb的使用: l查看源码和行号 p a或main::a&#xff08;main函数中a)打印变量a的值 要打印单个寄存器的值&#xff0c;可以使用“i registers eax”或者“p $eax” 设置断点b 5&#xff08;根据行数&#xff09;/main&#xff08;根据函数&#xff09;/*0x40059b&#xff0…

【键值皆有序map 线段树 数学 】100240. 最小化曼哈顿距离

本文涉及知识点 键值皆有序map 线段树 数学 LeetCode100240. 最小化曼哈顿距离 给你一个下标从 0 开始的数组 points &#xff0c;它表示二维平面上一些点的整数坐标&#xff0c;其中 points[i] [xi, yi] 。 两点之间的距离定义为它们的曼哈顿距离。 请你恰好移除一个点&am…

大数据-TXT文本重复行计数工具

支持系统类型&#xff1a;Windows 64位系统 Linux 64位系统 苹果64位系统 硬盘要求&#xff1a;固态硬盘&#xff08;有效剩余磁盘空间大小最低3倍于大数据文件的大小&#xff09; 内存要求&#xff1a;最低8G&#xff08;例如只有几百G数据&#xff09; 如果处理TB级大数据文…

Matlab中的脚本和函数

Matlab中的脚本和函数 文章目录 Matlab中的脚本和函数脚本创建脚本代码注释函数创建函数局部函数嵌套函数私有函数匿名函数补充知识函数句柄测试环境:Win11 + Matlab R2021a 脚本 ​ Matlab脚本是最简单的程序文件类型。它们可用于自动执行一系列 Matlab 命令,如命令行重复执…