起因
最近冰墩墩也太火了,各路大神纷纷表示用代码画冰墩墩,而我也刚好看到一个前端大神 Austin 用 JavaScript 前端写了一个冰墩墩,大家可以下载代码,本地打开就可以看到这个可爱的冰墩墩了。代码在后文参考资料中,大家可以自己下载。
自己前端知识既然不够,而刚好自己也在学习鸿蒙,这不就想着怎么把这个顶流拉取到本地然后读取到我的鸿蒙代码中呢,这不我立马搜索一下:鸿蒙如何读取本地 HTML 。
然后就有了对 WebView 的探索,最后终于让我琢磨成功,运行成功后的样子预览图如下:
除了对本地文件的读取,还支持对网络页面的访问,比如我这里就是读取 HarmonyOS应用开发官网,读者当然可以自己想访问哪个网页就访问自己的网页。
什么是 Webview
其实现在很多应用 App 里都内置了 Web 网页,比如微信、淘宝。在 Android 中就是利用 WebView 这一组件实现。WebView 是一个基于 webkit 引擎、展现 web 页面的控件。
鸿蒙也不例外,也通过 WebView 来提供应用中集成 Web 页面的能力。
作用:
- 显示和渲染 Web 页面
- 直接使用 HTML 文件(网络上或本地 resources 中)作布局
- 可和 JavaScript 交互调用
WebView 使用
如果我们要加载远程网页,就要增加对网络的支持。在 config.json
中写入如下代码:
"module": { "package": "com.yuzhou1su.webviewdemo", "name": ".MyApplication", "mainAbility": "com.yuzhou1su.webviewdemo.MainAbility", "deviceType": [ "phone", "tv", "tablet" ], "reqPermissions": [ { "name": "ohos.permission.INTERNET" // 增加网络权限 } ],
在 layout 的 ability_main.xml
中创建 WebView
在"slice/MainAbilitySlice.java"文件中通过 webview.load(String url) 方法访问具体的 Web 页面,通过 WebConfig 类对 WebView 组件的行为进行配置,代码如下:
package com.yuzhou1su.webviewdemo.slice; import com.yuzhou1su.webviewdemo.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.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.yuzhou1su.webviewdemo.DataAbility/resources/rawfile/BingDwenDwen.html"; private static final String JS_NAME = "JsCallJava"; private WebView webview; private TextField urlTextField; @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); webview.getWebConfig().setDataAbilityPermit(true); //这个要加上,设置 webview 支持打开本地文件 urlTextField = (TextField) findComponentById(ResourceTable.Id_textField); 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(); initLoadLocalUrlButton(); } 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()); }); } @Override public void onActive() { super.onActive(); } @Override public void onForeground(Intent intent) { super.onForeground(intent); } }
在 background_ability_main.xml
写入如下代码:
<?xml version="1.0" encoding="UTF-8" ?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <solid ohos:color="#F8F1EAEA"/> </shape>
在 background_button.xml
写入如下代码:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:ohos="http://schemas.huawei.com/res/ohos" ohos:shape="rectangle"> <corners ohos:radius="20vp"/> <solid ohos:color="#FF46F5C3"/> </shape>
WebView 读取本地页面
将本地的 HTML 文件放在"resources/rawfile/"目录下,在本教程中命名为 BingDwenDwen.html 。在 HarmonyOS 系统中,WebView 要访问本地 Web 文件,需要通过 DataAbility 的方式进行访问,所以此处创建 DataAbility.java
文件,写入如下代码:
package com.yuzhou1su.webviewdemo; 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; } }
然后在 "entry/src/main/config.json" 中完成 DataAbility 的声明,代码如下:
{ "name": "com.yuzhou1su.webviewdemo.DataAbility", "type": "data", "uri": "dataability://com.yuzhou1su.webviewdemo.DataAbility" }
至此,整个 config.json
文件内容如下:
{ "app": { "bundleName": "com.yuzhou1su.webviewdemo", "vendor": "yuzhou1su", "version": { "code": 1000000, "name": "1.0.0" } }, "deviceConfig": {}, "module": { "package": "com.yuzhou1su.webviewdemo", "name": ".MyApplication", "mainAbility": "com.yuzhou1su.webviewdemo.MainAbility", "deviceType": [ "phone", "tv", "tablet" ], "reqPermissions": [ { "name": "ohos.permission.INTERNET" } ], "distro": { "deliveryWithInstall": true, "moduleName": "entry", "moduleType": "entry", "installationFree": true }, "abilities": [ { "skills": [ { "entities": [ "entity.system.home" ], "actions": [ "action.system.home" ] } ], "orientation": "unspecified", "name": "com.yuzhou1su.webviewdemo.MainAbility", "icon": "$media:icon", "description": "$string:mainability_description", "label": "$string:entry_MainAbility", "type": "page", "launchType": "standard" }, { "name": "com.yuzhou1su.webviewdemo.DataAbility", "type": "data", "uri": "dataability://com.yuzhou1su.webviewdemo.DataAbility" } ] } }
然后在 "slice/MainAbilitySlice.java" 中声明需要访问的文件路径,通过 webview.load(String url) 方法加载本地 Web 页面,可以通过 WebConfig 类的对象对 WebView 访问 DataAbility 的能力进行配置,示例代码如下:
private static final String URL_LOCAL = "dataability://com.yuzhou1su.webviewdemo.DataAbility/resources/rawfile/BingDwenDwen.html"; private static final String JS_NAME = "JsCallJava"; private WebView webview; private TextField urlTextField; @Override public void onStart(Intent intent) { super.onStart(intent); super.setUIContent(ResourceTable.Layout_ability_main); initView(); }
代码结构如下
然后运行一个 MataPad Pro,执行我们的代码:
如果点击打开在线网页,就能看到鸿蒙开发官网:
然后我们点击 打开本地网页,就能看到一个可爱的冰墩墩:
总结
至此,我们通过 WebView 来访问远程网页和本地 HTML 都成功了,但是这仅仅是很小的一个工作,奈何本人前端知识实在匮乏,比如冰墩墩让我画我肯定画不出来的,但是并不影响我对冰墩墩和鸿蒙开发的热爱。
如果大家还想继续研究 WebView,可以自己多多探索,如果大家看完我的文章,有兴趣去做出一个 APP 内置浏览器功能,那我的目的也就达到了。