1. 介绍 开发者如果需要在自己的应用中嵌入Web页面,可以通过WebView组件进行开发。WebView组件派生于通用组件Component,可以像通用组件一样使用。
本教程以应用嵌入WebView组件为例,从WebView组件构建、Web页面加载、应用与WebView中Web页面间的交互三个方面出发,让您快速了解WebView组件的基本能力及使用方法。
WebView组件能力的详细介绍可以参考开发指南WebView。
说明
- 请使用真机或模拟器运行查看WebView效果,预览器不支持WebView显示。
- 只有预置WebView能力的真机设备才支持WebView功能。具体请以实际设备支持情况为准。智能穿戴设备不支持WebView。
2. 搭建HarmonyOS环境我们首先需要完成HarmonyOS开发环境搭建,可参照如下步骤进行。
- 安装DevEco Studio,详情请参考下载和安装软件。
- 设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:
- 如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。
- 如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境。
- 开发者可以参考以下链接,完成设备调试的相关配置:
3. 增加一个WebView组件
步骤 1 - 在"resources/base/layout/ability_main.xml"文件中创建WebView,示例代码如下:
-
- ohos:id="$+id:webview"
- ohos:height="match_parent"
- ohos:width="match_parent">
步骤 2 - 在"slice/MainAbilitySlice.java"文件中通过如下方式获取WebView对象,示例代码如下:
- WebView webview = (WebView) findComponentById(ResourceTable.Id_webview);
—-结束
4. 通过WebView加载Web页面WebView加载页面分为加载Web页面和加载本地Web页面两种情况,接下来我们将分别进行介绍。
- WebView加载网络Web页面
在entry/src/main/config.json中申请网络权限ohos.permission.INTERNET,示例代码如下:
- module": {
- ......
- "reqPermissions": [
- {
- "name": "ohos.permission.INTERNET"
- }
- ]
- }
在"slice/MainAbilitySlice.java"文件中通过webview.load(String url)方法访问具体的Web页面,通过WebConfig类对WebView组件的行为进行配置,示例代码如下:
- WebConfig webConfig = webview.getWebConfig();
- // WebView加载URL,其中urlTextField为输入URL的TextField组件
- webview.load(urlTextField.getText());
在Web页面进行链接跳转时,WebView默认会打开目标网址,通过WebAgent对象可以定制该行为,示例代码如下
- webview.setWebAgent(new WebAgent() {
- [url=home.php?mod=space&uid=2735960]@Override[/url]
- public boolean isNeedLoadUrl(WebView webView, ResourceRequest request) {
- if (request == null || request.getRequestUrl() == null) {
- LogUtil.info(TAG,"WebAgent isNeedLoadUrl:request is null.");
- return false;
- }
- String url = request.getRequestUrl().toString();
- if (url.startsWith("http:") || url.startsWith("https:")) {
- webView.load(url);
- return false;
- } else {
- return super.isNeedLoadUrl(webView, request);
- }
- }
- });
除此之外,WebAgent对象还提供了相关的回调函数以观测页面状态的变更,如onLoadingPage、onPageLoaded、onError等方法。WebView提供Navigator类进行历史记录的浏览和处理,通过getNavigator()方法获取该类的对象,使用canGoBack()或canGoForward()方法检查是否可以向后或向前浏览,使用goBack()或goForward()方法向后或向前浏览,示例代码如下:
- Navigator navigator = webView.getNavigator();
- if (navigator.canGoBack()) {
- navigator.goBack();
- }
- if (navigator.canGoForward()) {
- navigator.goForward();
- }
WebView加载本地Web页面
将本地的HTML文件放在"resources/rawfile/"目录下,在本教程中命名为test.html。在HarmonyOS系统中,WebView要访问本地Web文件,需要通过DataAbility的方式进行访问,DataAbility的具体使用方法可以参考开发DataAbility。
在"entry/src/main/config.json"中完成DataAbility的声明,示例代码如下:
- module": {
- ......
- "abilities": [
- {
- "name": "com.huawei.codelab.DataAbility",
- "type": "data",
- "uri": "dataability://com.huawei.codelab.DataAbility"
- }
- ]
- }
另外需要实现一个DataAbility,通过实现openRawFile(Uri uri, String mode)方法,完成WebView对本地Web页面的访问,示例代码如下:
- public class DataAbility extends Ability {
- ...
- @Override
- public RawFileDescriptor openRawFile(Uri uri, String mode) throws FileNotFoundException {
- if (uri == null) {;
- return super.openRawFile(uri, mode);
- }
- String path = uri.getEncodedPath();
- int splitIndex = path.indexOf('/', 1);
- String providerName = Uri.decode(path.substring(1, splitIndex));
- String rawFilePath = Uri.decode(path.substring(splitIndex + 1));
- RawFileDescriptor rawFileDescriptor = null;
- try {
- rawFileDescriptor = getResourceManager().getRawFileEntry(rawFilePath).openRawFileDescriptor();
- } catch (IOException e) {
- // 异常处理
- }
- return rawFileDescriptor;
- }
- }
在"slice/MainAbilitySlice.java"中声明需要访问的文件路径,通过webview.load(String url)方法加载本地Web页面,可以通过WebConfig类的对象对WebView访问DataAbility的能力进行配置,示例代码如下:
- private static final String URL_LOCAL = "dataability://com.huawei.codelab.DataAbility/resources/rawfile/test.html";
- // 配置是否支持访问DataAbility资源,默认为true
- webConfig.setDataAbilityPermit(true);
- webview.load(URL_LOCAL);
5. 实现应用与WebView中的Web页面间的通信 本教程以本地Web页面"resources/rawfile/test.html"为例介绍如何实现应用与WebView中的Web页面间交互。
首先需要对WebConfig进行配置,使能WebView与Web页面JavaScript交互的能力,示例代码如下:
- // 配置是否支持JavaScript,默认值为false
- webConfig.setJavaScriptPermit(true);
- 应用调用Web页面
在"resources/rawfile/test.html"中编写callJS方法,待应用调用,示例代码如下:
在"slice/MainAbilitySlice.java"中实现应用对JavaScript的调用,示例代码如下:
- webview.executeJs("javascript:callJS('这是来自JavaSlice的消息')", msg -> {
- // 在这里处理Js的方法的返回值
- });
我们可以通过setBrowserAgent方法设置自定义BrowserAgent对象,以观测JavaScript事件及通知等,通过复写onJsMessageShow方法来接管Web页面弹出Alert对话框的事件,示例代码如下:
- webview.setBrowserAgent(new BrowserAgent(this) {
- @Override
- public boolean onJsMessageShow(WebView webView, String url, String message, boolean isAlert, JsMessageResult result) {
- LogUtil.info(TAG,"BrowserAgent onJsMessageShow : " + message);
- if (isAlert) {
- // 将Web页面的alert对话框改为ToastDialog方式提示
- new ToastDialog(getApplicationContext()).setText(message).setAlignment(LayoutAlignment.CENTER).show();
- // 对弹框进行确认处理
- result.confirm();
- return true;
- } else {
- return super.onJsMessageShow(webView, url, message, isAlert, result);
- }
- }
- });
- Web页面使用JavaScript调用应用
在"resources/rawfile/test.html"中编写按钮,当按钮被点击时实现JavaScript对应用的调用,示例代码如下:在"slice/MainAbilitySlice.java"中实现应用对JavaScript发起的调用的响应,示例代码如下:
- private static final String JS_NAME = "JsCallJava";
- webview.addJsCallback(JS_NAME, str -> {
- // 处理接收到的JavaScript发送来的消息,本教程通过ToastDialog提示确认收到Web页面发来的消息
- new ToastDialog(this).setText(str).setAlignment(LayoutAlignment.CENTER).show();
- // 返回给JavaScript
- return "Js Call Java Success";
- });
6. 恭喜你
通过本教程的学习,你已学会如何使用WebView组件。
7. 完整示例
下面我们通过一个完整的示例来看下WebView组件的使用。本示例可以让用户选择加载手动输入的Web URL,也可以加载本地Web页面,并实现了前进、后退及应用与Web页面交互的基本功能,实现效果如下:
- 点击"Load URL"按钮会加载上方网址的Web页面,通过"Go Back"和"Go Forward"按钮实现Web页面间的导航。
- 点击"Load Local HTML"按钮加载本地Web页面,点击"Send Message to Local HTML"或者Web页面中的"调用Java方法"按钮,实现应用与Web页面间的交互。
具体实现代码如下:- 布局与样式
在HUAWEI DevEco Studio中创建一个Phone的Empty Feature Ability(Java)模板工程,然后在生成的布局文件"resources/base/layout/ability_main.xml"中增加如下代码:
-
-
- xmlns:ohos="http://schemas.huawei.com/res/ohos"
- ohos:height="match_parent"
- ohos:orientation="vertical"
- ohos:width="match_parent">
-
- ohos:id="$+id:textField"
- ohos:height="30vp"
- ohos:width="match_parent"
- ohos:left_margin="5vp"
- ohos:right_margin="5vp"
- ohos:text_alignment="vertical_center"
- ohos:text="https://www.huawei.com/cn/"
- ohos:hint="请输入网址"
- ohos:basement="#000099"
- ohos:text_size="20fp">
-
-
- ohos:height="510vp"
- ohos:width="match_parent"
- ohos:top_margin="5vp"
- ohos:background_element="$graphic:background_button"
- ohos:orientation="horizontal">
-
- ohos:margin="5vp"
- ohos:height="500vp"
- ohos:id="$+id:webview"
- ohos:width="match_parent"/>
-
-
- ohos:id="$+id:loadUrl"
- ohos:height="30vp"
- ohos:left_margin="5vp"
- ohos:right_margin="5vp"
- ohos:top_margin="5vp"
- ohos:text="Load URL"
- ohos:background_element="$graphic:background_button"
- ohos:text_size="20fp"
- ohos:width="match_parent">
-
-
- ohos:height="30vp"
- ohos:width="match_parent"
- ohos:top_margin="5vp"
- ohos:orientation="horizontal">
-
- ohos:id="$+id:goback"
- ohos:height="match_parent"
- ohos:width="1"
- ohos:left_margin="5vp"
- ohos:right_margin="5vp"
- ohos:weight="1"
- ohos:text="Go Back"
- ohos:background_element="$graphic:background_button"
- ohos:text_size="20fp">
-
-
- ohos:id="$+id:forward"
- ohos:height="match_parent"
- ohos:width="1"
- ohos:text="GO Forward"
- ohos:right_margin="5vp"
- ohos:left_margin="5vp"
- ohos:weight="1"
- ohos:background_element="$graphic:background_button"
- ohos:text_size="20fp">
-
-
-
- ohos:width="match_parent"
- ohos:id="$+id:load_local_url"
- ohos:height="30vp"
- ohos:text="Load Local HTML"
- ohos:left_margin="5vp"
- ohos:top_margin="5vp"
- ohos:right_margin="5vp"
- ohos:background_element="$graphic:background_button"
- ohos:text_size="20fp">
-
-
- ohos:id="$+id:callJS"
- ohos:height="30vp"
- ohos:width="match_parent"
- ohos:left_margin="5vp"
- ohos:top_margin="5vp"
- ohos:right_margin="5vp"
- ohos:text="Send Message to Local HTML"
- ohos:background_element="$graphic:background_button"
- ohos:text_size="20fp">
-
在"resources/base/graphic/"目录下增加background_button.xml文件用来定义背景属性。
-
-
- ohos:shape="rectangle">
-
- ohos:radius="6vp"/>
-
- ohos:color="#e9e9e9"/>
- 首页代码
在"slice/MainAbilitySlice.java"中添加如下代码:
- import com.huawei.codelab.ResourceTable;
- import ohos.aafwk.ability.AbilitySlice;
- import ohos.aafwk.content.Intent;
- import ohos.agp.components.Button;
- import ohos.agp.components.TextField;
- import ohos.agp.components.webengine.BrowserAgent;
- import ohos.agp.components.webengine.JsMessageResult;
- import ohos.agp.components.webengine.Navigator;
- import ohos.agp.components.webengine.ResourceRequest;
- import ohos.agp.components.webengine.WebAgent;
- import ohos.agp.components.webengine.WebConfig;
- import ohos.agp.components.webengine.WebView;
- import ohos.agp.utils.LayoutAlignment;
- import ohos.agp.window.dialog.ToastDialog;
- public class MainAbilitySlice extends AbilitySlice {
- private static final String URL_LOCAL = "dataability://com.huawei.codelab.DataAbility/resources/rawfile/test.html";
- private static final String JS_NAME = "JsCallJava";
- private WebView webview;
- private TextField urlTextField;
- private Navigator navigator;
- @Override
- public void onStart(Intent intent) {
- super.onStart(intent);
- super.setUIContent(ResourceTable.Layout_ability_main);
- initView();
- }
- private void initView() {
- webview = (WebView) findComponentById(ResourceTable.Id_webview);
- urlTextField = (TextField) findComponentById(ResourceTable.Id_textField);
- navigator = webview.getNavigator();
- initButton();
- configWebView();
- }
- private void configWebView() {
- WebConfig webConfig = webview.getWebConfig();
- // 是否支持Javascript,默认值false
- webConfig.setJavaScriptPermit(true);
- webview.setWebAgent(new WebAgent() {
- @Override
- public boolean isNeedLoadUrl(WebView webView, ResourceRequest request) {
- if (request == null || request.getRequestUrl() == null) {
- return false;
- }
- String url = request.getRequestUrl().toString();
- if (url.startsWith("http:") || url.startsWith("https:")) {
- webView.load(url);
- return false;
- } else {
- return super.isNeedLoadUrl(webView, request);
- }
- }
- });
- webview.setBrowserAgent(new BrowserAgent(this) {
- @Override
- public boolean onJsMessageShow(WebView webView, String url, String message, boolean isAlert, JsMessageResult result) {
- if (isAlert) {
- new ToastDialog(getApplicationContext()).setText(message).setAlignment(LayoutAlignment.CENTER).show();
- result.confirm();
- return true;
- } else {
- return super.onJsMessageShow(webView, url, message, isAlert, result);
- }
- }
- });
- // 配置JS发来的消息处理
- webview.addJsCallback(JS_NAME, str -> {
- // 处理接收到的Js发送来的消息
- new ToastDialog(this).setText(str).setAlignment(LayoutAlignment.CENTER).show();
- // 返回给Js
- return "Js Call Java Success";
- });
- }
- private void initButton() {
- initLoadUrlButton();
- initGoBackButton();
- initGoForwardButton();
- initLoadLocalUrlButton();
- initCallJsButton();
- }
- private void initCallJsButton() {
- Button callJs = (Button) findComponentById(ResourceTable.Id_callJS);
- callJs.setClickedListener(component -> {
- webview.executeJs("javascript:callJS('这是来自JavaSlice的消息')", msg -> {
- // 在这里处理Java调用Js的方法的返回值
- });
- });
- }
- private void initLoadLocalUrlButton() {
- Button loadLocalUrlButton = (Button) findComponentById(ResourceTable.Id_load_local_url);
- loadLocalUrlButton.setClickedListener(component -> {
- webview.load(URL_LOCAL);
- });
- }
- private void initLoadUrlButton() {
- Button loadUrlButton = (Button) findComponentById(ResourceTable.Id_loadUrl);
- loadUrlButton.setClickedListener(component -> {
- webview.load(urlTextField.getText());
- });
- }
- private void initGoBackButton() {
- Button goBackButton = (Button) findComponentById(ResourceTable.Id_goback);
- goBackButton.setClickedListener(component -> {
- if (navigator.canGoBack()) {
- navigator.goBack();
- }
- });
- }
- private void initGoForwardButton() {
- Button goForwardButton = (Button) findComponentById(ResourceTable.Id_forward);
- goForwardButton.setClickedListener(component -> {
- if (navigator.canGoForward()) {
- navigator.goForward();
- }
- });
- }
- @Override
- public void onActive() {
- super.onActive();
- }
- @Override
- public void onForeground(Intent intent) {
- super.onForeground(intent);
- }
- }
- DataAbility实现
创建一个DataAbility,并添加如下代码:
- import java.io.FileNotFoundException;
- import java.io.IOException;
- import ohos.aafwk.ability.Ability;
- import ohos.aafwk.content.Intent;
- import ohos.global.resource.RawFileDescriptor;
- import ohos.utils.net.Uri;
- public class DataAbility extends Ability {
- @Override
- public void onStart(Intent intent) {
- super.onStart(intent);
- }
- @Override
- public RawFileDescriptor openRawFile(Uri uri, String mode) throws FileNotFoundException {
- if (uri == null) {
- return super.openRawFile(uri, mode);
- }
- String path = uri.getEncodedPath();
- final int splitIndex = path.indexOf('/', 1);
- final String providerName = Uri.decode(path.substring(1, splitIndex));
- String rawFilePath = Uri.decode(path.substring(splitIndex + 1));
- RawFileDescriptor rawFileDescriptor = null;
- try {
- rawFileDescriptor = getResourceManager().getRawFileEntry(rawFilePath).openRawFileDescriptor();
- } catch (IOException e) {
- // 处理异常
- }
- return rawFileDescriptor;
- }
- }
- 本地Web页面实现
在"resources/rawfile/"目录下创建本地Web页面test.html,示例代码如下:
-
-
-
- 本地html
-
-
-
这是一个本地HTML页面
-
-
-
-
说明
以上代码仅demo演示参考使用,产品化的代码需要考虑数据校验和国际化。