├── 14.公共事件概述.md ├── 11.数据管理.md ├── 9.动画.md ├── 10.网络请求.md ├── 15.Promise、Async、Await、then.md ├── README.md ├── 8.通知.md ├── 13.应用状态管理Storage.md ├── 12.使用第三方库.md ├── 3.HarmonyOS开发术语.md ├── 1.HarmonyOS简介.md ├── 7.应用组件UIAbility.md ├── 6.常用组件.md ├── 4.HarmonyOS应用开发初探.md ├── 2.ArkTS基础语法.md ├── 5.ArkTS声明式UI入门.md └── LICENSE /14.公共事件概述.md: -------------------------------------------------------------------------------- 1 | 14.公共事件概述 2 | --- 3 | 4 | 公共事件(Common Event Service,CES)根据事件发送方不同,可分为系统公共事件和自定义公共事件。 5 | 6 | - 系统公共事件: 系统将收集到的事件信息根据系统策略发送给订阅该事件的用户程序。 7 | 公共事件包括终端设备用户可感知的亮灭屏时间,以及系统关键服务发布的系统事件(例如USB插拔、网络连接、系统升级)等。 8 | 9 | - 自定义公共事件: 由应用自身定义的期望特定订阅者可以接收的公共事件,这些公共事件往往与应用自身的业务逻辑相关。 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /11.数据管理.md: -------------------------------------------------------------------------------- 1 | 11.数据管理 2 | === 3 | 4 | ## 首选项 5 | 6 | 首选项为应用提供Key-Value键值型的数据存储能力,支持应用持久化轻量级数据,并对其进行增删除改查等。该存储对象中的数据会被缓存在内存中,因此它可以获得更快的存取速度 7 | 8 | 9 | ### 首选项的特点是: 10 | 11 | 1. 以Key-Value形式存储数据 12 | 13 | Key是不重复的关键字,Value是数据值。 14 | 15 | 2. 非关系型数据库 16 | 17 | 区别于关系型数据库,它不保证遵循ACID(Atomicity, Consistency, Isolation and Durability)特性,数据之间无关系。 18 | 19 | 进程中每个文件仅存在一个Preferences实例,应用获取到实例后,可以从中读取数据,或者将数据存入实例中。通过调用flush方法可以将实例中的数据回写到文件里。 20 | 21 | 22 | 23 | ### 约束与限制 24 | 因Storage实例会加载到内存中,建议存储的数据不超过一万条,并及时清理不再使用的实例,以便减少非内存开销。 25 | 数据中的key为string类型,要求非空且字符长度不超过80。 26 | 当数据中的value为string类型时,允许为空,字符长度不超过8192。 27 | 28 | 29 | 30 | 31 | 32 | ---------- 33 | 34 | 35 | - [上一篇:10.网络请求](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/10.%E7%BD%91%E7%BB%9C%E8%AF%B7%E6%B1%82.md) 36 | - [下一篇:12.使用第三方库](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/12.%E4%BD%BF%E7%94%A8%E7%AC%AC%E4%B8%89%E6%96%B9%E5%BA%93.md) 37 | 38 | 39 | 40 | --- 41 | 42 | - 邮箱 :charon.chui@gmail.com 43 | - Good Luck! 44 | -------------------------------------------------------------------------------- /9.动画.md: -------------------------------------------------------------------------------- 1 | 9.动画 2 | === 3 | 4 | ### 属性动画 5 | 6 | ```TypeScript 7 | Image($r('app.media.image1')) 8 | .animation({ 9 | duration: 1000, 10 | // 动画的播放速度,值越大动画播放越快,值越小播放越慢,为0时无动画效果。 11 | tempo: 1.0, 12 | delay: 0, 13 | curve: Curve.Linear, 14 | // 设置动画播放模式,默认播放完成后重头开始播放。 15 | playMode: PlayMode.Normal, 16 | // 播放次数,默认一次,设置为-1时表示无限次播放。 17 | iterations: 1 18 | }) 19 | ``` 20 | 21 | 1、animation属性作用域。animation自身也是组件的一个属性,其作用域为animation之前。即产生属性动画的属性须在animation之前声明,其后声明的将不会产生属性动画。 22 | 23 | 2、产生属性动画的属性变化时需触发UI状态更新。在本示例中,产生动画的属性width,其值是通过变量iconWidth从30变为100,故该变量iconWidth的改变需触发UI状态更新。 24 | 25 | 3、产生属性动画的属性本身需满足一定的要求,并非任何属性都可以产生属性动画。目前支持的属性包括width、height、position、opacity、backgroundColor、scale、rotate、translate等 26 | 27 | 28 | ### 页面动画 29 | 30 | 31 | 32 | 33 | 34 | 35 | ---------- 36 | 37 | 38 | - [上一篇:8.通知](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/8.%E9%80%9A%E7%9F%A5.md) 39 | - [下一篇:10.网络请求](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/10.%E7%BD%91%E7%BB%9C%E8%AF%B7%E6%B1%82.md) 40 | 41 | 42 | 43 | 44 | --- 45 | 46 | - 邮箱 :charon.chui@gmail.com 47 | - Good Luck! 48 | -------------------------------------------------------------------------------- /10.网络请求.md: -------------------------------------------------------------------------------- 1 | 10.网络请求 2 | === 3 | 4 | 按照以下步骤完成HTTP数据请求: 5 | 6 | - 导入http模块 7 | 8 | ```TypeScript 9 | import http from '@ohos.net.http'; 10 | ``` 11 | 12 | - 创建httpRequest对象 13 | 14 | 使用createHttp()创建一个httpRequest对象,里面包括常用的一些网络请求方法,比如request、destroy、on('headerReceive')等。 15 | 16 | ```TypeScript 17 | let httpRequest = http.createHttp(); 18 | ``` 19 | 20 | 需要注意的是每一个httpRequest对象对应一个http请求任务,不可复用。 21 | 22 | - 订阅请求头(可选) 23 | 24 | 用于订阅http响应头,此接口会比request请求先返回,可以根据业务需要订阅此消息。 25 | 26 | ```TypeScript 27 | httpRequest.on('headersReceive', (header) => { 28 | console.info('header: ' + JSON.stringify(header)); 29 | }); 30 | ``` 31 | 32 | - 发起http请求 33 | 34 | http模块支持常用的POST和GET等方法,封装在RequestMethod中。调用request方法发起网络请求,需要传入两个参数。第一个是请求的url地址,第二个是可选参数,类型为HttpRequestOptions,用于定义可选参数的类型和取值范围,包含请求方式、连接超时时间、请求头字段等。 35 | 36 | 使用Get请求,参数内容需要拼接到URL中进行发送,如下示例中在url后面拼接了两个自定义参数,分别命名为param1和param2,值分别为value1和value2: 37 | 38 | ```TypeScript 39 | let url= "https://EXAMPLE_URL?param1=v1¶m2=v2"; 40 | let promise = httpRequest.request( 41 | // 请求url地址 42 | url, 43 | { 44 | // 请求方式 45 | method: http.RequestMethod.GET, 46 | // 可选,默认为60s 47 | connectTimeout: 60000, 48 | // 可选,默认为60s 49 | readTimeout: 60000, 50 | // 开发者根据自身业务需要添加header字段 51 | header: { 52 | 'Content-Type': 'application/json' 53 | } 54 | }); 55 | ``` 56 | POST请求参数需要添加到extraData里面,如下示例中在extraData里面定义添加了两个自定义参数param1和param2,值分别为value1和value2: 57 | 58 | ```TypeScript 59 | let url = "https://EXAMPLE_URL"; 60 | let promise = httpRequest.request( 61 | // 请求url地址 62 | url, 63 | { 64 | // 请求方式 65 | method: http.RequestMethod.POST, 66 | // 请求的额外数据。 67 | extraData: { 68 | "param1": "value1", 69 | "param2": "value2", 70 | }, 71 | // 可选,默认为60s 72 | connectTimeout: 60000, 73 | // 可选,默认为60s 74 | readTimeout: 60000, 75 | // 开发者根据自身业务需要添加header字段 76 | header: { 77 | 'Content-Type': 'application/json' 78 | } 79 | }); 80 | ``` 81 | - 处理响应结果。 82 | 83 | data为网络请求返回的结果,err为请求异常时返回的结果。data的类型为HttpResponse。 84 | 85 | ```TypeScript 86 | promise.then((data) => { 87 | if (data.responseCode === http.ResponseCode.OK) { 88 | console.info('Result:' + data.result); 89 | console.info('code:' + data.responseCode); 90 | } 91 | }).catch((err) => { 92 | console.info('error:' + JSON.stringify(err)); 93 | }); 94 | ``` 95 | 其中data.responseCode为http请求返回的状态码,如果状态码为http.ResponseCode.OK(即200),则表示请求成功,更多状态码可以在ResponseCode中查看。 96 | 97 | data.result为服务器返回的业务数据,开发者可以根据自身业务场景解析此数据。 98 | 99 | 100 | 101 | 102 | 103 | 104 | ---------- 105 | 106 | 107 | - [上一篇:9.动画](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/9.%E5%8A%A8%E7%94%BB.md) 108 | - [下一篇:11.数据管理](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/11.%E6%95%B0%E6%8D%AE%E7%AE%A1%E7%90%86.md) 109 | 110 | 111 | --- 112 | 113 | - 邮箱 :charon.chui@gmail.com 114 | - Good Luck! 115 | -------------------------------------------------------------------------------- /15.Promise、Async、Await、then.md: -------------------------------------------------------------------------------- 1 | Promise、Async、Await、Then 2 | --- 3 | 4 | 5 | ### Promise 6 | 7 | Promise的中文意思是承诺。可以通俗的理解为: 把这个任务(函数)交给我,我来完成。 8 | 9 | Promise是JavaScript中用于处理异步操作的一个关键概念。 10 | 11 | 它代表了一个尚未完成但预期在将来完成的操作。 12 | 13 | 使用Promise可以避免所谓的回调地狱,即多层嵌套的回调函数,从而使代码更加清晰和易于维护。 14 | 15 | ```JavaScript 16 | let promise = new Promise(function(resolve, reject) { 17 | // 异步操作代码 18 | setTimeout(() => { 19 | resolve("操作成功"); 20 | }, 1000); 21 | }); 22 | 23 | promise.then((value) => { 24 | console.log(value); 25 | }); 26 | 27 | ``` 28 | 29 | 一个Promise有三种可能的状态: 30 | 31 | - pending(待定): 初始状态,即不是成功,也不是失败 32 | - fulfilled(已实现): 意味着操作成功完成 33 | - rejected(已拒绝): 意味着操作失败 34 | 35 | 36 | ``` 37 | let fulfilledPromise = Promise.resolve('成功'); 38 | let rejectedPromise = Promise.reject('失败'); 39 | 40 | fulfilledPromise.then(value => console.log(value)); // 输出成功 41 | rejectedPromise.catch(error => console.log(error)); // 输出失败 42 | ``` 43 | 44 | 45 | #### 链式调用和错误处理 46 | 47 | Promise的另一个优点是可以通过链式调用.then()和.catch()方法来处理复杂的异步流程。 48 | 49 | ``` 50 | new Promise((resolve, reject) => { 51 | setTimeout(() => resolve(1), 1000) 52 | }) 53 | .then(result => { 54 | console.log(result); // 输出1 55 | return result * 2; 56 | }) 57 | .then(result => { 58 | console.log(result); // 输出2 59 | return result * 3; 60 | }) 61 | .then(result => { 62 | console.log(result); // 输出3 63 | return result * 4; 64 | }) 65 | .catch(error => { 66 | console.log('error') 67 | }); 68 | ``` 69 | 70 | 71 | ### Async/Await 72 | 73 | Async和Await是建立在Promise之上的高级抽象,使得异步代码的编写和阅读更加接近于同步代码的风格。 74 | 75 | 76 | #### Async 77 | 78 | 通过在函数声明前加上async关键字,可以将任何函数转换为返回Promise的异步函数。 79 | 80 | 这意味着你可以使用.then()和.catch()来处理它们的结果。 81 | 82 | ``` 83 | async function asyncFunction() { 84 | return "异步操作完成"; 85 | } 86 | 87 | asyncFunction().then(value => console.log(value)); // 异步操作完成 88 | ``` 89 | 90 | 91 | #### Await 92 | 93 | await关键字只能在async函数内部使用。 94 | 95 | 它可以暂停async函数的执行,等待Promise的解决(resolve),然后以Promise的值继续执行函数。 96 | 97 | ``` 98 | async function asyncFunction() { 99 | let promise = new Promise((resolve, rejct) => { 100 | setTimeout(() => resolve("完成"), 1000) 101 | }); 102 | 103 | let result = await promise; // 等待,直到promise解决 104 | console.log(result); 105 | } 106 | 107 | asyncFunction(); 108 | ``` 109 | 110 | 使用await可以像同步代码一样处理异步任务,避免了过多的.then()嵌套。 111 | 112 | 所有await必须放在async函数中。 113 | 114 | #### 错误处理 115 | 116 | 在async/await中,错误处理可以通过传统的try...catch语句实现,这使得异步代码的错误处理更加直观。 117 | 118 | 119 | ``` 120 | async function asyncFunction() { 121 | try { 122 | let response = await fetch('http://example.com'); 123 | let data = await response.json(); 124 | } catch (error) { 125 | console.log('error'); 126 | } 127 | } 128 | 129 | asyncFunction(); 130 | 131 | ``` 132 | 133 | 134 | 在实际应用中,async和await使得处理复杂的异步逻辑更加简单,尤其是在涉及多个依次执行的异步操作时。 135 | 136 | 虽然async/await在很多情况下可以提供更加清晰和简洁的代码,但Promise也有其独特的优势。 137 | 138 | 例如,处理多个并行异步操作时,需要并行执行并等待它们全部完成,使用Promise.all()通常是更好的选择。 139 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | HarmonyOS Next 学习笔记 2 | === 3 | 4 | 5 | 6 | 7 | 8 | > 混沌未分天地乱,茫茫渺渺无人见。 9 | > 自从盘古破鸿蒙,开辟从兹清浊辨。 10 | > 覆载群生仰至仁,发明万物接为善。 11 | > 欲知造化会元功,须看西游释厄传。 12 | 13 | 14 | 鸿蒙,是一个汉语词语,亦作“鸿濛”。传说在盘古开天辟地之前,世界是一团混沌状,因此人们把那个时代称作鸿蒙时代,后来该词也常被用来泛指远古时代。 15 | 16 | 在"老子"看来,一切具体存在的本源都是混沌,万物是由混沌中产生的。“盘古开天辟地”的故事家喻户晓,而天地万物就是从混沌中孕育而生的。 17 | 18 | > 道生一,一生二,二生三,三生万物 19 | 20 | 21 | 目录 22 | === 23 | 24 | 25 | **HarmonyOS NEXT版本目前仅开发给合作的企业,个人开发者仍无法查看文档。** 26 | 27 | **因此目前是基于HarmonyOS 4.0版本,NEXT发布后会再更新** 28 | 29 | - [1.HarmonyOS简介][1] 30 | - [2.ArkTS基础语法][2] 31 | - [3.HarmonyOS开发术语][3] 32 | - [4.HarmonyOS应用开发初探][4] 33 | - [5.ArkTS声明式UI入门][5] 34 | - [6.常用组件][6] 35 | - [7.应用组件UIAbility][7] 36 | - [8.通知][8] 37 | - [9.动画][9] 38 | - [10.网络请求][10] 39 | - [11.数据管理][11] 40 | - [12.使用第三方库][12] 41 | - [13.应用状态管理Storage][13] 42 | 43 | 44 | [1]: https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/1.HarmonyOS%E7%AE%80%E4%BB%8B.md "1.HarmonyOS简介" 45 | [2]: https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/2.ArkTS%E5%9F%BA%E7%A1%80%E8%AF%AD%E6%B3%95.md "2.ArkTS基础语法" 46 | [3]: https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/3.HarmonyOS%E5%BC%80%E5%8F%91%E6%9C%AF%E8%AF%AD.md "3.HarmonyOS开发术语" 47 | [4]: https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/4.HarmonyOS%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91%E5%88%9D%E6%8E%A2.md "4.HarmonyOS应用开发初探" 48 | [5]: https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/5.ArkTS%E5%A3%B0%E6%98%8E%E5%BC%8FUI%E5%85%A5%E9%97%A8.md "5.ArkTS声明式UI入门" 49 | [6]: https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/6.%E5%B8%B8%E7%94%A8%E7%BB%84%E4%BB%B6.md "6.常用组件" 50 | [7]: https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/7.%E5%BA%94%E7%94%A8%E7%BB%84%E4%BB%B6UIAbility.md "7.应用组件UIAbility" 51 | [8]: https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/8.%E9%80%9A%E7%9F%A5.md "8.通知" 52 | [9]: https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/9.%E5%8A%A8%E7%94%BB.md "9.动画" 53 | [10]: https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/10.%E7%BD%91%E7%BB%9C%E8%AF%B7%E6%B1%82.md "10.网络请求" 54 | [11]: https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/11.%E6%95%B0%E6%8D%AE%E7%AE%A1%E7%90%86.md "11.数据管理" 55 | [12]: https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/12.%E4%BD%BF%E7%94%A8%E7%AC%AC%E4%B8%89%E6%96%B9%E5%BA%93.md "12.使用第三方库" 56 | [13]: https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/13.%E5%BA%94%E7%94%A8%E7%8A%B6%E6%80%81%E7%AE%A1%E7%90%86Storage.md "13.应用状态管理Storage" 57 | 58 | 59 | 60 | Developed By 61 | === 62 | 63 | * Charon Chui - 64 | 65 | 66 | License 67 | === 68 | 69 | Copyright (C) 2013 Charon Chui 70 | 71 | Licensed under the Apache License, Version 2.0 (the "License"); 72 | you may not use this file except in compliance with the License. 73 | You may obtain a copy of the License at 74 | 75 | http://www.apache.org/licenses/LICENSE-2.0 76 | 77 | Unless required by applicable law or agreed to in writing, software 78 | distributed under the License is distributed on an "AS IS" BASIS, 79 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 80 | See the License for the specific language governing permissions and 81 | limitations under the License. 82 | -------------------------------------------------------------------------------- /8.通知.md: -------------------------------------------------------------------------------- 1 | 8.通知 2 | === 3 | 4 | 5 | 在创建通知前需要先导入notificationManager模块,该模块提供通知管理的能力,包括发布、取消发布通知,创建、获取、移除通知通道等能力。 6 | 7 | ```TypeScript 8 | import notification from '@ohos.notificationManager'; 9 | ``` 10 | 11 | 12 | 基础类型通知主要应用于发送短信息、提示信息、广告推送等,支持普通文本类型、长文本类型、多行文本类型和图片类型,可以通过contentType指定通知的内容类型。下面以普通文本类型和图片类型为例来介绍基础通知的发布,其它基础类型您可以查阅API。 13 | 14 | 发布普通文本类型通知,需要设置contentType类型为ContentType.NOTIFICATION_CONTENT_BASIC_TEXT。 15 | 16 | ```TypeScript 17 | import notification from '@ohos.notificationManager'; 18 | 19 | @Entry 20 | @Component 21 | struct NotificationDemo { 22 | publishNotification() { 23 | let notificationRequest: notification.NotificationRequest = { // 描述通知的请求 24 | id: 1, // 通知ID 25 | slotType: notification.SlotType.SERVICE_INFORMATION, 26 | content: { // 通知内容 27 | contentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, // 普通文本类型通知 28 | normal: { // 基本类型通知内容 29 | title: '通知内容标题', 30 | text: '通知内容详情', 31 | additionalText: '通知附加内容', // 通知附加内容,是对通知内容的补充。 32 | } 33 | } 34 | } 35 | notification.publish(notificationRequest).then(() => { // 发布通知 36 | console.info('publish success'); 37 | }).catch((err) => { 38 | console.error(`publish failed, dcode:${err.code}, message:${err.message}`); 39 | }); 40 | } 41 | 42 | build() { 43 | Column() { 44 | Button('发送通知') 45 | .onClick(() => { 46 | this.publishNotification() 47 | }) 48 | } 49 | .width('100%') 50 | } 51 | } 52 | ``` 53 | 发布图片类型通知,需要设置contentType类型为ContentType.NOTIFICATION_CONTENT_PICTURE。 54 | 55 | ```TypeScript 56 | import notification from '@ohos.notificationManager'; 57 | import image from '@ohos.multimedia.image'; 58 | 59 | @Entry 60 | @Component 61 | struct NotificationTest1 { 62 | async publishPictureNotification() { 63 | // 将资源图片转化为PixelMap对象 64 | let resourceManager = getContext(this).resourceManager; 65 | let imageArray = await resourceManager.getMediaContent($r('app.media.bigPicture').id); 66 | let imageResource = image.createImageSource(imageArray.buffer); 67 | let pixelMap = await imageResource.createPixelMap(); 68 | 69 | let notificationRequest: notification.NotificationRequest = { // 描述通知的请求 70 | id: 1, 71 | content: { 72 | contentType: notification.ContentType.NOTIFICATION_CONTENT_PICTURE, 73 | picture: { 74 | title: '好物热销中', // 通知内容标题 75 | text: '展开查看详情', // 通知内容 76 | expandedTitle: '今日热门推荐', // 通知展开时的内容标题 77 | briefText: '这里一定有您喜欢的', // 通知概要内容,是对通知内容的总结 78 | picture: pixelMap // 通知的图片内容 79 | } 80 | } 81 | } 82 | 83 | notification.publish(notificationRequest).then(() => { // 发布通知 84 | console.info('publish success'); 85 | }).catch((err) => { 86 | console.error(`publish failed, dcode:${err.code}, message:${err.message}`); 87 | }); 88 | } 89 | 90 | build() { 91 | Column() { 92 | Button('发送大图通知') 93 | .onClick(() => { 94 | this.publishPictureNotification() 95 | }) 96 | } 97 | .width('100%') 98 | } 99 | } 100 | ``` 101 | 102 | ---------- 103 | 104 | 105 | - [上一篇:7.应用组件UIAbility](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/7.%E5%BA%94%E7%94%A8%E7%BB%84%E4%BB%B6UIAbility.md) 106 | - [下一篇:9.动画](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/9.%E5%8A%A8%E7%94%BB.md) 107 | 108 | 109 | 110 | 111 | 112 | --- 113 | 114 | - 邮箱 :charon.chui@gmail.com 115 | - Good Luck! 116 | -------------------------------------------------------------------------------- /13.应用状态管理Storage.md: -------------------------------------------------------------------------------- 1 | # 13.应用状态管理Storage 2 | 3 | 4 | 前面说了@State、@Link等装饰器仅能在页面内,即一个组件树上共享状态变量。 5 | 6 | 如果开发者要实现应用级的,或者多个页面的状态数据共享,就需要用到应用级别的状态管理的概念。ArkTS根据不同特性,提供了多种应用状态管理的能力: 7 | 8 | - LocalStorage:页面级UI状态存储,通常用于UIAbility内、页面间的状态共享。 9 | 10 | - AppStorage:特殊的单例LocalStorage对象,由UI框架在应用程序启动时创建,为应用程序UI状态属性提供中央存储。 11 | 12 | - PersistentStorage:持久化存储UI状态,通常和AppStorage配合使用,选择AppStorage存储的数据写入磁盘,以确保这些属性在应用程序重新启动时的值与应用程序关闭时的值相同。 13 | 14 | - Environment:应用程序运行的设备的环境参数,环境参数会同步到AppStorage中,可以和AppStorage搭配使用。 15 | 16 | 17 | 18 | ## LocalStorage 19 | 20 | LocalStorage是ArkTS为构建页面级别状态变量提供存储的内存内“数据库”。 21 | 22 | 23 | LocalStorage是页面级的UI状态存储,通过@Entry装饰器接收的参数可以在页面内共享同一个LocalStorage实例。LocalStorage也可以在UIAbility内,页面间共享状态。 24 | 25 | LocalStorage使用场景和相关的装饰器:@LocalStorageProp和@LocalStorageLink。 26 | 27 | 28 | ## AppStorage 29 | 30 | AppStorage是应用全局的UI状态存储,是和应用的进程绑定的,由UI框架在应用程序启动时创建,为应用程序UI状态属性提供中央存储。 31 | 32 | 和AppStorage不同的是,LocalStorage是页面级的,通常应用于页面内的数据共享。而AppStorage是应用级的全局状态共享,还相当于整个应用的“中枢”,持久化数据PersistentStorage和环境变量Environment都是通过AppStorage中转,才可以和UI交互。 33 | 34 | 35 | AppStorage是在应用启动的时候会被创建的单例。它的目的是为了提供应用状态数据的中心存储,这些状态数据在应用级别都是可访问的。AppStorage将在应用运行过程保留其属性。属性通过唯一的键字符串值访问。 36 | 37 | AppStorage可以和UI组件同步,且可以在应用业务逻辑中被访问。 38 | 39 | ## PersistentStorage 40 | 41 | LocalStorage和AppStorage都是运行时的内存,但是在应用退出再次启动后,依然能保存选定的结果,是应用开发中十分常见的现象,这就需要用到PersistentStorage。 42 | 43 | PersistentStorage是应用程序中的可选单例对象。此对象的作用是持久化存储选定的AppStorage属性,以确保这些属性在应用程序重新启动时的值与应用程序关闭时的值相同。 44 | 45 | 46 | 47 | PersistentStorage将选定的AppStorage属性保留在设备磁盘上。应用程序通过API,以决定哪些AppStorage属性应借助PersistentStorage持久化。UI和业务逻辑不直接访问PersistentStorage中的属性,所有属性访问都是对AppStorage的访问,AppStorage中的更改会自动同步到PersistentStorage。 48 | 49 | 50 | ### 限制条件 51 | PersistentStorage允许的类型和值有: 52 | 53 | - number, string, boolean, enum 等简单类型。 54 | - 可以被JSON.stringify()和JSON.parse()重构的对象。例如Date, Map, Set等内置类型则不支持,以及对象的属性方法不支持持久化。 55 | 56 | PersistentStorage不允许的类型和值有: 57 | 58 | - 不支持嵌套对象(对象数组,对象的属性是对象等)。因为目前框架无法检测AppStorage中嵌套对象(包括数组)值的变化,所以无法写回到PersistentStorage中。 59 | - 不支持undefined 和 null 。 60 | 61 | 持久化数据是一个相对缓慢的操作,应用程序应避免以下情况: 62 | 63 | - 持久化大型数据集。 64 | 65 | - 持久化经常变化的变量。 66 | 67 | PersistentStorage的持久化变量最好是小于2kb的数据,不要大量的数据持久化,因为PersistentStorage写入磁盘的操作是同步的,大量的数据本地化读写会同步在UI线程中执行,影响UI渲染性能。如果开发者需要存储大量的数据,建议使用数据库api。 68 | 69 | 70 | ## Environment 71 | 72 | 开发者如果需要应用程序运行的设备的环境参数,以此来作出不同的场景判断,比如多语言,暗黑模式等,需要用到Environment设备环境查询。 73 | 74 | Environment是ArkUI框架在应用程序启动时创建的单例对象。它为AppStorage提供了一系列描述应用程序运行状态的属性。Environment的所有属性都是不可变的(即应用不可写入),所有的属性都是简单类型。 75 | 76 | 77 | 78 | ## MVVM 79 | 80 | 应用通过状态去渲染更新UI是程序设计中相对复杂,但又十分重要的,往往决定了应用程序的性能。程序的状态数据通常包含了数组、对象,或者是嵌套对象组合而成。在这些情况下,ArkUI采取MVVM = Model + View + ViewModel模式,其中状态管理模块起到的就是ViewModel的作用,将数据与视图绑定在一起,更新数据的时候直接更新视图。 81 | 82 | - Model层:存储数据和相关逻辑的模型。它表示组件或其他相关业务逻辑之间传输的数据。Model是对原始数据的进一步处理。 83 | 84 | - View层:在ArkUI中通常是@Components修饰组件渲染的UI。 85 | 86 | - ViewModel层:在ArkUI中,ViewModel是存储在自定义组件的状态变量、LocalStorage和AppStorage中的数据。 87 | 88 | 自定义组件通过执行其build()方法或者@Builder装饰的方法来渲染UI,即ViewModel可以渲染View。 89 | View可以通过相应event handler来改变ViewModel,即事件驱动ViewModel的改变,另外ViewModel提供了@Watch回调方法用于监听状态数据的改变。 90 | 在ViewModel被改变时,需要同步回Model层,这样才能保证ViewModel和Model的一致性,即应用自身数据的一致性。 91 | ViewModel结构设计应始终为了适配自定义组件的构建和更新,这也是将Model和ViewModel分开的原因。 92 | 目前很多关于UI构造和更新的问题,都是由于ViewModel的设计并没有很好的支持自定义组件的渲染,或者试图去让自定义组件强行适配Model层,而中间没有用ViewModel来进行分离。例如,一个应用程序直接将SQL数据库中的数据读入内存,这种数据模型不能很好的直接适配自定义组件的渲染,所以在应用程序开发中需要适配ViewModel层。 93 | 94 | 95 | 96 | ![image](https://github.com/CharonChui/Pictures/blob/master/arkui_mvvm.png?raw=true) 97 | 98 | 99 | 100 | ### ViewModel的数据源 101 | 102 | ViewModel通常包含多个顶层数据源。@State和@Provide装饰的变量以及LocalStorage和AppStorage都是顶层数据源,其余装饰器都是与数据源做同步的数据。装饰器的选择取决于状态需要在自定义组件之间的共享范围。共享范围从小到大的排序是: 103 | 104 | - @State:组件级别的共享,通过命名参数机制传递,例如:CompA: ({ aProp: this.aProp }),表示传递层级(共享范围)是父子之间的传递。 105 | 106 | - @Provide:组件级别的共享,可以通过key和@Consume绑定,因此不用参数传递,实现多层级的数据共享,共享范围大于@State。 107 | 108 | - LocalStorage:页面级别的共享,可以通过@Entry在当前组件树上共享LocalStorage实例。 109 | 110 | - AppStorage:应用全局的UI状态存储,和应用进程绑定,在整个应用内的状态数据的共享。 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | ---------- 127 | 128 | 129 | - [上一篇:12.使用第三方库](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/12.%E4%BD%BF%E7%94%A8%E7%AC%AC%E4%B8%89%E6%96%B9%E5%BA%93.md) 130 | 131 | 132 | 133 | 134 | --- 135 | 136 | - 邮箱 :charon.chui@gmail.com 137 | - Good Luck! 138 | -------------------------------------------------------------------------------- /12.使用第三方库.md: -------------------------------------------------------------------------------- 1 | # 12.使用第三方库 2 | 3 | 4 | 5 | 目前提供了两种途径获取开源三方库: 6 | 7 | - 通过访问Gitee网站开源社区获取 8 | 在Gitee中,搜索OpenHarmony-TPC仓库,在tpc_resource中对三方库进行了资源汇总。 9 | 10 | - 通过HarmonyOS开发者官网提供的资源中心获取 11 | 进入HarmonyOS开发者官网选择 开发>DevEco Service,在DevEco Service页面下找到资源中心,在资源中心找到三方库入口。 12 | 13 | ## 常用三方库介绍 14 | 常用的三方库可以分为UI、动画、网络、数据、安全、多媒体、框架、工具等。 15 | 16 | ### UI库 17 | 18 | - PhototView:可以通过该组件快速实现图片的缩放、平移和旋转等操作。 19 | - RoundImageView:可以生成圆角矩形、或者椭圆形等图片形状。 20 | 21 | ### 网络库 22 | 23 | - HttpClient:其以耳熟能详的OKHTTP为基础,整合android-async-http、AutobahnAndroid、OkGo等库的功能特性,实现了自定义拦截器、cookie管理等功能。 24 | - axios:运行在nodejs和浏览器中的axios,也是比较熟知的基于promise的网络请求库,同样实现了强大的网络功能。 25 | 26 | ### 动画库 27 | 28 | - lottie:用于解决应用中复杂动画的问题。 29 | - reboud:可以模拟弹簧动力学,实现驱动物理效果等。 30 | 31 | 32 | 33 | ## 使用开源三方库lottie 34 | 35 | lottie是基于lottie-web开发,集成在三方库社区内的开源版本,是HarmonyOS系统中复杂动画的一种解决方案。 36 | 37 | 动画是传达想法和创造更好的用户交互体验的工具,常见使用动画的场景如下: 38 | 39 | - 启动动画:APP logo动画的播放。 40 | - 加载动画:网络请求的loading动画。 41 | - 上下拉刷新动画:请求更多资源时的刷新动画。 42 | - 按钮动画:切换按钮、编辑按钮、播放按钮等按钮的切换过渡动画。 43 | - 视图转场动画:一些场景的转场添加动画能够提升用户体验。 44 | 45 | lottie提供了使用JSON动画文件的解决方案,开发者可以在原生应用中像使用静态图像一样使用动画,而不用关注动画的实现过程,并且lottie具有一套完整的API控制动画的行为,可以让动画更具有交互性。 46 | 47 | ### lottie的安装与卸载 48 | 49 | #### 安装lottie 50 | 通过ohpm执行对应的指令,将lottie安装到项目中。 51 | 52 | ```TypeScript 53 | ohpm install @ohos/lottie 54 | ``` 55 | 56 | #### 卸载lottie 57 | 58 | 通过ohpm执行卸载指令,将lottie从项目中删除,其程序包和配置信息将会从项目中移除。 59 | ```TypeScript 60 | ohpm uninstall @ohos/lottie 61 | ``` 62 | #### 使用lottie 63 | 64 | 65 | 通过import指令在项目中引入lottie到文件中。 66 | 67 | ```TypeScript 68 | import lottie from '@ohos/lottie' 69 | ``` 70 | 71 | #### 构建Canvas画布 72 | 73 | lottie解析JSON动画文件的数据需要基于Canvas 画布进行2D渲染,所以在加载JSON动画之前,要先初始化渲染上下文,并在画面中创建Canvas画布区域,将对应的渲染上下文renderingContext传递给Canvas。 74 | ```TypeScript 75 | // 初始化渲染上下文 76 | private renderingSettings: RenderingContextSettings = new RenderingContextSettings(true) // 设置开启抗锯齿 77 | private renderingContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.renderingSettings) // 创建2D渲染上下文 78 | 79 | // 加载Canvas画布 80 | Canvas(this.renderingContext) 81 | ... 82 | ``` 83 | 84 | #### 使用lottie加载JSON动画 85 | 86 | 加载JSON动画需要用到loadAnimation方法,在方法中需配置相应的初始设置,包括渲染上下文、渲染方式以及JSON动画资源的路径等。 87 | 88 | 可以直接使用lottie.loadAnimation方法,也可以用一个animationItem实例来接收返回的animationItem对象。 89 | 90 | ```TypeScript 91 | // 用animationItem实例接收 92 | let animationItem = lottie.loadAnimation({ 93 | container: this.renderingContext, // 渲染上下文 94 | renderer: 'canvas', // 渲染方式 95 | loop: 10, // 设置为循环播放10次 96 | autoplay: true, // 是否自动播放,默认true 97 | path: 'common/lottie/data.json', // json路径 98 | }) 99 | // 或者直接使用 100 | lottie.loadAnimation({ 101 | container: this.renderingContext, // 渲染上下文 102 | renderer: 'canvas', // 渲染方式 103 | loop: true, // 默认为true 104 | autoplay: true, // 是否自动播放,默认true 105 | path: 'common/lottie/data.json', // json路径 106 | }) 107 | ``` 108 | 109 | #### lottie控制动画 110 | 111 | lottie内封装了包括状态控制,进度控制,播放设置控制和属性控制等多个API,用户可以利用这些API完成对动画的控制,实现更加灵活的交互效果。 112 | 113 | ```TypeScript 114 | // 播放、暂停、停止、销毁 可以使用lottie,也可以使用animationItem实例进行控制 115 | lottie.play(); // 从目前停止的帧开始播放 116 | lottie.stop(); // 停止播放,回到第0帧 117 | lottie.pause(); // 暂停该动画,在当前帧停止并保持 118 | lottie.togglePause(); // 切换暂停/播放状态 119 | lottie.destroy(); // 删除该动画,移除相应的元素标签等。在unmount的时候,需要调用该方法 120 | 121 | // 播放进度控制 122 | animationItem.goToAndStop(value, isFrame); // 跳到某个时刻/帧并停止。isFrame(默认false)指示value表示帧还是时间(毫秒) 123 | animationItem.goToAndPlay(value, isFrame); // 跳到某个时刻/帧并进行播放 124 | animationItem.goToAndStop(30, true); // 例:跳转到第30帧并停止 125 | animationItem.goToAndPlay(300); // 例:跳转到第300毫秒并播放 126 | 127 | // 控制帧播放 128 | animationItem.setSegment(5,15); // 限定动画资源播放时的整体帧范围,即设置动画片段 129 | animationItem.resetSegments(5,15); // 重置播放的动画片段 130 | animationItem.playSegments(arr, forceFlag); // arr可以包含两个数字或者两个数字组成的数组,forceFlag表示是否立即强制播放该片段 131 | animationItem.playSegments([10,20], false); // 例:播放完之前的片段,播放10-20帧 132 | animationItem.playSegments([[5,15],[20,30]], true); //例: 直接播放5-15帧和20-30帧 133 | 134 | // 动画基本属性控制 135 | lottie.setSpeed(speed); // 设置播放速度,speed为1表示正常速度 136 | lottie.setDirection(direction); // 设置播放方向,1表示正向播放,-1表示反向播放 137 | 138 | // 获取动画帧数属性 139 | animationItem.getDuration(); //获取动画时长 140 | ``` 141 | 142 | #### 事件订阅 143 | 144 | 在一些特殊场景下,比如开始加载动画或者动画播放结束时,可能需要执行相应的操作,在lottie中提供了事件订阅和取消订阅的功能,当触发对应的event,会执行传入的回调函数,用户可以在回调函数中完成要实现的功能。 145 | 146 | ```TypeScript 147 | // 订阅事件 148 | animationItem.addEventListener(event,function(){ 149 | // TODO something 150 | }) 151 | 152 | // 取消订阅事件 153 | animationItem.removeEventListener(event,function(){ 154 | // TODO something 155 | }) 156 | ``` 157 | 158 | 常见的event事件类型如下: 159 | 160 | ```TypeScript 161 | // event事件类型 162 | 'enterFrame' // 每进入一帧就会触发 163 | 'loopComplete' // 当前循环下播放(循环播放/非循环播放)结束时触发 164 | 'complete' // 播放完成时触发 165 | 'segmentStart' // 播放指定片段时触发,playSegments、resetSegments等方法刚开始播放指定片段时会发出,如果playSegments播放多个片段,多个片段最开始都会触发。 166 | 'destroy' // 销毁动画时触发 167 | 'data_ready' // 数据准备完成 168 | 'DOMLoaded' // 动画相关dom已经被添加 169 | 'error' // 出现错误 170 | 'data_failed' // 数据加载失败 171 | ``` 172 | 173 | 174 | 175 | 176 | ---------- 177 | 178 | 179 | - [上一篇:11.数据管理](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/11.%E6%95%B0%E6%8D%AE%E7%AE%A1%E7%90%86.md) 180 | - [上一篇:13.应用状态管理Storage](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/13.%E5%BA%94%E7%94%A8%E7%8A%B6%E6%80%81%E7%AE%A1%E7%90%86Storage.md) 181 | 182 | 183 | 184 | --- 185 | 186 | - 邮箱 :charon.chui@gmail.com 187 | - Good Luck! 188 | -------------------------------------------------------------------------------- /3.HarmonyOS开发术语.md: -------------------------------------------------------------------------------- 1 | 3.HarmonyOS开发术语 2 | === 3 | 4 | 应用模型是系统为开发者提供的应用程序所需能力的抽象提炼,它提供了应用程序必备的组件和运行机制。有了应用模型,开发者可以基于一套统一的模型进行应用开发,使应用开发更简单、高效。 5 | 6 | 应用模型的构成要素包括: 7 | 8 | - 应用组件 9 | 10 | 应用组件是应用的基本组成单位,是应用的运行入口。 11 | 用户启动、使用和退出应用过程中,应用组件会在不同的状态间切换,这些状态称为应用组件的生命周期。 12 | 应用组件提供生命周期的回调函数,开发者通过应用组件的生命周期回调感知应用的状态变化。 13 | 应用开发者在编写应用时,首先需要编写的就是应用组件,同时还需编写应用组件的生命周期回调函数,并在应用配置文件中配置相关信息。 14 | 15 | 这样,操作系统在运行期间通过配置文件创建应用组件的实例,并调度它的生命周期回调函数,从而执行开发者的代码。 16 | 17 | - 应用进程模型 18 | 19 | 应用进程模型定义应用进程的创建和销毁方式,以及进程间的通信方式。 20 | 21 | - 应用线程模型 22 | 23 | 应用线程模型定义应用进程内线程的创建和销毁方式、主线程和UI线程的创建方式、线程间的通信方式。 24 | 25 | - 应用任务管理模型(仅对系统应用开放) 26 | 27 | 应用任务管理模型定义任务(Mission)的创建和销毁方式,以及任务与组件间的关系。 28 | 29 | 所谓任务,即用户使用一个应用组件实例的记录。每次用户启动一个新的应用组件实例,都会生成一个新的任务。 30 | 31 | 例如,用户启动一个视频应用,此时在“最近任务”界面,将会看到视频应用这个任务,当用户点击这个任务时,系统会把该任务切换到前台,如果这个视频应用中的视频编辑功能也是通过应用组件编写的,那么在用户启动视频编辑功能时,会创建视频编辑的应用组件实例,在“最近任务”界面中,将会展示视频应用、视频编辑两个任务。 32 | 33 | - 应用配置文件 34 | 35 | 应用配置文件中包含应用配置信息、应用组件信息、权限信息、开发者自定义信息等,这些信息在编译构建、分发和运行阶段分别提供给编译工具、应用市场和操作系统使用。 36 | 37 | 38 | 39 | 鸿蒙应用实际是通过Ability框架进行开发的,Ability在英文解释中是能力的意思。鸿蒙认为开发一个应用程序,其实是对设备能力的一种实现。 40 | Ability是应用所具备能力的抽象,也是应用程序的重要组成部分。一个应用可以具备多种能力(即可以包含多个Ability),HarmonyOS支持应用以Ability为单位进行部署。 41 | 42 | 43 | 44 | ### Stage模型 45 | 46 | OpenHarmony API 9开始新增的模型,是**目前主推且会长期演进的模型**。 47 | 在该模型中,由于提供了AbilityStage、WindowStage等类作为应用组件和Window窗口的“舞台”,因此称这种应用模型为Stage模型。 48 | 49 | ![image](https://github.com/CharonChui/Pictures/blob/master/stage-concepts.png?raw=true) 50 | 51 | 52 | 53 | #### UIAbility组件和ExtensionAbility组件 54 | 55 | Stage模型提供UIAbility和ExtensionAbility两种类型的组件,这两种组件都有具体的类承载,支持面向对象的开发方式。 56 | 57 | ##### UIAbility 58 | 59 | UIAbility组件是一种包含UI的应用组件,主要用于和用户交互。 60 | 例如,图库类应用可以在UIAbility组件中展示图片瀑布流,在用户选择某个图片后,在新的页面中展示图片的详细内容。 61 | 同时用户可以通过返回键返回到瀑布流页面。UIAbility的生命周期只包含创建/销毁/前台/后台等状态,与显示相关的状态通过WindowStage的事件暴露给开发者。 62 | 63 | ##### ExtensionAbility 64 | 65 | ExtensionAbility组件是一种面向特定场景的应用组件。 66 | 开发者并不直接从ExtensionAbility派生,而是需要使用ExtensionAbility的派生类。 67 | 目前ExtensionAbility有用于卡片场景的FormExtensionAbility,用于输入法场景的InputMethodExtensionAbility,用于闲时任务场景的WorkSchedulerExtensionAbility等多种派生类,这些派生类都是基于特定场景提供的。 68 | 例如,用户在桌面创建应用的卡片,需要应用开发者从FormExtensionAbility派生,实现其中的回调函数,并在配置文件中配置该能力。 69 | ExtensionAbility派生类实例由用户触发创建,并由系统管理生命周期。在Stage模型上,普通应用开发者不能开发自定义服务,而需要根据自身的业务场景通过ExtensionAbility的派生类来实现。 70 | 71 | #### WindowStage 72 | 73 | 每个UIAbility类实例都会与一个WindowStage类实例绑定,该类提供了应用进程内窗口管理器的作用。它包含一个主窗口。也就是说UIAbility通过WindowStage持有了一个主窗口,该主窗口为ArkUI提供了绘制区域。 74 | 75 | #### Context 76 | 77 | 在Stage模型上,Context及其派生类向开发者提供在运行期可以调用的各种资源和能力。UIAbility组件和各种ExtensionAbility派生类都有各自不同的Context类,他们都继承自基类Context,但是各自又根据所属组件,提供不同的能力。 78 | 79 | #### AbilityStage 80 | 81 | 每个Entry类型或者Feature类型的HAP在运行期都有一个AbilityStage类实例,当HAP中的代码首次被加载到进程中的时候,系统会先创建AbilityStage实例。每个在该HAP中定义的UIAbility类,在实例化后都会与该实例产生关联。开发者可以使用AbilityStage获取该HAP中UIAbility实例的运行时信息。 82 | 83 | 84 | 85 | 86 | ### FA(Feature Ability)模型 87 | Ability框架架下具有UI界面的Ability类型,用于与用户进行交互。Feature Ability唯一对应一种模板,即Page模板(Page Ability)。 88 | 89 | ** 已经不再主推。 ** 90 | API version 8及更早版本的应用开发仅支持FA模型。 91 | 92 | FA模型将Ability分为FA(Feature Ability)和PA(Particle Ability)两种类型,其中FA支持Page Ability模板,PA支持Service ability、Data ability、以及Form ability模板。详情可参考FA模型综述。 93 | 94 | 95 | ### Stage模型与FA模型的区别 96 | 97 | - Stage模型与FA模型最大的区别在于Stage模型中,多个应用组件共享同一个ArkTS引擎实例; 98 | - 而FA模型中,每个应用组件独享一个ArkTS引擎实例。 99 | - 因此在Stage模型中,应用组件之间可以方便的共享对象和状态,同时减少复杂应用运行对内存的占用。 100 | - Stage模型作为主推的应用模型,开发者通过它能够更加便利地开发出分布式场景下的复杂应用。 101 | 102 | 103 | 104 | 105 | 106 | ### PA 107 | Particle Ability,是在FA模型的Ability框架下无界面的Ability,主要为Feature Ability提供服务与支持,例如作为后台服务提供计算能力,或作为数据仓库提供数据访问能力。Particle Ability有三种模板,分别为Service模板(Service Ability)、Data模板(Data Ability)、以及Form模板(Form Ability)。 108 | 109 | 110 | 111 | ### Ability 112 | 应用的基本组成部分,是应用所具备能力的抽象。Ability是系统调度应用的最小单元,是能够完成一个独立功能的组件,一个应用可以包含一个或多个Ability。 在FA模型与Stage模型,分别定义了不同类型的Ability。 113 | 114 | ### AMS 115 | Ability Manager Service,Ability管理服务。 116 | 117 | ### HAP 118 | OpenHarmony Ability Package,一个HAP文件包含应用的所有内容,由代码、资源、三方库及应用配置文件组成,其文件后缀名为.hap。 119 | 120 | 121 | ### HAR 122 | HAR(Harmony Archive)静态共享包 123 | 124 | ### HSP 125 | HSP(Harmony Shared Package)动态共享包 126 | 127 | 128 | ### App Pack 129 | 上架应用市场的应用包的组织方式,包含一个或多个hap包,后缀名为.app。 130 | 131 | ![image](https://github.com/CharonChui/Pictures/blob/master/hap_app_harmony.png?raw=true) 132 | 133 | 134 | - 一个开发态的Module编译后生成一个部署态的HAP,Module和HAP一一对应。 135 | - HAP中的module.json由开发视图中的app.json5和module.json5合成。 136 | - 所有的HAP最终会编译到一个App Pack中(以.app为后缀的包文件),用于发布到应用市场。 137 | 138 | ### App Component 139 | 应用组件,每个Ability就是一个应用级组件。 140 | 141 | ### ArkCompiler 142 | 方舟编译器,是OpenHarmony内置的组件化、可配置的多语言编译和运行平台,包含编译器、工具链、运行时等关键部件,支持高级语言在多种芯片平台的编译与运行,并支撑OpenHarmony标准操作系统及其应用和服务运行在手机、个人电脑、平板、电视、汽车和智能穿戴等多种设备上的需求。 143 | 144 | ### ArkTS 145 | OpenHarmony生态的应用开发语言。它在TypeScript(简称 TS)的基础上,扩展了声明式UI、状态管理等能力,让开发者可以以更简洁、更自然的方式开发应用。 146 | 147 | ### 方舟开发框架(ArkUI) 148 | OpenHarmony上原生UI框架。是一套极简、高性能、跨设备应用设计研发的UI开发框架,支撑开发者高效地构建跨设备应用UI界面。 149 | 150 | 方舟开发框架针对不同目的和技术背景的开发者提供了两种开发范式,分别是: 151 | 152 | - 基于ArkTS的声明式开发范式(简称“声明式开发范式”)适用于复杂度较大,团队合作度较高的程序。 153 | - 兼容JS的类Web开发范式(简称“类Web开发范式”),适用于界面较为简单的程序应用和卡片。 154 | 155 | ### XComponent 156 | ArkUI提供的组件接口,满足开发者自渲染的需求。 157 | 158 | ### UI Component 159 | UI组件,组成用户界面的一部分,可提供用户交互的能力。 160 | OpenHarmony提供了一套UI开发框架,即方舟开发框架(ArkUI框架)。方舟开发框架可为开发者提供应用UI开发所必需的能力,比如多种组件、布局计算、动画能力、UI交互、绘制等。 161 | 162 | 163 | 164 | 165 | 166 | ### Atomic Service 167 | 原子化服务,OpenHarmony提供的一种全新应用形态。具有独立入口,用户可通过点击、碰一碰、扫一扫等方式直接触发,无需显示安装,由系统静默安装后即可使用,为用户提供便捷服务。 168 | 169 | ### C API 170 | OpenHarmony SDK 提供的native开发接口。 171 | 172 | ### DevEco Device Tool 173 | 面向智能设备开发者,提供一站式的开发环境、一站式资源获取通道,实现了从芯片模板工程创建到开发资源挑选定制,再到编码、编译、调试、调优、烧录环节的全流程覆盖,帮助开发者实现智能硬件设备的高效开发。 174 | 175 | 176 | 177 | 178 | ### fp 179 | 180 | 设置字体大小时建议使用字体像素单位fp(font pixel),字体像素大小默认情况下与vp相同,即默认情况下1fp=1vp。如果用户在设置中选择了更大的字体,字体的实际显示大小就会在vp的基础上乘以scale系数,即1fp=1vp*scale。 181 | 182 | 183 | ### vp 184 | 185 | 虚拟像素(virtual pixel, vp)是一台设备针对应用而言所具有的虚拟尺寸(区别于屏幕硬件本身的像素单位)。它提供了一种灵活的方式来适应不同屏幕密度的显示效果。 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | ---------- 194 | 195 | 196 | - [上一篇:2.ArkTS入门](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/2.ArkTS%E5%9F%BA%E7%A1%80%E8%AF%AD%E6%B3%95.md) 197 | - [下一篇:4.HarmonyOS应用开发初探](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/4.HarmonyOS%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91%E5%88%9D%E6%8E%A2.md) 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | --- 206 | 207 | - 邮箱 :charon.chui@gmail.com 208 | - Good Luck! 209 | -------------------------------------------------------------------------------- /1.HarmonyOS简介.md: -------------------------------------------------------------------------------- 1 | 1.HarmonyOS简介 2 | === 3 | 4 | [HarmonyOS](https://developer.huawei.com/consumer/cn/discover/)是新一代的智能终端操作系统,为不同设备的智能化、互联与协同提供了统一的语言。带来简洁、流畅、连续、安全可靠的全场景交互体验。 5 | 6 | 是由全球开发者共建的开源分布式操作系统,具备面向全场景、分布式等特点,是一款“全(全领域)・ 新 (新一代)・ 开(开源)・ 放(开放)”的操作系统。 7 | 8 | 9 | ### 万物互联 10 | 11 | > 混沌未分天地乱,茫茫渺渺无人见。 12 | > 自从盘古破鸿蒙,开辟从兹清浊辨。 13 | > 覆载群生仰至仁,发明万物接为善。 14 | > 欲知造化会元功,须看西游释厄传。 15 | 16 | 17 | 18 | 鸿蒙,是一个汉语词语,亦作“鸿濛”。传说在盘古开天辟地之前,世界是一团混沌状,因此人们把那个时代称作鸿蒙时代,后来该词也常被用来泛指远古时代。 19 | 20 | 在老子看来,一切具体存在的本源都是混沌,万物是由混沌中产生的。“盘古开天辟地”的故事家喻户晓,而天地万物就是从混沌中孕育而生的。 21 | 22 | > 道生一,一生二,二生三,三生万物 23 | 24 | 25 | 鸿蒙OS:定位万物互联时代的操作系统 26 | 27 | 鸿蒙的定位不是替代安卓,而是实现万物互联。 28 | 29 | “1+8+N”是HarmonyOS打造的全场景战略。 30 | 其中,“1”是智能手机,“8”是指大屏、音箱、眼镜、手表、车机、耳机、平板电脑和PC“八大行星”。围绕着关键的八大行星,周边还有合作伙伴开发的N个“卫星”,指的是移动办公、智能家居、运动健康、影音娱乐及智能出行等板块的延伸业务。 31 | 32 | 33 | 34 | 35 | ### HarmonyOS版本: 36 | 37 | 鸿蒙操作系统是华为自主研发的,2012年华为开始组建团队研发鸿蒙操作系统,并于2019年8月9日在华为开发者大会上正式发布了鸿蒙操作系统,即HarmonyOS 1.0。 38 | 39 | - Harmony OS 1.0 : 2019年8月9日 40 | - Harmony OS 2.0 : 2020年9月10日 41 | - Harmony OS 3.1 :2022年11月4日 42 | - Harmony OS 4.0 : 2023年8月4日 43 | - Harmony OS NEXT : 预计2024年Q1 44 | 45 | ### Harmony OS 特点 46 | 47 | 在Harmony OS 2.0发布会上提到了: 一为万物 万物归一 48 | 49 | 50 | 51 | 表明了Harmony OS的目标是万物互联,并不是单一的一个移动端操作系统。 52 | 53 | 系统内核的对比: 54 | 55 | - Android 56 | - 基于linux的宏内核设计,宏内核包含了操作系统绝大多数的功能和模块。 57 | - 这些功能和模块都具有最高的权限,只要一个模块出错,整个系统就会崩溃。 58 | 59 | - Harmony OS 60 | - 基于微内核设计,微内核仅包含了操作系统必要的功能模块(任务管理、内存分配等)。 61 | - 必要的模块处在核心地位具有最高权限,其他模块不具有最高权限。 62 | - 也就是其他模块出现问题,对于整个系统的运行是没有阻碍的。微内核稳定性更高。 63 | - 鸿蒙的运行速度快,大约快60%。 64 | 65 | 66 | 在Harmony OS 3.0发布时宣布,从3.0开始的SDK 8.0和9.0已经完全不支持Java开发。 67 | 68 | #### 宏内核、微内核 69 | 70 | 华为最新发布的鸿蒙系统采用了微内核架构,而安卓系统采用了宏内核。采用微内核架构的还有windows系统内核、塞班系统、google正在研发的Fuchsia系统(用于替代android)。 71 | 那什么是宏内核,什么是微内核? 72 | 73 | ##### 宏内核 74 | 宏内核(Monolithic kernel)又称单内核,其特征是操作系统内核的所有模块(包括进程调度、内存管理、文件系统、设备驱动等)均运行在内核态,具备直接操作硬件的能力,这类操作系统包括UNIX/Linux、FreeBSD等。 75 | ![image](https://github.com/CharonChui/Pictures/blob/master/hong_kenal.png?raw=true) 76 | 77 | 78 | ##### 微内核 79 | 80 | ![image](https://github.com/CharonChui/Pictures/blob/master/hong_wei_kenal.png?raw=true) 81 | 82 | 将单个功能或模块(如文件系统、设备驱动等)从内核中拆分出来,作为一个独立的服务部署到独立的运行环境中;内核仅保留极少的功能,为这些服务提供通信等基础能力,使其能够互相协作以完成操作系统所必需的功能。 83 | 84 | 这种架构被称为微内核(Microkernel)。 85 | 86 | 也就是说微内核只包含了内核最核心的功能,例如进程调度、内存管理、通信。 87 | 88 | 在微内核架构下,服务与服务之间是完全隔离的,单个服务即使出现故障或受到安全攻击,也不会直接导致整个操作系统崩溃或被攻破,从而能有效提高操作系统的可靠性与安全性。 89 | 此外,微内核架构带来了机制与策略的进一步分离,也可以更方便地为不同场景定制不同的服务,从而更好地适应不用的应用需求。 90 | 91 | 92 | 微内核优点 : 稳定性好 , 实时性好 ; 93 | 94 | 微内核缺点 : 高度模块化 , 模块之间只能通过消息传递信息 , 效率低 ; 95 | 96 | ## OpenHarmony 97 | 98 | [OpenHarmony](https://www.openharmony.cn/)是由开放原子开源基金会(OpenAtom Foundation)孵化及运营的开源项目,目标是面向全场景、全连接、全智能时代,基于开源的方式,搭建一个智能终端设备操作系统的框架和平台,促进万物互联产业的繁荣发展。 99 | 100 | [开放原子开源基金会(OpenAtom Foundation)](https://www.openatom.cn/)是致力于推动全球开源产业发展的非营利机构,由阿里巴巴、百度、华为、浪潮、360、腾讯、招商银行联合发起,于 2020 年 6 月登记成立,“立足中国,面向世界”,是我国在开源领域的首个基金会。 101 | 开放原子开源基金会的主管单位是工信部。 102 | 103 | 104 | 105 | 106 | 107 | ### 技术架构 108 | 109 | OpenHarmony整体遵从分层设计,从下向上依次为: 110 | 111 | - 内核层 112 | - 系统服务层 113 | - 框架层 114 | - 应用层 115 | 116 | 系统功能按照“系统 > 子系统 > 组件”逐级展开,在多设备部署场景下,支持根据实际需求裁剪某些非必要的组件。 117 | 118 | OpenHarmony技术架构如下所示: 119 | ![image](https://github.com/CharonChui/Pictures/blob/master/openharmony_archi.png?raw=true) 120 | 121 | 122 | ##### 内核层 123 | 124 | - 内核子系统:采用多内核(Linux内核或者LiteOS)设计,支持针对不同资源受限设备选用适合的OS内核。内核抽象层(KAL,Kernel Abstract Layer)通过屏蔽多内核差异,对上层提供基础的内核能力,包括进程/线程管理、内存管理、文件系统、网络管理和外设管理等。 125 | 支持适用于嵌入式设备及资源受限设备,具有小体积、高性能、低功耗等特征的LiteOS内核;支持基于linux kernel演进的适用于标准系统的linux内核。 126 | - 驱动子系统:驱动框架(HDF)是系统硬件生态开放的基础,提供统一外设访问能力和驱动开发、管理框架。 127 | 128 | 129 | ##### 系统服务层 130 | 131 | 系统服务层是OpenHarmony的核心能力集合,通过框架层对应用程序提供服务。该层包含以下几个部分: 132 | 133 | - 系统基本能力子系统集:为分布式应用在多设备上的运行、调度、迁移等操作提供了基础能力,由分布式软总线、分布式数据管理、分布式任务调度、公共基础库、多模输入、图形、安全、AI等子系统组成。 134 | - 基础软件服务子系统集:提供公共的、通用的软件服务,由事件通知、电话、多媒体、DFX(Design For X) 等子系统组成。 135 | - 增强软件服务子系统集:提供针对不同设备的、差异化的能力增强型软件服务,由智慧屏专有业务、穿戴专有业务、IoT专有业务等子系统组成。 136 | - 硬件服务子系统集:提供硬件服务,由位置服务、用户IAM、穿戴专有硬件服务、IoT专有硬件服务等子系统组成。 137 | - 根据不同设备形态的部署环境,基础软件服务子系统集、增强软件服务子系统集、硬件服务子系统集内部可以按子系统粒度裁剪,每个子系统内部又可以按功能粒度裁剪。 138 | 139 | ##### 框架层 140 | 141 | 框架层为应用开发提供了C/C++/JS等多语言的用户程序框架和Ability框架,适用于JS语言的ArkUI框架,以及各种软硬件服务对外开放的多语言框架API。根据系统的组件化裁剪程度,设备支持的API也会有所不同。 142 | 143 | ##### 应用层 144 | 145 | 应用层包括系统应用和第三方非系统应用。应用由一个或多个FA(Feature Ability)或PA(Particle Ability)组成。其中,FA有UI界面,提供与用户交互的能力;而PA无UI界面,提供后台运行任务的能力以及统一的数据访问抽象。基于FA/PA开发的应用,能够实现特定的业务功能,支持跨设备调度与分发,为用户提供一致、高效的应用体验。 146 | 147 | 148 | 149 | ### 技术特性 150 | 151 | #### 硬件互助,资源共享 152 | 主要通过下列模块达成 153 | 154 | - 分布式软总线 155 | 分布式软总线是多设备终端的统一基座,为设备间的无缝互联提供了统一的分布式通信能力,能够快速发现并连接设备,高效地传输任务和数据。 156 | 分布式软总线旨在为OpenHarmony系统提供跨进程或跨设备的通信能力,主要包含软总线和进程间通信两部分。其中,软总线为应用和系统提供近场设备间分布式通信的能力,提供不区分通信方式的设备发现,连接,组网和传输功能;而进程间通信则提供了对设备内或设备间无差别的进程间通信能力。 157 | 158 | - 分布式数据管理 159 | 160 | 分布式数据管理基于分布式软总线,实现了应用程序数据和用户数据的分布式管理。用户数据不再与单一物理设备绑定,业务逻辑与数据存储分离,应用跨设备运行时数据无缝衔接,为打造一致、流畅的用户体验创造了基础条件 161 | 162 | - 分布式任务调度 163 | 164 | 分布式任务调度基于分布式软总线、分布式数据管理、分布式Profile等技术特性,构建统一的分布式服务管理(发现、同步、注册、调用)机制,支持对跨设备的应用进行远程启动、远程调用、绑定/解绑、以及迁移等操作,能够根据不同设备的能力、位置、业务运行状态、资源使用情况并结合用户的习惯和意图,选择最合适的设备运行分布式任务 165 | 166 | - 设备虚拟化 167 | 168 | 分布式设备虚拟化平台可以实现不同设备的资源融合、设备管理、数据处理,将周边设备作为手机能力的延伸,共同形成一个超级虚拟终端。 169 | 170 | #### 一次开发,多端部署 171 | 172 | OpenHarmony提供用户程序框架、Ability框架以及UI框架,能够保证开发的应用在多终端运行时保证一致性。一次开发、多端部署。 173 | 174 | 多终端软件平台API具备一致性,确保用户程序的运行兼容性。 175 | 176 | - 支持在开发过程中预览终端的能力适配情况(CPU/内存/外设/软件资源等)。 177 | - 支持根据用户程序与软件平台的兼容性来调度用户呈现。 178 | 179 | #### 统一OS,弹性部署 180 | 181 | OpenHarmony通过组件化和组件弹性化等设计方法,做到硬件资源的可大可小,在多种终端设备间,按需弹性部署,全面覆盖了ARM、RISC-V、x86等各种CPU,从百KiB到GiB级别的RAM。 182 | 183 | 184 | ### 语言编译运行时 185 | 186 | 语言运行时提供了JS、C/C++语言程序的编译、执行环境,提供支撑运行时的基础库,以及关联的API接口、编译器和配套工具。 187 | OpenHarmony三方组件,是经过验证可在OpenHarmony系统上可重复使用的软件组件,可帮助开发者快速开发OpenHarmony系统或应用。 188 | 189 | 根据其开发语言分为了2种: 190 | 191 | - 一种是使用JavaScript和TypeScript语言的三方组件,通常以源码或OpenHarmony HAR的方式引入,在应用开发中使用。 192 | - 另一种是C和C++语言的三方组件,通常以源码或OpenHarmony hpm包的方式引入,在应用开发中以NAPI的方式使用,或直接编译在OpenHarmony操作系统镜像中。 193 | 194 | 当前OpenHarmony提供了UI,动画,图片,多媒体,文件数据,网络,安全,工具,其他等类型的三方组件。 195 | 196 | ### 版本 197 | 198 | - OpenHarmony 1.0 : 2020-09 199 | - OpenHarmony 2.x : 2021-06 200 | - OpenHarmony 3.x : 2021-09 201 | - OpenHarmony 4.0 : 2023-10 202 | 203 | 204 | ### Harmony OS和Open Harmony的关系 205 | 206 | - OpenHarmony就是一个操作系统的内核,OpenHarmony是鸿蒙操作系统的底座。 207 | - 一直到 Harmony OS 4都是配备Open Harmony的安卓,还是安卓(使用了鸿蒙的部分技术) 208 | - Harmony OS Next是基于Open Harmony,是一个全新的操作系统,和Android没有任何关系。 209 | - Harmony OS Next / Open Harmony使用HAP格式(HAP无法原生在Android上运行,且Android的APK也无法直接在Harmony OS Next / Open Harmony上原生运行) 210 | 211 | 212 | 原因: 213 | 214 | - 最开始鸿蒙发布的时候是被迫的 215 | - 后来Open Harmony 3已经像样了,但是又遇到了高通芯片面临驱动问题 216 | - 最终到现在有了麒麟芯片和高通芯片(目前有些爱好者已经在小米上刷上了Open Harmony, 为什么小米能刷Open Harmony,这是因为现在高通开放了一部分驱动,所以开源爱好者可以据此来做一定的修改)的支持才推Harmony OS Next 217 | 218 | 219 | ### 鸿蒙是否是Android套壳 220 | 221 | 222 | ![image](https://github.com/CharonChui/Pictures/blob/master/android_os_1.png?raw=true) 223 | 224 | Android的主要构成可以分为两部分: 225 | 226 | - AOSP,Android系统的核心 227 | - GMS,谷歌服务 也就是之前谷歌禁止华为用的那套东西。 228 | 简单来说GMS就是基于Android系统并嵌入了谷歌自己的一套体系。 比如使用谷歌地图、谷歌应用市场、谷歌账号。 229 | 但是国内绝大部分人都不会使用谷歌地图、谷歌账号等,所以华为使用了HMS进行代替。国内各个手机厂商都有一套自己的服务体系,所以GMS不用也就不用了。 230 | 231 | 232 | 鸿蒙和Android因为一项被称为AOSP(Android Open Source Project,Android开放源代码项目)的项目变得有一定关系,这个项目正是操作系统接口库这个级别的技术,任何操作系统只要集成这个接口库,其上面就可以运行Android的应用程序了。比如,Windows11集成了AOSP之后,就可以运行Android应用程序了。 233 | 234 | 其实,OpenHarmony才是华为历时多年研发的正牌操作系统,HarmonyOS算是OpenHarmony的发行版,就跟Android与MIUI、ColorOS等系统关系一样。 235 | 236 | OpenHarmony本身并不集成AOSP,所以在OpenHarmony设备上是无法运行Android程序的。 237 | 238 | 239 | 240 | ![image](https://github.com/CharonChui/Pictures/blob/master/harmony_11.png?raw=true) 241 | 242 | 在Harmony OS Next之前,鸿蒙的组成可以看成是AOSP、HSM、万物互联分布式、全场景四个部分。 243 | 244 | 245 | 什么才算一个新的操作系统 246 | 247 | miui算不算? emui算不算? 显然这些都不是新OS,都是安卓。 248 | 249 | 因为他们的应用程序都是用的针对安卓开发的应用程序,脱离安卓的可执行程序,这些系统连启动桌面都做不到(因为桌面都是APK格式)。 250 | 尽管鸿蒙OS和阿里云OS都声称重写了代码,但是只要跑的是APK,就无法改变这是安卓的实质。 251 | 252 | 所以从Harmony OS 1 到 Harmony OS 4都可以说是Android套壳。 253 | 254 | 但是随着Harmony OS Next的发布,Harmony OS 已经是一个新操作系统了。因为他有了自己的新格式HAP,且使用ArkTS进行开发。 255 | 256 | ### 鸿蒙是指什么 257 | 258 | 鸿蒙一般是指鸿蒙生态。 259 | 鸿蒙生态包括Open Harmony和 Harmony OS,当然还包括开发工具以及周边的一些开发库。 260 | 261 | ### HAP是否是APK套壳 262 | 263 | 264 | HAP格式的发展经历了两个阶段: 265 | 266 | - 第一个阶段: 解压后会有一个APK当做程序入口,这是早起生态不完善导致 267 | - 第二个阶段: 解压后不会再有APK,HAP无法通过简单的解包、打包转换为APK 268 | 所以HAP并不是APK的套壳。 269 | 270 | 271 | ### 开发语言 272 | 273 | HAP使用的开发语言是ArkTS,ArkTS是Type Script的超集,而Type Script是JavaScript的超集,所以就是用的网页那一套开发。 274 | 275 | ### Harmony OS NEXT 276 | 277 | 自鸿蒙1.0发布开始大家显示沸腾,然后纷纷失望表示是套娃。 278 | 但是华为开发者大会HDC 2023上宣布Harmony OS NEXT是纯鸿蒙不在支持Android APK文件的安装。 279 | 280 | 纯鸿蒙是最终的目标,自从华为开始发布Harmony OS开始,最终的目标肯定是将其发展成为和Android、iOS、Windows、Mac并肩的系统。 281 | 但是任何一个软件开发者都能知道这有多难,所以Harmony OS选择了兼容Android的方式起步,现在已经发展到了4.0版本,真正属于自己的系统也该慢慢起步了。 282 | 283 | 284 | Harmony Next放弃安卓虚拟机,是一个全新的系统,所以到时候会面临一个难题,就是APP的重新开发和数据迁移,这其实需要一个过程,并不是会直接一刀切新手机全部只安装Harmony Next。 285 | 286 | 前期肯定Harmony OS还是会基于双框架模式。 例如微信: 第一步是先发版新版APP,这样本地数据就会在手机内存中。 287 | 288 | 但是华为能否推动其他公司单独对APP进行鸿蒙的重新开发,毕竟要增加各大公司的开发成本,在Android、iOS之外又要组建鸿蒙开发团队,并且要实现目前的功能。 289 | 290 | 这个是很难的,那为什么还会发布Harmony OS NEXT呢? 没有生态的系统是不可能成功的。 291 | 292 | 不要忘了Open Harmony属于开放原子开源基金会,而开放原子开源基金会的主管单位是工信部。 293 | 华为是先把代码捐献给开放原子开源基金会,之后又fork回来做自己的Harmony OS Next。 294 | 295 | 为什么要这么做? 296 | 这不仅仅是一个全新操作系统的问题,更多的是现在中国和美国在打科技战,所以中国需要一个完全可控、且生态能统一的数字基建自主操作系统。 297 | 298 | 所以纯自研鸿蒙肯定是起来, 国内的公司也会积极进行鸿蒙APP的单独适配开发。 299 | 300 | 301 | 该版本发布后华为就可以宣称: 新内核完全自研,底层接口不再兼容Android原生,系统权限设计思路对标iOS,不支持三方WebView、RN动态下发等常见开发框架。 302 | 303 | 304 | 305 | ### Harmony OS开发套件 306 | ![image](https://github.com/CharonChui/Pictures/blob/master/hmos_develop_kit.png?raw=true) 307 | 308 | 309 | 310 | 311 | ------------- 312 | 313 | 314 | - [下一篇:2.ArkTS基础语法](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/2.ArkTS%E5%9F%BA%E7%A1%80%E8%AF%AD%E6%B3%95.md) 315 | 316 | 317 | 318 | 319 | --- 320 | 321 | - 邮箱 :charon.chui@gmail.com 322 | - Good Luck! 323 | -------------------------------------------------------------------------------- /7.应用组件UIAbility.md: -------------------------------------------------------------------------------- 1 | 7.应用组件UIAbility 2 | === 3 | 4 | 5 | ## UIAbility 6 | 7 | UIAbility是一种包含用户界面的应用组件,主要用于和用户进行交互。 8 | 9 | UIAbility也是系统调度的单元,为应用提供窗口在其中绘制界面。 10 | 11 | 12 | UIAbility是一种包含用户界面的应用组件,主要用于和用户进行交互。UIAbility也是系统调度的单元,为应用提供窗口在其中绘制界面。 13 | 14 | 每一个UIAbility实例,都对应于一个最近任务列表中的任务。 15 | 16 | 一个应用可以有一个UIAbility,也可以有多个UIAbility,如下图所示。例如浏览器应用可以通过一个UIAbility结合多页面的形式让用户进行的搜索和浏览内容;而聊天应用增加一个“外卖功能”的场景,则可以将聊天应用中“外卖功能”的内容独立为一个UIAbility,当用户打开聊天应用的“外卖功能”,查看外卖订单详情,此时有新的聊天消息,即可以通过最近任务列表切换回到聊天窗口继续进行聊天对话。 17 | 18 | 一个UIAbility可以对应于多个页面,建议将一个独立的功能模块放到一个UIAbility中,以多页面的形式呈现。例如新闻应用在浏览内容的时候,可以进行多页面的跳转使用。 19 | 20 | ![image](https://github.com/CharonChui/Pictures/blob/master/UIAbility_single_more.png?raw=true) 21 | 22 | ## UIAbility内页面的跳转和数据传递 23 | 24 | ### Page 25 | 26 | 在一个应用包含一个UIAbility的场景下,可以通过新建多个页面来实现和丰富应用的内容 27 | 28 | src/main/ets/pages目录中的文件 29 | 30 | ### 路由 31 | 32 | 页面间的导航可以通过页面路由router模块来实现。页面路由模块根据页面url找到目标页面,从而实现跳转。通过页面路由模块,可以使用不同的url访问不同的页面,包括跳转到UIAbility内的指定页面、用UIAbility内的某个页面替换当前页面、返回上一页面或指定的页面等。 33 | url路径需要在entry/src/resources/base/profile/main_pages.json中配置 34 | 35 | 36 | 页面跳转的几种方式,根据需要选择一种方式跳转即可。 37 | 38 | 方式一:API9及以上,router.pushUrl()方法新增了mode参数,可以将mode参数配置为router.RouterMode.Single单实例模式和router.RouterMode.Standard多实例模式。 39 | 在单实例模式下:如果目标页面的url在页面栈中已经存在同url页面,离栈顶最近同url页面会被移动到栈顶,移动后的页面为新建页,原来的页面仍然存在栈中,页面栈的元素数量不变;如果目标页面的url在页面栈中不存在同url页面,按多实例模式跳转,页面栈的元素数量会加1。 40 | 41 | 说明 42 | 当页面栈的元素数量较大或者超过32时,可以通过调用router.clear()方法清除页面栈中的所有历史页面,仅保留当前页面作为栈顶页面。 43 | 44 | 45 | ```TypeScript 46 | router.pushUrl({ 47 | url: 'pages/Second', 48 | params: { 49 | src: 'Index页面传来的数据', 50 | } 51 | }, router.RouterMode.Single) 52 | ``` 53 | 54 | 55 | 方式二:API9及以上,router.replaceUrl()方法新增了mode参数,可以将mode参数配置为router.RouterMode.Single单实例模式和router.RouterMode.Standard多实例模式。 56 | 在单实例模式下:如果目标页面的url在页面栈中已经存在同url页面,离栈顶最近同url页面会被移动到栈顶,替换当前页面,并销毁被替换的当前页面,移动后的页面为新建页,页面栈的元素数量会减1;如果目标页面的url在页面栈中不存在同url页面,按多实例模式跳转,页面栈的元素数量不变。 57 | 58 | ```TypeScript 59 | router.replaceUrl({ 60 | url: 'pages/Second', 61 | params: { 62 | src: 'Index页面传来的数据', 63 | } 64 | }, router.RouterMode.Single) 65 | ``` 66 | 67 | 第二个页面获取 68 | ```TypeScript 69 | import router from '@ohos.router'; 70 | 71 | @Entry 72 | @Component 73 | struct Second { 74 | src: string = (router.getParams() as Record)['src']; 75 | // 页面刷新展示 76 | ... 77 | } 78 | ``` 79 | 第二个页面返回: 80 | ```TypeScript 81 | router.back() 82 | ``` 83 | 84 | 页面返回和参数接收 85 | 经常还会遇到一个场景,在Second页面中,完成了一些功能操作之后,希望能返回到Index页面,那我们要如何实现呢? 86 | 87 | 在Second页面中,可以通过调用router.back()方法实现返回到上一个页面,或者在调用router.back()方法时增加可选的options参数(增加url参数)返回到指定页面。 88 | 89 | 说明 90 | 调用router.back()返回的目标页面需要在页面栈中存在才能正常跳转。 91 | 例如调用router.pushUrl()方法跳转到Second页面,在Second页面可以通过调用router.back()方法返回到上一个页面。 92 | 例如调用router.clear()方法清空了页面栈中所有历史页面,仅保留当前页面,此时则无法通过调用router.back()方法返回到上一个页面。 93 | 返回上一个页面。 94 | 95 | ```TypeScript 96 | router.back(); 97 | ``` 98 | 99 | 返回到指定页面。 100 | ```TypeScript 101 | router.back({ 102 | url: 'pages/Index', 103 | params: { 104 | src: 'Second页面传来的数据', 105 | } 106 | }) 107 | ``` 108 | 109 | 从Second页面返回到Index页面。在Index页面通过调用router.getParams()方法,获取Second页面传递过来的自定义参数。 110 | 111 | ##### 说明 112 | 调用router.back()方法,不会新建页面,返回的是原来的页面,在原来页面中@State声明的变量不会重复声明,以及也不会触发页面的aboutToAppear()生命周期回调,因此无法直接在变量声明以及页面的aboutToAppear()生命周期回调中接收和解析router.back()传递过来的自定义参数。 113 | 114 | 可以放在业务需要的位置进行参数解析。示例代码在Index页面中的onPageShow()生命周期回调中进行参数的解析。 115 | ```TypeScript 116 | import router from '@ohos.router'; 117 | @Entry 118 | @Component 119 | struct Index { 120 | @State src: string = ''; 121 | onPageShow() { 122 | this.src = (router.getParams() as Record)['src']; 123 | } 124 | // 页面刷新展示 125 | ... 126 | } 127 | ``` 128 | 129 | 130 | 即在调用router.back()方法之前,可以先调用router.enableBackPageAlert()方法开启页面返回询问对话框功能。 131 | 132 | #### 说明 133 | router.enableBackPageAlert()方法开启页面返回询问对话框功能,只针对当前页面生效。例如在调用router.pushUrl()或者router.replaceUrl()方法,跳转后的页面均为新建页面,因此在页面返回之前均需要先调用router.enableBackPageAlert()方法之后,页面返回询问对话框功能才会生效。 134 | 如需关闭页面返回询问对话框功能,可以通过调用router.disableAlertBeforeBackPage()方法关闭该功能即可。 135 | ```TypeScript 136 | router.enableBackPageAlert({ 137 | message: 'Message Info' 138 | }); 139 | 140 | router.back(); 141 | ``` 142 | 143 | ### UIAbility的生命周期 144 | 145 | 为了实现多设备形态上的裁剪和多窗口的可扩展性,系统对组件管理和窗口管理进行了解耦。 146 | 147 | UIAbility的生命周期包括Create、Foreground、Background、Destroy四个状态,WindowStageCreate和WindowStageDestroy为窗口管理器(WindowStage)在UIAbility中管理UI界面功能的两个生命周期回调,从而实现UIAbility与窗口之间的弱耦合。 148 | 149 | 所以要注意WindowStageCreate和WindowStageDestroy并不是UIAbility的生命周期状态。 150 | 151 | 152 | ![image](https://github.com/CharonChui/Pictures/blob/master/uiability_shengmingzhouqi.png?raw=true) 153 | 154 | 155 | 156 | - Create状态,在UIAbility实例创建时触发,系统会调用onCreate回调。可以在onCreate回调中进行相关初始化操作。例如用户打开电池管理应用,在应用加载过程中,在UI页面可见之前,可以在onCreate回调中读取当前系统的电量情况,用于后续的UI页面展示。 157 | 158 | - UIAbility实例创建完成之后,在进入Foreground之前,系统会创建一个WindowStage。每一个UIAbility实例都对应持有一个WindowStage实例。 159 | WindowStage为本地窗口管理器,用于管理窗口相关的内容,例如与界面相关的获焦/失焦、可见/不可见。 160 | 161 | 可以在onWindowStageCreate回调中,设置UI页面加载、设置WindowStage的事件订阅。 162 | 163 | 在onWindowStageCreate(windowStage)中通过loadContent接口设置应用要加载的页面。 164 | 165 | ```TypeScript 166 | import UIAbility from '@ohos.app.ability.UIAbility'; 167 | import window from '@ohos.window'; 168 | 169 | export default class EntryAbility extends UIAbility { 170 | ... 171 | 172 | onWindowStageCreate(windowStage: window.WindowStage) { 173 | // 设置UI页面加载 174 | // 设置WindowStage的事件订阅(获焦/失焦、可见/不可见) 175 | ... 176 | 177 | windowStage.loadContent('pages/Index', (err, data) => { 178 | ... 179 | }); 180 | } 181 | ... 182 | } 183 | ``` 184 | 185 | 例如用户打开游戏应用,正在打游戏的时候,有一个消息通知,打开消息,消息会以弹窗的形式弹出在游戏应用的上方,此时,游戏应用就从获焦切换到了失焦状态,消息应用切换到了获焦状态。对于消息应用,在onWindowStageCreate回调中,会触发获焦的事件回调,可以进行设置消息应用的背景颜色、高亮等操作。 186 | 187 | 188 | 189 | - Foreground和Background状态,分别在UIAbility切换至前台或者切换至后台时触发。 190 | 分别对应于onForeground回调和onBackground回调。 191 | 192 | onForeground回调,在UIAbility的UI页面可见之前,即UIAbility切换至前台时触发。可以在onForeground回调中申请系统需要的资源,或者重新申请在onBackground中释放的资源。 193 | 194 | onBackground回调,在UIAbility的UI页面完全不可见之后,即UIAbility切换至后台时候触发。可以在onBackground回调中释放UI页面不可见时无用的资源,或者在此回调中执行较为耗时的操作,例如状态保存等。 195 | 196 | 例如用户打开地图应用查看当前地理位置的时候,假设地图应用已获得用户的定位权限授权。在UI页面显示之前,可以在onForeground回调中打开定位功能,从而获取到当前的位置信息。 197 | 198 | 当地图应用切换到后台状态,可以在onBackground回调中停止定位功能,以节省系统的资源消耗。 199 | 200 | - onWindowStageDestroy: 对应于onWindowStageCreate回调。在UIAbility实例销毁之前,则会先进入onWindowStageDestroy回调,我们可以在该回调中释放UI页面资源。 201 | 例如在onWindowStageCreate中设置的获焦/失焦等WindowStage订阅事件。 202 | 203 | - Destroy状态,在UIAbility销毁时触发。可以在onDestroy回调中进行系统资源的释放、数据的保存等操作。 204 | 例如用户使用应用的程序退出功能,会调用UIAbilityContext的terminalSelf()方法,从而完成UIAbility销毁。或者用户使用最近任务列表关闭该UIAbility实例时,也会完成UIAbility的销毁。 205 | 206 | 207 | #### WindowStageCreate和WindowStageDestroy状态 208 | 209 | 210 | ![image](https://github.com/CharonChui/Pictures/blob/master/arkui_uiability_windowstage.png?raw=true) 211 | 212 | UIAbility实例创建完成之后,在进入Foreground之前,系统会创建一个WindowStage。WindowStage创建完成后会进入onWindowStageCreate()回调,可以在该回调中设置UI加载、设置WindowStage的事件订阅。 213 | 214 | 在onWindowStageCreate()回调中通过loadContent()方法设置应用要加载的页面,并根据需要调用on('windowStageEvent')方法订阅WindowStage的事件(获焦/失焦、可见/不可见)。 215 | 216 | - 每一个UIAbility都包含一个Context属性。 217 | 218 | 219 | ### UIAbility的启动模式 220 | 221 | 对于浏览器或者新闻等应用,用户在打开该应用,并浏览访问相关内容后,回到桌面,再次打开该应用,显示的仍然是用户当前访问的界面。 222 | 223 | 对于应用的分屏操作,用户希望使用两个不同应用(例如备忘录应用和图库应用)之间进行分屏,也希望能使用同一个应用(例如备忘录应用自身)进行分屏。 224 | 225 | 对于文档应用,用户从文档应用中打开一个文档内容,回到文档应用,继续打开同一个文档,希望打开的还是同一个文档内容。 226 | 227 | 基于以上场景的考虑,UIAbility当前支持singleton(单实例模式)、multiton(多实例模式)和specified(指定实例模式)3种启动模式。 228 | 229 | 对启动模式的详细说明如下: 230 | 231 | #### singleton(单实例模式) 232 | 当用户打开浏览器或者新闻等应用,并浏览访问相关内容后,回到桌面,再次打开该应用,显示的仍然是用户当前访问的界面。 233 | 234 | 这种情况下可以将UIAbility配置为singleton(单实例模式)。每次调用startAbility()方法时,如果应用进程中该类型的UIAbility实例已经存在,则复用系统中的UIAbility实例,系统中只存在唯一一个该UIAbility实例。 235 | 236 | 即在最近任务列表中只存在一个该类型的UIAbility实例。 237 | 238 | singleton启动模式,也是默认情况下的启动模式。 239 | 240 | singleton启动模式,每次调用startAbility()启动UIAbility时,如果应用进程中该类型的UIAbility实例已经存在,则复用系统中的UIAbility实例,并回调UIAbility的onNewWant()回调,不会进入其onCreate()和onWindowStageCreate()回调。系统中只存在唯一一个该UIAbility实例。 241 | 242 | singleton启动模式的开发使用,在module.json5文件中的“launchType”字段配置为“singleton”即可。 243 | 244 | { 245 | "module": { 246 | ... 247 | "abilities": [ 248 | { 249 | "launchType": "singleton", 250 | ... 251 | } 252 | ] 253 | } 254 | } 255 | 256 | 257 | 258 | 259 | #### multiton(多实例模式) 260 | 261 | 用户在使用分屏功能时,希望使用两个不同应用(例如备忘录应用和图库应用)之间进行分屏,也希望能使用同一个应用(例如备忘录应用自身)进行分屏。 262 | 263 | 这种情况下可以将UIAbility配置为multiton(多实例模式)。每次调用startAbility()方法时,都会在应用进程中创建一个该类型的UIAbility实例。 264 | 265 | 即在最近任务列表中可以看到有多个该类型的UIAbility实例。 266 | 267 | multiton启动模式,每次调用startAbility()方法时,都会在应用进程中创建一个该类型的UIAbility实例。 268 | 269 | multiton启动模式的开发使用,在module.json5文件中的“launchType”字段配置为“multiton”即可。 270 | 271 | 272 | #### specified(指定实例模式) 273 | 274 | 用户打开文档应用,从文档应用中打开一个文档内容,回到文档应用,继续打开同一个文档,希望打开的还是同一个文档内容;以及在文档应用中新建一个新的文档,每次新建文档,希望打开的都是一个新的空白文档内容。 275 | 276 | 这种情况下可以将UIAbility配置为specified(指定实例模式)。在UIAbility实例新创建之前,允许开发者为该实例创建一个字符串Key,新创建的UIAbility实例绑定Key之后,后续每次调用startAbility方法时,都会询问应用使用哪个Key对应的UIAbility实例来响应startAbility请求。如果匹配有该UIAbility实例的Key,则直接拉起与之绑定的UIAbility实例,否则创建一个新的UIAbility实例。运行时由UIAbility内部业务决定是否创建多实例。 277 | 278 | 279 | specified启动模式,根据业务需要是否创建一个新的UIAbility实例。在UIAbility实例创建之前,会先进入AbilityStage的onAcceptWant回调,在onAcceptWant回调中为每一个UIAbility实例创建一个Key,后续每次调用startAbility()方法创建该类型的UIAbility实例都会询问使用哪个Key对应的UIAbility实例来响应startAbility()请求。 280 | 281 | specified启动模式的开发使用的步骤如下所示。 282 | 283 | 在module.json5文件中的“launchType”字段配置为“specified”。 284 | { 285 | "module": { 286 | ... 287 | "abilities": [ 288 | { 289 | "launchType": "specified", 290 | ... 291 | } 292 | ] 293 | } 294 | } 295 | 在调用startAbility()方法的want参数中,增加一个自定义参数来区别UIAbility实例,例如增加一个“instanceKey”自定义参数。 296 | // 在启动指定实例模式的UIAbility时,给每一个UIAbility实例配置一个独立的Key标识 297 | function getInstance() { 298 | ... 299 | } 300 | let context:common.UIAbilityContext = ...; // context为调用方UIAbility的UIAbilityContext 301 | let want: Want = { 302 | deviceId: '', // deviceId为空表示本设备 303 | bundleName: 'com.example.myapplication', 304 | abilityName: 'SpecifiedAbility', 305 | moduleName: 'specified', // moduleName非必选 306 | parameters: { // 自定义信息 307 | instanceKey: getInstance(), 308 | }, 309 | } 310 | context.startAbility(want).then(() => { 311 | ... 312 | }).catch((err: BusinessError) => { 313 | ... 314 | }) 315 | 在被拉起方UIAbility对应的AbilityStage的onAcceptWant生命周期回调中,解析传入的want参数,获取“instanceKey”自定义参数。根据业务需要返回一个该UIAbility实例的字符串Key标识。如果之前启动过此Key标识的UIAbility,则会将之前的UIAbility拉回前台并获焦,而不创建新的实例,否则创建新的实例并启动。 316 | onAcceptWant(want: want): string { 317 | // 在被启动方的AbilityStage中,针对启动模式为specified的UIAbility返回一个UIAbility实例对应的一个Key值 318 | // 当前示例指的是device Module的EntryAbility 319 | if (want.abilityName === 'MainAbility') { 320 | return `DeviceModule_MainAbilityInstance_${want.parameters.instanceKey}`; 321 | } 322 | return ''; 323 | } 324 | 例如在文档应用中,可以对不同的文档实例内容绑定不同的Key值。当每次新建文档的时候,可以传入不同的新Key值(如可以将文件的路径作为一个Key标识),此时AbilityStage中启动UIAbility时都会创建一个新的UIAbility实例;当新建的文档保存之后,回到桌面,或者新打开一个已保存的文档,回到桌面,此时再次打开该已保存的文档,此时AbilityStage中再次启动该UIAbility时,打开的仍然是之前原来已保存的文档界面。 325 | 326 | 327 | ### UIAbilityContext 328 | 329 | UIAbilityContext是UIAbility的上下文环境,继承自Context。 330 | 331 | UIAbility类拥有自身的上下文信息,该信息为UIAbilityContext类的实例,UIAbilityContext类拥有abilityInfo、currentHapModuleInfo等属性。 332 | 333 | 通过UIAbilityContext可以获取UIAbility的相关配置信息,如包代码路径、Bundle名称、Ability名称和应用程序需要的环境状态等属性信息,以及可以获取操作UIAbility实例的方法(如startAbility()、connectServiceExtensionAbility()、terminateSelf()等)。 334 | 335 | 在UIAbility中可以通过this.context获取UIAbility实例的上下文信息。 336 | 337 | 338 | ### EventHub 339 | 340 | EventHub为UIAbility提供了事件机制,使它们能够进行订阅、取消订阅和处罚事件等数据通信能力。 341 | 342 | 在基类Context中,提供了EventHub对象,可用于UIAbility组件实例内通信。 343 | 344 | ```TypeScript 345 | class EntryAbility extends UIAbility { 346 | onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { 347 | let eventhub = this.context.eventhub; 348 | 349 | eventhub.on('event1', (data: string) => { 350 | ... 351 | }); 352 | } 353 | } 354 | ``` 355 | 在UI中通过eventHub.emit()发送事件: 356 | ```TypeScript 357 | import common from '@ohos.app.ability.common'; 358 | 359 | @Entry 360 | @Component 361 | struct Index { 362 | private context = getContext(this) as common.UIAbilityContext; 363 | 364 | eventHubFunc() { 365 | // 不带参数触发自定义“event1”事件 366 | this.context.eventHub.emit('event1'); 367 | // 带1个参数触发自定义“event1”事件 368 | this.context.eventHub.emit('event1', 1); 369 | // 带2个参数触发自定义“event1”事件 370 | this.context.eventHub.emit('event1', 2, 'test'); 371 | } 372 | } 373 | ``` 374 | 375 | 可以根据需要调用eventHub.off()方法取消该事件的订阅。 376 | 377 | 378 | 379 | ---------- 380 | 381 | 382 | - [上一篇:6.常用组件](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/6.%E5%B8%B8%E7%94%A8%E7%BB%84%E4%BB%B6.md) 383 | - [下一篇:8.通知](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/8.%E9%80%9A%E7%9F%A5.md) 384 | 385 | 386 | 387 | 388 | --- 389 | 390 | - 邮箱 :charon.chui@gmail.com 391 | - Good Luck! 392 | -------------------------------------------------------------------------------- /6.常用组件.md: -------------------------------------------------------------------------------- 1 | # 6.常用组件 2 | 3 | 4 | 在我们常用的应用中,经常会有视图内容切换的场景,来展示更加丰富的内容。比如经典的首页和我的页面,点击底部的页签的选项,可以实现“首页”和“我的” 5 | 6 | Tabs组件仅可包含子组件TabContent,每一个页签对应一个内容视图即TabContent组件。下面的示例代码构建了一个简单的页签页面: 7 | 8 | ```TypeScript 9 | @Entry 10 | @Component 11 | struct TabsExample { 12 | private controller: TabsController = new TabsController() 13 | 14 | build() { 15 | Column() { 16 | Tabs({ barPosition: BarPosition.Start, controller: this.controller }) { 17 | TabContent() { 18 | Column().width('100%').height('100%').backgroundColor(Color.Green) 19 | } 20 | .tabBar('green') 21 | 22 | TabContent() { 23 | Column().width('100%').height('100%').backgroundColor(Color.Blue) 24 | } 25 | .tabBar('blue') 26 | 27 | TabContent() { 28 | Column().width('100%').height('100%').backgroundColor(Color.Yellow) 29 | } 30 | .tabBar('yellow') 31 | 32 | TabContent() { 33 | Column().width('100%').height('100%').backgroundColor(Color.Pink) 34 | } 35 | .tabBar('pink') 36 | } 37 | .barWidth('100%') // 设置TabBar宽度 38 | .barHeight(60) // 设置TabBar高度 39 | .width('100%') // 设置Tabs组件宽度 40 | .height('100%') // 设置Tabs组件高度 41 | .backgroundColor(0xF5F5F5) // 设置Tabs组件背景颜色 42 | } 43 | .width('100%') 44 | .height('100%') 45 | } 46 | } 47 | ``` 48 | 49 | - TabContent组件不支持设置通用宽度属性,其宽度默认撑满Tabs父组件。 50 | - TabContent组件不支持设置通用高度属性,其高度由Tabs父组件高度与TabBar组件高度决定。 51 | 52 | 53 | 54 | 因为Tabs的布局模式默认是Fixed的,所以Tabs的页签是不可滑动的。当页签比较多的时候,可能会导致页签显示不全,将布局模式设置为Scrollable的话,可以实现页签的滚动。 55 | 56 | Tabs的布局模式有Fixed(默认)和Scrollable两种: 57 | 58 | - BarMode.Fixed:所有TabBar平均分配barWidth宽度(纵向时平均分配barHeight高度),页签不可滚动。 59 | 60 | - BarMode.Scrollable:每一个TabBar均使用实际布局宽度,超过总长度(横向Tabs的barWidth,纵向Tabs的barHeight)后可滑动。 61 | 62 | Tabs组件页签默认显示在顶部,某些场景下您可能希望Tabs页签出现在底部或者侧边,您可以使用Tabs组件接口中的参数barPosition设置页签位置。此外页签显示位置还与vertical属性相关联,vertical属性用于设置页签的排列方向,当vertical的属性值为false(默认值)时页签横向排列,为true时页签纵向排列。 63 | 64 | barPosition的值可以设置为BarPosition.Start(默认值)和BarPosition.End 65 | 66 | 67 | ### 自定义TabBar 68 | ![image](https://github.com/CharonChui/Pictures/blob/master/arkts_custom_tabbar.png?raw=true) 69 | 70 | 上面的这种底部页签效果,需要用到自定义TabBar。 71 | 72 | 73 | TabContent的tabBar属性除了支持string类型,还支持使用@Builder装饰器修饰的函数。您可以使用@Builder装饰器,构造一个生成自定义TabBar样式的函数,实现上面的底部页签效果,示例代码如下: 74 | 75 | ```TypeScript 76 | @Entry 77 | @Component 78 | struct TabsExample { 79 | @State currentIndex: number = 0; 80 | private tabsController: TabsController = new TabsController(); 81 | 82 | @Builder TabBuilder(title: string, targetIndex: number, selectedImg: Resource, normalImg: Resource) { 83 | Column() { 84 | Image(this.currentIndex === targetIndex ? selectedImg : normalImg) 85 | .size({ width: 25, height: 25 }) 86 | Text(title) 87 | .fontColor(this.currentIndex === targetIndex ? '#1698CE' : '#6B6B6B') 88 | } 89 | .width('100%') 90 | .height(50) 91 | .justifyContent(FlexAlign.Center) 92 | .onClick(() => { 93 | this.currentIndex = targetIndex; 94 | this.tabsController.changeIndex(this.currentIndex); 95 | }) 96 | } 97 | 98 | build() { 99 | Tabs({ barPosition: BarPosition.End, controller: this.tabsController }) { 100 | TabContent() { 101 | Column().width('100%').height('100%').backgroundColor('#00CB87') 102 | } 103 | .tabBar(this.TabBuilder('首页', 0, $r('app.media.home_selected'), $r('app.media.home_normal'))) 104 | 105 | TabContent() { 106 | Column().width('100%').height('100%').backgroundColor('#007DFF') 107 | } 108 | .tabBar(this.TabBuilder('我的', 1, $r('app.media.mine_selected'), $r('app.media.mine_normal'))) 109 | } 110 | .barWidth('100%') 111 | .barHeight(50) 112 | .onChange((index: number) => { 113 | this.currentIndex = index; 114 | }) 115 | } 116 | } 117 | ``` 118 | 119 | 120 | 121 | 示例代码中将barPosition的值设置为BarPosition.End,使页签显示在底部。使用@Builder修饰TabBuilder函数,生成由Image和Text组成的页签。同时也给Tabs组件设置了TabsController控制器,当点击某个页签时,调用changeIndex方法进行页签内容切换。 122 | 123 | 最后还需要给Tabs添加onChange事件,Tab页签切换后触发该事件,这样当我们左右滑动内容视图的时候,页签样式也会跟着改变。 124 | 125 | 126 | #### 播放器 127 | 128 | ```TypeScript 129 | Video(value: {src?: string | Resource, currentProgressRate?: number | string |PlaybackSpeed, previewUri?: string |PixelMap | Resource, controller?: VideoController}) 130 | ``` 131 | 132 | - src表示视频播放源的路径,可以支持本地视频路径和网络路径。使用网络地址时,如https,需要注意的是需要在module.json5文件中申请网络权限。 133 | - currentProgressRate表示视频播放倍速,其参数类型为number,取值支持0.75,1.0,1.25,1.75,2.0,默认值为1.0倍速; 134 | - previewUri表示视频未播放时的预览图片路径; 135 | - controller表示视频控制器。 136 | 137 | 138 | **视频支持的规格是:mp4、mkv、webm、TS。** 139 | 140 | objectFit 中视频显示模式包括Contain、Cover、Auto、Fill、ScaleDown、None 6种模式,默认情况下使用ImageFit.Cover(保持宽高比进行缩小或者放大,使得图片两边都大于或等于显示边界),其他效果(如自适应显示、保持原有尺寸显示、不保持宽高比进行缩放等)可以根据具体使用场景/设备来进行选择。 141 | 142 | 143 | 144 | ### Video组件回调事件介绍 145 | 146 | Video组件能够支持常规的点击、触摸等通用事件,同时也支持onStart、onPause、onFinish、onError等事件,具体事件的功能描述见下表: 147 | 148 | 149 | - onStart(event:() => void) : 播放时触发该事件。 150 | 151 | - onPause(event:() => void) : 暂停时触发该事件。 152 | 153 | - onFinish(event:() => void) : 播放结束时触发该事件。 154 | 155 | - onError(event:() => void) : 播放失败时触发该事件。 156 | 157 | - onPrepared(callback:(event?: { duration: number }) => void) 158 | 视频准备完成时触发该事件,通过duration可以获取视频时长,单位为s。 159 | 160 | - onSeeking(callback:(event?: { time: number }) => void) 161 | 操作进度条过程时上报时间信息,单位为s。 162 | 163 | - onSeeked(callback:(event?: { time: number }) => void) 164 | 操作进度条完成后,上报播放时间信息,单位为s。 165 | 166 | - onUpdate(callback:(event?: { time: number }) => void) 167 | 播放进度变化时触发该事件,单位为s,更新时间间隔为250ms。 168 | 169 | - onFullscreenChange(callback:(event?: { fullscreen: boolean }) => void) 170 | 在全屏播放与非全屏播放状态之间切换时触发该事件 171 | 172 | 173 | ## Dialog 174 | 175 | 176 | 弹窗是一种模态窗口,通常用来展示用户当前需要的或用户必须关注的信息或操作。在弹出框消失之前,用户无法操作其他界面内容。ArkUI为我们提供了丰富的弹窗功能,弹窗按照功能可以分为以下两类: 177 | 178 | - 确认类:例如警告弹窗AlertDialog。 179 | - 选择类:包括文本选择弹窗TextPickerDialog 、日期滑动选择弹窗DatePickerDialog、时间滑动选择弹窗TimePickerDialog等。 180 | 181 | ```TypeScript 182 | Button('点击显示弹窗') 183 | .onClick(() => { 184 | AlertDialog.show( 185 | { 186 | title: '删除联系人', // 标题 187 | message: '是否需要删除所选联系人?', // 内容 188 | autoCancel: false, // 点击遮障层时,是否关闭弹窗。 189 | alignment: DialogAlignment.Bottom, // 弹窗在竖直方向的对齐方式 190 | offset: { dx: 0, dy: -20 }, // 弹窗相对alignment位置的偏移量 191 | primaryButton: { 192 | value: '取消', 193 | action: () => { 194 | console.info('Callback when the first button is clicked'); 195 | } 196 | }, 197 | secondaryButton: { 198 | value: '删除', 199 | fontColor: '#D94838', 200 | action: () => { 201 | console.info('Callback when the second button is clicked'); 202 | } 203 | }, 204 | cancel: () => { // 点击遮障层关闭dialog时的回调 205 | console.info('Closed callbacks'); 206 | } 207 | } 208 | ) 209 | }) 210 | ``` 211 | 此外,您还可以使用AlertDialog,构建只包含一个操作按钮的确认弹窗,使用confirm响应操作按钮回调。 212 | 213 | ```TypeScript 214 | AlertDialog.show( 215 | { 216 | title: '提示', 217 | message: '提示信息', 218 | autoCancel: true, 219 | alignment: DialogAlignment.Bottom, 220 | offset: { dx: 0, dy: -20 }, 221 | confirm: { 222 | value: '确认', 223 | action: () => { 224 | console.info('Callback when confirm button is clicked'); 225 | } 226 | }, 227 | cancel: () => { 228 | console.info('Closed callbacks') 229 | } 230 | } 231 | ) 232 | ``` 233 | 234 | 235 | 236 | ### 自定义弹窗 237 | 238 | 自定义弹窗的使用更加灵活,适用于更多的业务场景,在自定义弹窗中您可以自定义弹窗内容,构建更加丰富的弹窗界面。自定义弹窗的界面可以通过装饰器@CustomDialog定义的组件来实现,然后结合CustomDialogController来控制自定义弹窗的显示和隐藏。 239 | 240 | 241 | ## Web组件 242 | 243 | ArkUI为我们提供了Web组件来加载网页,借助它我们就相当于在自己的应用程序里嵌入一个浏览器,从而非常轻松地展示各种各样的网页。 244 | 245 | 246 | Web组件的使用非常简单,只需要在Page目录下的ArkTS文件中创建一个Web组件,传入两个参数就可以了。其中 247 | 248 | ```TypeScript 249 | @Entry 250 | @Component 251 | struct WebComponent { 252 | controller: WebController = new WebController(); 253 | build() { 254 | Column() { 255 | Web({ src: 'https://developer.harmonyos.com/', controller: this.controller }) 256 | } 257 | } 258 | } 259 | ``` 260 | 261 | 262 | 访问在线网页时您需要在module.json5文件中申明网络访问权限:ohos.permission.INTERNET。 263 | 264 | #### 加载本地网页 265 | 266 | 前面实现了Web组件加载在线网页,Web组件同样也可以加载本地网页。首先在main/resources/rawfile目录下创建一个HTML文件,然后通过$rawfile引用本地网页资源,示例代码如下: 267 | 268 | ```TypeScript 269 | @Entry 270 | @Component 271 | struct SecondPage { 272 | controller: WebController = new WebController(); 273 | 274 | build() { 275 | Column() { 276 | Web({ src: $rawfile('index.html'), controller: this.controller }) 277 | } 278 | } 279 | } 280 | ``` 281 | 282 | 283 | 有的网页可能不能很好适配手机屏幕,需要对其缩放才能有更好的效果,开发者可以根据需要给Web组件设置zoomAccess属性,zoomAccess用于设置是否支持手势进行缩放,默认允许执行缩放。Web组件默认支持手势进行缩放。 284 | 您还可以使用zoom(factor: number)方法用于设置网站的缩放比例。其中factor表示缩放倍数。 285 | 286 | 287 | #### 文本缩放 288 | 289 | 如果需要对文本进行缩放,可以使用textZoomAtio(textZoomAtio: number)方法。其中textZoomAtio用于设置页面的文本缩放百分比,默认值为100,表示100%,使用textZoomAtio,文本会放大,但是图片不会随着文本一起放大。 290 | 291 | #### Web组件事件 292 | 293 | Web组件还提供了处理Javascript的对话框、网页加载进度及各种通知与请求事件的方法。例如onProgressChange可以监听网页的加载进度,onPageEnd在网页加载完成时触发该回调,且只在主frame触发,onConfirm则在网页触发confirm告警弹窗时触发回调。下面以onConfirm事件为例讲解Web组件事件的使用,更多Web组件事件可以查看事件。 294 | 295 | 296 | 如果您希望响应Web组件中网页的警告弹窗事件,您可以在onAlert或者onConfirm的回调方法中处理这些事件。以confirm弹窗为例,在网页触发onConfirm()告警弹窗时,显示一个AlertDialog弹窗。 297 | 298 | ```TypeScript 299 | @Entry 300 | @Component 301 | struct WebComponent { 302 | controller:WebController = new WebController(); 303 | build() { 304 | Column() { 305 | Web({ src:$rawfile('index.html'), controller:this.controller }) 306 | .onConfirm((event) => { 307 | AlertDialog.show({ 308 | title: 'title', 309 | message: event.message, 310 | confirm: { 311 | value: 'onAlert', 312 | action: () => { 313 | event.result.handleConfirm(); 314 | } 315 | }, 316 | cancel: () => { 317 | event.result.handleCancel(); 318 | } 319 | }) 320 | return true; 321 | }) 322 | } 323 | } 324 | } 325 | ``` 326 | 327 | #### Web和JavaScript交互 328 | 329 | 在开发专为适配Web组件的网页时,您可以实现Web组件和JavaScript代码之间的交互。Web组件可以调用JavaScript方法,JavaScript也可以调用Web组件里面的方法。 330 | 331 | 如果您希望加载的网页在Web组件中运行JavaScript,则必须为您的Web组件启用JavaScript功能,默认情况下是允许JavaScript执行的。 332 | 333 | ```TypeScript 334 | Web({ src:'https://www.example.com', controller:this.controller }) 335 | .javaScriptAccess(true) 336 | ``` 337 | 338 | 339 | Web组件调用JS方法 340 | 您可以在Web组件onPageEnd事件中添加runJavaScript方法。事件是网页加载完成时的回调,runJavaScript方法可以执行HTML中的JavaScript脚本。 341 | 342 | ``` 343 | // xxx.ets 344 | @Entry 345 | @Component 346 | struct WebComponent { 347 | controller: WebController = new WebController(); 348 | @State webResult: string = '' 349 | build() { 350 | Column() { 351 | Text(this.webResult).fontSize(20) 352 | Web({ src: $rawfile('index.html'), controller: this.controller }) 353 | .javaScriptAccess(true) 354 | .onPageEnd(e => { 355 | this.controller.runJavaScript({ 356 | script: 'test()', 357 | callback: (result: string)=> { 358 | this.webResult = result; 359 | }}); 360 | }) 361 | } 362 | } 363 | } 364 | 365 | 366 | 367 | 368 | 369 | 370 | 375 | 376 | ``` 377 | 378 | 当页面加载完成时,触发onPageEnd事件,调用HTML文件中的test方法并将结果返回给Web组件。 379 | 380 | ##### JS调用Web组件方法 381 | 382 | 383 | 您可以使用registerJavaScriptProxy将Web组件中的JavaScript对象注入daowindow对象中,这样网页中的JS就可以直接调用该对象了。需要注意的是,要想registerJavaScriptProxy方法生效,须调用refresh方法。下面的示例将ets文件中的对象testObj注入到了window对象中。 384 | 385 | ``` 386 | // xxx.ets 387 | @Entry 388 | @Component 389 | struct WebComponent{ 390 | @State dataFromHtml: string = '' 391 | controller: WebController = new WebController() 392 | testObj = { 393 | test: (data) => { 394 | this.dataFromHtml = data; 395 | return 'ArkUI Web Component'; 396 | }, 397 | toString: () => { 398 | console.log('Web Component toString'); 399 | } 400 | } 401 | 402 | build() { 403 | Column() { 404 | Text(this.dataFromHtml).fontSize(20) 405 | Row() { 406 | Button('Register JavaScript To Window').onClick(() => { 407 | this.controller.registerJavaScriptProxy({ 408 | object: this.testObj, 409 | name: 'objName', 410 | methodList: ['test', 'toString'], 411 | }); 412 | this.controller.refresh(); 413 | }) 414 | } 415 | 416 | Web({ src: $rawfile('index.html'), controller: this.controller }) 417 | .javaScriptAccess(true) 418 | } 419 | } 420 | } 421 | ``` 422 | 423 | 其中object表示参与注册的对象,name表示注册对象的名称为objName,与window中调用的对象名一致;methodList表示参与注册的应用侧JavaScript对象的方法,包含test、toString两个方法。在HTML中使用的时候直接使用objName调用methodList里面对应的方法即可,示例如下: 424 | ``` 425 | // index.html 426 | 427 | 428 | 429 | 430 | 431 | 432 | 437 | 438 | ``` 439 | 440 | 您还可以使用deleteJavaScriptRegister删除通过registerJavaScriptProxy注册到window上的指定name的应用侧JavaScript对象。 441 | 442 | 443 | 444 | #### 处理页面导航 445 | 446 | 当我们在使用浏览器浏览网页时,可以执行返回、前进、刷新等操作,Web组件同样支持这些操作。您可以使用backward()返回到上一个页面,使用forward()前进一个页面,您也可以使用refresh()刷新页面,使用clearHistory()来清除历史记录。 447 | 448 | 449 | ---------- 450 | 451 | 452 | - [上一篇:5.ArkTS声明式UI入门](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/5.ArkTS%E5%A3%B0%E6%98%8E%E5%BC%8FUI%E5%85%A5%E9%97%A8.md) 453 | - [下一篇:7.应用组件UIAbility](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/7.%E5%BA%94%E7%94%A8%E7%BB%84%E4%BB%B6UIAbility.md) 454 | 455 | 456 | 457 | --- 458 | 459 | - 邮箱 :charon.chui@gmail.com 460 | - Good Luck! 461 | -------------------------------------------------------------------------------- /4.HarmonyOS应用开发初探.md: -------------------------------------------------------------------------------- 1 | # 4.HarmonyOS应用开发初探 2 | 3 | 因为OpenHarmony API 9开始新增的Stage模型,是**目前主推且会长期演进的模型**。 4 | 所以后面会全部使用Stage模型进行开发 5 | 6 | 7 | 8 | #### 开发IDE 9 | 10 | [DevEco Studio](https://developer.huawei.com/consumer/cn/deveco-studio/) 11 | 12 | DevEco是基于inteliJ开源项目进行开发的,所以和Android Studio基本一样。 13 | 开发工具整体来看和Android Studio基本类似。 14 | 15 | 直接下载安装即可。 整个使用和Android Studio基本一样。 16 | 17 | 接下来通过Create Project创建一个新项目。 18 | ![image](https://github.com/CharonChui/Pictures/blob/master/eco_dev_create_project.png?raw=true) 19 | 这里选Empty Ablity的就可以。 20 | 21 | Ability:应用的基本组成部分,是应用所具备能力的抽象。Ability是系统调度应用的最小单元,是能够完成一个独立功能的组件,一个应用可以包含一个或多个Ability。 在FA模型与Stage模型,分别定义了不同类型的Ability。 22 | 鸿蒙应用实际是通过Ability框架进行开发的,Ability在英文解释中是能力的意思。鸿蒙认为开发一个应用程序,其实是对设备能力的一种实现。 23 | Ability是应用所具备能力的抽象,也是应用程序的重要组成部分。一个应用可以具备多种能力(即可以包含多个Ability),HarmonyOS支持应用以Ability为单位进行部署。 24 | 25 | 26 | 27 | 接下来就是和Android Studio一样,填项目名、Bundle name就是包名,是应用程序的唯一标识,一般用公司的域名 com.huawei.xxx这样。 28 | Compatible SDK目前最新的就是API 9. 29 | 这里有个Enable Super Visual,这是一种低代码的开发模式,对于一些简单的app可以使用,一般不用使用。 30 | ![image](https://github.com/CharonChui/Pictures/blob/master/harmony_app_create_project_2.png?raw=true) 31 | 32 | 接着就能看到整个项目的目录: 33 | 34 | 35 | 36 | 37 | 从上往下看: 38 | 39 | - AppScode中存放应用全局所需要的资源文件: 40 | 41 | - AppScope下的app.json5 :应用的全局配置信息,存放应用公共的配置信息。例如: bundleName、versionName、icon、appName等。 42 | - resource -> base -> element: 存放公共的字符串、布局文件等资源。 43 | - resource -> base -> media: 全局公共的多媒体资源文件。 44 | 45 | ```json 46 | { 47 | "app": { 48 | "bundleName": "com.example.harmonyapp", 49 | "vendor": "example", 50 | "versionCode": 1000000, 51 | "versionName": "1.0.0", 52 | "icon": "$media:app_icon", 53 | "label": "$string:app_name" 54 | } 55 | } 56 | ``` 57 | - entry:应用的主模块,存放Harmony OS应用的代码、资源等。编译构建生成一个HAP包。 58 | 59 | - oh-package.json5: 工程级依赖配置文件,用于记录引入包的配置信息。 60 | - src > main > ets:用于存放ArkTS源码。下面又分为entryability:应用/服务的入口和pages:应用/服务包含的页面。 61 | - src > main > resources:用于存放应用/服务所用到的资源文件,如图形、多媒体、字符串、布局文件等。关于资源文件,详见资源文件的分类。 62 | - src > main > module.json5:模块配置文件。主要包含HAP包的配置信息、应用/服务在具体设备上的配置信息以及应用/服务的全局配置信息。 63 | 64 | ```json 65 | { 66 | "module": { 67 | "name": "entry", 68 | "type": "entry", 69 | "description": "$string:module_desc", 70 | "mainElement": "EntryAbility", 71 | "deviceTypes": [ 72 | "phone", 73 | "tablet" 74 | ], 75 | "deliveryWithInstall": true, 76 | "installationFree": false, 77 | "pages": "$profile:main_pages", 78 | "abilities": [ 79 | { 80 | "name": "EntryAbility", 81 | "srcEntry": "./ets/entryability/EntryAbility.ts", 82 | "description": "$string:EntryAbility_desc", 83 | "icon": "$media:icon", 84 | "label": "$string:EntryAbility_label", 85 | "startWindowIcon": "$media:icon", 86 | "startWindowBackground": "$color:start_window_background", 87 | "exported": true, 88 | "skills": [ 89 | { 90 | "entities": [ 91 | "entity.system.home" 92 | ], 93 | "actions": [ 94 | "action.system.home" 95 | ] 96 | } 97 | ] 98 | } 99 | ] 100 | } 101 | } 102 | ``` 103 | 104 | 105 | 每个应用项目的代码目录下必须包含应用配置文件,这些配置文件会向编译工具、操作系统和应用市场提供应用的基本信息。 106 | 107 | 在基于Stage模型开发的应用项目代码下,都存在一个app.json5配置文件、以及一个或多个module.json5配置文件。 108 | 109 | app.json5(应用的全局配置文件)配置文件主要包含以下内容: 110 | 111 | - 应用的全局配置信息,包含应用的Bundle名称、开发厂商、版本号等基本信息。 112 | 113 | - 特定设备类型的配置信息。 114 | 115 | module.json5(模块的配置文件)配置文件主要包含以下内容: 116 | 117 | - Module的基本配置信息,包含Module名称、类型、描述、支持的设备类型等基本信息。 118 | 119 | - 应用组件信息,包含UIAbility组件和ExtensionAbility组件的描述信息。 120 | 121 | - 应用运行过程中所需的权限信息。 122 | 123 | 124 | 125 | 126 | - build-profile.json5:当前的模块信息 、编译信息配置项,包括buildOption、targets配置等。 127 | 128 | ```json 129 | { 130 | "apiType": 'stageMode', 131 | "buildOption": { 132 | }, 133 | "targets": [ 134 | { 135 | "name": "default", 136 | "runtimeOS": "HarmonyOS" 137 | }, 138 | { 139 | "name": "ohosTest", 140 | } 141 | ] 142 | } 143 | ``` 144 | 145 | - hvigorfile.ts:工程级编译构建任务脚本,hvigor是基于任务管理机制实现的一款全新的自动化构建工具,主要提供任务注册编排,工程模型管理、配置管理等核心能力。 146 | - obfuscation-rules.txt:混淆规则文件。混淆开启后,在使用Release模式进行编译时,会对代码进行编译、混淆及压缩处理,保护代码资产。 147 | - build-profile.json5:工程级级配置信息,包括签名signingConfigs、产品配置products等。 148 | - oh_modules:工程的依赖包,存放工程依赖的源文件。 149 | 150 | 151 | - src/main/resources/base/profile/main_pages.json: 保存的是页面page的路径配置信息,所有需要进行路由跳转的page页面都要在这里进行配置。 152 | 153 | HarmonyOS的用户应用程序包以App Pack(Application Package)形式发布,它由一个或多个HAP(HarmonyOS Ability Package)以及描述每个HAP属性的pack.info组成。HAP是Ability的部署包,HarmonyOS应用代码围绕Ability组件展开。 154 | 155 | 很多应用由多个页面组成,比如用户可以从音乐列表页面单击歌曲,跳转到该歌曲的播放界面。开发者需要通过页面路由将这些页面串联起来,按需实现跳转。 156 | 157 | 158 | 装饰器:方舟开发框架定义了一些具有特殊含义的装饰器,用于装饰类、结构、方法和变量。例如,上例中的@Entry、@Component和@State都是装饰器。 159 | 160 | 161 | ```TypeScript 162 | import router from '@ohos.router' 163 | // 装饰的自定义组件用作页面的默认入口组件,加载页面时,将首先创建并呈现@Entry装饰的自定义组件 164 | @Entry 165 | // 装饰的struct表示该接口提具有组件化能力,能够成为一个独立的组件 166 | @Component 167 | // 自定义组件内容 168 | struct Index { 169 | // @State装饰的变量是组件内部的状态数据,当状态数据被修改时,会自动进行UI刷新 170 | @State message: string = 'Hello World' 171 | // 自定义组件必须定义build方法,在其中以声明式的方式进行UI描述 172 | build() { 173 | // ArkUI提供的容器行组件 174 | Row() { 175 | // ArkUI提供的容器列组件 176 | Column() { 177 | // ArkUI提供的容器基础组件,如文字、字体大小、字体加粗 178 | Text(this.message) 179 | // 设置组件UI样式的属性方法 180 | .fontSize(50) 181 | .fontWeight(FontWeight.Bold) 182 | 183 | // 添加按钮,以响应用户点击 184 | Button() { 185 | // 按钮文案、文字大小颜色、样式 186 | Text('戳我') 187 | .fontSize(40) 188 | .fontColor(Color.Red) 189 | // 比粗体更粗 190 | .fontWeight(FontWeight.Bolder) 191 | }.type(ButtonType.Capsule) // 按钮风格: 胶囊型按钮(圆角默认为高度的一半) 192 | .margin({ 193 | // 距离上一个的高度 194 | top: 20 195 | }) 196 | .onClick(() => { 197 | // 跳转到第二个页面 198 | // 页面路由router根据页面的uri找到目标页面,从而实现跳转。 199 | // 这个url路径就是main_pages.json中配置的路径 200 | router.pushUrl({url: 'pages/Second'}).then(() => { 201 | console.info('start second page') 202 | }) 203 | }) 204 | .backgroundColor('#0D9FFB') 205 | .width('40%') 206 | .height('5%') 207 | } 208 | // 列布局的宽为屏幕宽度 209 | .width('100%') 210 | } 211 | // 行布局的高度为屏幕高度 212 | .height('100%') 213 | } 214 | } 215 | ``` 216 | 217 | ArkTS通过装饰器@Component和@Entry装饰struct关键字声明的数据结构,构成一个自定义组件。自定义组件中提供了一个build函数,开发者需在该函数内以链式调用的方式进行基本的UI描述. 218 | 219 | ![image](https://github.com/CharonChui/Pictures/blob/master/arkts_demo_1.png?raw=true) 220 | 221 | 222 | - 装饰器: 用于装饰类、结构、方法以及变量,并赋予其特殊的含义。 223 | 如上述示例中@Entry、@Component和@State都是装饰器,@Component表示自定义组件,@Entry表示该自定义组件为入口组件,@State表示组件中的状态变量,状态变量变化会触发UI刷新。 224 | - UI描述:以声明式的方式来描述UI的结构,例如build()方法中的代码块。 225 | - 自定义组件:可复用的UI单元,可组合其他组件,如上述被@Component装饰的struct Hello。 226 | - 系统组件:ArkUI框架中默认内置的基础和容器组件,可直接被开发者调用,比如示例中的Column、Text、Divider、Button。 227 | - 属性方法:组件可以通过链式调用配置多项属性,如fontSize()、width()、height()、backgroundColor()等。 228 | - 事件方法:组件可以通过链式调用设置多个事件的响应逻辑,如跟随在Button后面的onClick()。 229 | - 系统组件、属性方法、事件方法具体使用可参考基于ArkTS的声明式开发范式。 230 | 231 | 232 | 233 | 下面新加一个页面Second.ets。 新加页面之后需要在resource-base-profile-main_pages.json文件中增加Second.ets页面的配置: 234 | 235 | ```JavaScript 236 | { 237 | "src": [ 238 | "pages/Index", 239 | "pages/Second" 240 | ] 241 | } 242 | ``` 243 | 244 | 下面写第二个页面的内容: 245 | ```JavaScript 246 | // router 需要导包,写router后会自动导入 247 | import router from '@ohos.router' 248 | @Entry 249 | @Component 250 | struct Second { 251 | // 注意string是小写,不是String 252 | @State message: string = "Hello HarmonyOS" 253 | 254 | build() { 255 | Row() { 256 | Column() { 257 | Text(this.message) 258 | .fontSize(50) 259 | .fontWeight(FontWeight.Bold) 260 | Button() { 261 | Text('Back') 262 | .fontSize(25) 263 | .fontWeight(FontWeight.Bold) 264 | } 265 | .type(ButtonType.Capsule) 266 | .margin({ 267 | top: 20 268 | }) 269 | .backgroundColor('#0D9FFB') 270 | .width('40%') 271 | .height('5%') 272 | .onClick(() => { 273 | // 点击按钮返回上一个页面 274 | router.back(); 275 | }) 276 | } 277 | .width('100%') 278 | } 279 | .height('100%') 280 | } 281 | } 282 | ``` 283 | 284 | 285 | 在开发态,一个应用包含一个或者多个Module,可以在DevEco Studio工程中创建一个或者多个Module。 286 | Module是应用/服务的基本功能单元,包含了源代码、资源文件、第三方库及应用/服务配置文件,每一个Module都可以独立进行编译和运行。 287 | Module分为“Ability”和“Library”两种类型: 288 | 289 | - “Ability”类型的Module对应于编译后的HAP(Harmony Ability Package); 290 | - “Library”类型的Module对应于HAR(Harmony Archive),或者HSP(Harmony Shared Package)。 291 | - 一个Module可以包含一个或多个UIAbility组件 292 | 293 | 294 | 295 | - 开发者通过DevEco Studio把应用程序编译为一个或者多个.hap后缀的文件,即HAP。HAP是应用安装的基本单位,包含了编译后的代码、资源、三方库及配置文件。HAP可分为Entry和Feature两种类型。 296 | 297 | - Entry类型的HAP:是应用的主模块,在module.json5配置文件中的type标签配置为“entry”类型。在同一个应用中,同一设备类型只支持一个Entry类型的HAP,通常用于实现应用的入口界面、入口图标、主特性功能等。 298 | - Feature类型的HAP:是应用的动态特性模块,在module.json5配置文件中的type标签配置为“feature”类型。一个应用程序包可以包含一个或多个Feature类型的HAP,也可以不包含;Feature类型的HAP通常用于实现应用的特性功能,可以配置成按需下载安装,也可以配置成随Entry类型的HAP一起下载安装(请参见module对象内部结构中的“deliveryWithInstall”)。 299 | 300 | - 每个应用可以包含多个.hap文件,一个应用中的.hap文件合在一起称为一个Bundle,而bundleName就是应用的唯一标识(请参见app.json5配置文件中的bundleName标签)。需要特别说明的是:在应用上架到应用市场时,需要把应用包含的所有.hap文件(即Bundle)打包为一个.app后缀的文件用于上架,这个.app文件称为App Pack(Application Package),其中同时包含了描述App Pack属性的pack.info文件;在云端分发和端侧安装时,都是以HAP为单位进行分发和安装的。 301 | 302 | 303 | 304 | ![image](https://github.com/CharonChui/Pictures/blob/master/app-pack-stage.png?raw=true) 305 | 306 | 307 | 308 | 多HAP使用规则: 309 | 310 | - App Pack包不能直接安装到设备上,只是上架应用市场的单元。 311 | 312 | - App Pack包打包时会对每个HAP在json文件中的配置进行校验,确保bundleName、versionCode等标签取值相同,详见App打包时的HAP合法性校验。 313 | 314 | - App Pack包中同一设备类型的所有HAP中必须有且只有一个Entry类型的HAP,Feature类型的HAP可以有一个或者多个,也可以没有。 315 | 316 | - App Pack包中的每个HAP必须配置moduleName标签,同一设备类型的所有HAP对应的moduleName标签必须唯一。 317 | 318 | - 同一应用的所有HAP签名证书要保持一致。上架应用市场是以App Pack的形式上架,并对其进行了签名。应用市场分发时会将所有HAP从App Pack中拆分出来,同时对其中的所有HAP进行重签名,这样保证了所有HAP签名证书的一致性。在调试阶段,开发者通过命令行或IDE将HAP安装到设备上时要保证所有HAP签名证书一致,否则会出现安装失败的问题。 319 | 320 | ![image](https://github.com/CharonChui/Pictures/blob/master/hap_app_harmony.png?raw=true) 321 | 322 | ##### 共享包概述 323 | 324 | 当前系统提供了两种共享包,HAR(Harmony Archive)静态共享包和HSP(Harmony Shared Package)动态共享包: 325 | 326 | - HAR(Harmony Archive)是静态共享包,可以包含代码、C++库、资源和配置文件。通过HAR可以实现多个模块或多个工程共享ArkUI组件、资源等相关代码。HAR不同于HAP,不能独立安装运行在设备上,只能作为应用模块的依赖项被引用。 327 | - HSP(Harmony Shared Package)是动态共享包,按照使用场景可以分为应用内HSP和应用间HSP。应用内HSP指的是专门为某一应用开发的HSP,只能被该应用内部其他HAP/HSP使用,用于应用内部代码、资源的共享。应用内HSP跟随其宿主应用的APP包一起发布,与宿主应用同进程,具有相同的包名和生命周期。(当前暂不支持应用间HSP) 328 | 329 | 330 | HAR与HSP都是为了实现代码和资源的共享,都可以包含代码、C++库、资源和配置文件,最大的不同之处在于:HAR中的代码和资源跟随使用方编译,如果有多个使用方,它们的编译产物中会存在多份相同拷贝;而HSP中的代码和资源可以独立编译,运行时在一个进程中代码也只会存在一份。 331 | 332 | 333 | ![image](https://github.com/CharonChui/Pictures/blob/master/in-app-hsp-har.png?raw=true) 334 | 335 | HSP旨在解决多个模块引用相同的HAR,导致APP包大小膨胀的问题。 336 | 337 | 使用HSP的约束条件: 338 | 339 | - HSP及其使用方都必须是Stage模型。 340 | - HSP及其使用方都必须使用esmodule编译模式。 341 | - HSP不支持在配置文件中声明abilities、extensionAbilities标签。 342 | 343 | 344 | #### 导出native方法 345 | 346 | 347 | 在HAR中也可以包含C++编写的so。对于so中的native方法,HAR通过以下方式导出,以导出libnative.so的加法接口add为例: 348 | 349 | ```JavaScript 350 | // library/src/main/ets/utils/nativeTest.ts 351 | import native from "libnative.so" 352 | 353 | export function nativeAdd(a: number, b: number) { 354 | let result: number = native.add(a, b); 355 | return result; 356 | } 357 | ``` 358 | 359 | 360 | 361 | 362 | ### 资源访问 363 | 364 | #### 应用资源 365 | 对于应用资源,在工程中,通过"$r('app.type.name')"形式引用。 366 | 367 | 其中,app为应用内resources目录中定义的资源; 368 | type为资源类型或资源的存放位置,取值包含“color”、“float”、“string”、“plural”、“media”; 369 | name为资源命名,由开发者定义资源时确定。 370 | 371 | 对于rawfile目录资源,通过"$rawfile('filename')"形式引用。其中,filename为rawfile目录下文件的相对路径,文件名需要包含后缀,路径开头不可以以"/"开头。 372 | 373 | 对于rawfile目录的descriptor,可通过资源管理的getRawFd接口引用,其返回值descriptor.fd为hap包的fd。此时,访问rawfile文件需要结合{fd, offset, length}一起使用。 374 | 375 | 说明: 376 | 377 | - 资源描述符不能拼接使用,仅支持普通字符串如'app.type.name'。 378 | 379 | - $r返回值为Resource对象,可通过getStringValue 方法获取对应的字符串。 380 | 381 | 资源组目录下的“资源文件示例”显示了.json文件内容,包含color.json文件、string.json文件和plural.json文件,访问应用资源时需先了解.json文件的使用规范。 382 | 资源的具体使用方法如下: 383 | 384 | ```JavaScript 385 | Text($r('app.string.string_hello')) 386 | .fontColor($r('app.color.color_hello')) 387 | .fontSize($r('app.float.font_hello')) 388 | 389 | Text($r('app.string.string_world')) 390 | .fontColor($r('app.color.color_world')) 391 | .fontSize($r('app.float.font_world')) 392 | 393 | // 引用string.json资源。Text中$r的第一个参数指定string资源,第二个参数用于替换string.json文件中的%s。 394 | //如下示例代码value为"We will arrive at five of the clock"。 395 | Text($r('app.string.message_arrive', "five of the clock")) 396 | .fontColor($r('app.color.color_hello')) 397 | .fontSize($r('app.float.font_hello')) 398 | 399 | // 引用plural$资源。Text中$r的第一个指定plural资源,第二个参数用于指定单复数(在中文,单复数均使用other。在英文,one:代表单数,取值为1;other:代表复数,取值为大于等于1的整数),第三个参数用于替换%d 400 | // 如下示例代码为复数,value为"5 apples"。 401 | Text($r('app.plural.eat_apple', 5, 5)) 402 | .fontColor($r('app.color.color_world')) 403 | .fontSize($r('app.float.font_world')) 404 | 405 | Image($r('app.media.my_background_image')) // media资源的$r引用 406 | 407 | Image($rawfile('test.png')) // rawfile$r引用rawfile目录下图片 408 | 409 | Image($rawfile('newDir/newTest.png')) // rawfile$r引用rawfile目录下图片 410 | ``` 411 | 412 | #### 系统资源 413 | 414 | 除了自定义资源,开发者也可以使用系统中预定义的资源,统一应用的视觉风格。可以查看应用UX设计中关于资源的介绍,获取支持的系统资源ID及其在不同配置下的取值。 415 | 416 | 在开发过程中,分层参数的用法与资源限定词基本一致。对于系统资源,可以通过“$r('sys.type.resource_id')”的形式引用。其中,sys为系统资源;type为资源类型,取值包括“color”、“float”、“string”、“media”;resource_id为资源id。 417 | 418 | 说明: 419 | 420 | - 仅声明式开发范式支持使用系统资源。 421 | 422 | - 对于系统预置应用,建议使用系统资源;对于三方应用,可以根据需要选择使用系统资源或自定义应用资源。 423 | 424 | 425 | ```JavaScript 426 | Text('Hello') 427 | .fontColor($r('sys.color.ohos_id_color_emphasize')) 428 | .fontSize($r('sys.float.ohos_id_text_size_headline1')) 429 | .fontFamily($r('sys.string.ohos_id_text_font_family_medium')) 430 | .backgroundColor($r('sys.color.ohos_id_color_palette_aux1')) 431 | 432 | Image($r('sys.media.ohos_app_icon')) 433 | .border({ 434 | color: $r('sys.color.ohos_id_color_palette_aux1'), 435 | radius: $r('sys.float.ohos_id_corner_radius_button'), width: 2 436 | }) 437 | .margin({ 438 | top: $r('sys.float.ohos_id_elements_margin_horizontal_m'), 439 | bottom: $r('sys.float.ohos_id_elements_margin_horizontal_l') 440 | }) 441 | .height(200) 442 | .width(300) 443 | ``` 444 | 445 | 446 | 447 | 448 | ------------- 449 | 450 | 451 | - [上一篇:3.HarmonyOS开发术语](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/3.HarmonyOS%E5%BC%80%E5%8F%91%E6%9C%AF%E8%AF%AD.md) 452 | - [下一篇:5.ArkTS声明式UI入门](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/5.ArkTS%E5%A3%B0%E6%98%8E%E5%BC%8FUI%E5%85%A5%E9%97%A8.md) 453 | 454 | 455 | 456 | 457 | -------------------------------------------------------------------------------- /2.ArkTS基础语法.md: -------------------------------------------------------------------------------- 1 | 2.ArkTS基础语法 2 | === 3 | 4 | 5 | 6 | ### 背景 7 | 8 | 传统的Web开发使用的语言: 9 | 10 | - HTML: 控制页面元素 11 | - CSS: 控制布局和样式 12 | - JavaScript: 控制页面逻辑和数据状态 13 | 14 | 15 | 需要三种语言,而且语法不一样。 16 | 17 | 但是对华为来说Java又用不了,Web开发又太复杂,所以有了ArkTS。 18 | 19 | 为了确保应用开发的最佳体验,ArkTS提供对方舟开发框架ArkUI的声明式语法和其他特性的支持。由于此部分特性不在既有TypeScript的范围内。 20 | 21 | 22 | 23 | 24 | 25 | 26 | 但是对于Android和iOS的开发者,都会想到,如果有前端开发这一套,效率能行吗? 27 | 28 | 别忘了,鸿蒙还提供了方舟编译器(ArkCompiler),ArkCompiler AOT优化 : 隐藏继承优化、循环高级优化、过程空间优化、性能提升20%。 29 | 随着移动设备在人们的日常生活中变得越来越普遍,许多编程语言在设计之初没有考虑到移动设备,导致应用的运行缓慢、低效、功耗大,针对移动环境的编程语言优化需求也越来越大。ArkTS是专为解决这些问题而设计的,聚焦于提高运行效率。 30 | ArkTS的一大特性是它专注于低运行时开销。ArkTS对TypeScript的动态类型特性施加了更严格的限制,以减少运行时开销,提高执行效率。通过取消动态类型特性,ArkTS代码能更有效地被运行前编译和优化,从而实现更快的应用启动和更低的功耗。 31 | 32 | ### ArkTS 33 | 34 | ArkTS是OpenHarmony优选的主力应用开发语言。 35 | ArkTS围绕应用开发在TypeScript(简称TS)生态基础上做了进一步扩展,保持了TS的基本风格,同时通过规范定义强化开发期静态检查和分析,提升程序执行稳定性和性能。 36 | 37 | ArkTS是一种为构建高性能应用而设计的编程语言。ArkTS在继承TypeScript语法的基础上进行了优化,以提供更高的性能和开发效率。 38 | 39 | 40 | 目前流行的编程语言TypeScript是在JavaScript基础上通过添加类型定义扩展而来的,而ArkTS则是TypeScript的进一步扩展。 TypeScript深受开发者的喜爱,因为它提供了一种更结构化的JavaScript编码方法。ArkTS旨在保持TypeScript的大部分语法,为现有的TypeScript开发者实现无缝过渡,让移动开发者快速上手ArkTS。 41 | 42 | 43 | 与JavaScript的互通性是ArkTS语言设计中的关键考虑因素。鉴于许多移动应用开发者希望重用其TypeScript和JavaScript代码和库,ArkTS提供了与JavaScript的无缝互通,使开发者可以很容易地将JavaScript代码集成到他们的应用中。这意味着开发者可以利用现有的代码和库进行ArkTS开发。 44 | 45 | 46 | 47 | 从API version 10开始,ArkTS进一步通过规范强化静态检查和分析,对比标准TS的差异可以参考从TypeScript到ArkTS的适配规则: 48 | 49 | - 强制使用静态类型:静态类型是ArkTS最重要的特性之一。如果使用静态类型,那么程序中变量的类型就是确定的。同时,由于所有类型在程序实际运行前都是已知的,编译器可以验证代码的正确性,从而减少运行时的类型检查,有助于性能提升。 50 | 51 | - 禁止在运行时改变对象布局:为实现最大性能,ArkTS要求在程序执行期间不能更改对象布局。 52 | 53 | - 限制运算符语义:为获得更好的性能并鼓励开发者编写更清晰的代码,ArkTS限制了一些运算符的语义。比如,一元加法运算符只能作用于数字,不能用于其他类型的变量。 54 | 55 | - 不支持Structural typing:对Structural typing的支持需要在语言、编译器和运行时进行大量的考虑和仔细的实现,当前ArkTS不支持该特性。根据实际场景的需求和反馈,我们后续会重新考虑。 56 | 57 | 当前,ArkTS在TypeScript的基础上,匹配ArkUI框架,并扩展了声明式UI、状态管理等能力,的主要扩展了如下能力: 58 | 59 | - 基本语法:ArkTS定义了声明式UI描述、自定义组件和动态扩展UI元素的能力,再配合ArkUI开发框架中的系统组件及其相关的事件方法、属性方法等共同构成了UI开发的主体。 60 | 61 | - 状态管理:ArkTS提供了多维度的状态管理机制。在UI开发框架中,与UI相关联的数据可以在组件内使用,也可以在不同组件层级间传递,比如父子组件之间、爷孙组件之间,还可以在应用全局范围内传递或跨设备传递。另外,从数据的传递形式来看,可分为只读的单向传递和可变更的双向传递。开发者可以灵活的利用这些能力来实现数据和UI的联动。 62 | 63 | - 渲染控制:ArkTS提供了渲染控制的能力。条件渲染可根据应用的不同状态,渲染对应状态下的UI内容。循环渲染可从数据源中迭代获取数据,并在每次迭代过程中创建相应的组件。数据懒加载从数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。 64 | 65 | 66 | 67 | 68 | ### 声明 69 | 70 | ArkTS通过声明引入变量、常量、函数和类型。 71 | 72 | 73 | 74 | #### 变量 75 | 76 | ```TypeScript 77 | let hi: string = 'hello' 78 | hi = 'hello, world' 79 | ``` 80 | 81 | 由于ArkTS是一种静态类型语言,所有数据的类型都必须在编译时确定。 82 | 83 | 但是,如果一个变量或常量的声明包含了初始值,那么开发者就不需要显式指定其类型。ArkTS规范中列举了所有允许自动推断类型的场景。 84 | ```TypeScript 85 | let hi1: string = 'hello' 86 | let hi2 = 'hello, world' 87 | ``` 88 | 89 | #### 常量 90 | 91 | ```TypeScript 92 | const hello: string = 'hello' 93 | ``` 94 | 95 | 96 | 97 | 98 | #### number类型 99 | 100 | ArkTS提供number和Number类型,任何整数和浮点数都可以被赋给此类型的变量。 101 | 102 | 数字字面量包括整数字面量和十进制浮点数字面量。 103 | 104 | 整数字面量包括以下类别: 105 | 106 | - 由数字序列组成的十进制整数。例如:0、117、-345 107 | - 以0x(或0X)开头的十六进制整数,可以包含数字(0-9)和字母a-f或A-F。例如:0x1123、0x00111、-0xF1A7 108 | - 以0o(或0O)开头的八进制整数,只能包含数字(0-7)。例如:0o777 109 | - 以0b(或0B)开头的二进制整数,只能包含数字0和1。例如:0b11、0b0011、-0b11 110 | 111 | 浮点字面量包括以下: 112 | 113 | - 十进制整数,可为有符号数(即,前缀为“+”或“-”); 114 | - 小数点(“.”) 115 | - 小数部分(由十进制数字字符串表示) 116 | - 以“e”或“E”开头的指数部分,后跟有符号(即,前缀为“+”或“-”)或无符号整数。 117 | 118 | 119 | ```TypeScript 120 | let n1 = 3.14 121 | let n2 = 3.141592 122 | let n3 = .5 123 | let n4 = 1e10 124 | 125 | function factorial(n: number): number { 126 | if (n <= 1) { 127 | return 1 128 | } 129 | return n * factorial(n - 1) 130 | } 131 | ``` 132 | 133 | 134 | #### boolean类型 135 | 136 | ```TypeScript 137 | let isDone: boolean = false 138 | ``` 139 | 140 | 141 | #### string 142 | 143 | 字符串字面量由单引号(')或双引号(")之间括起来的零个或多个字符组成。 144 | 145 | 字符串字面量还有一特殊形式,是用反向单引号(`)括起来的模板字面量。 146 | 147 | 148 | ```TypeScript 149 | let s1 = 'Hello, world!\n' 150 | let s2 = 'this is a string' 151 | let a = 'Success' 152 | let s3 = `The result is ${a}` 153 | ``` 154 | 155 | #### void 156 | 157 | void类型用于指定函数没有返回值。 此类型只有一个值,同样是void。由于void是引用类型,因此它可以用于泛型类型参数。 158 | 159 | 160 | #### object 161 | 162 | Object类型是所有引用类型的基类型。任何值,包括基本类型的值(它们会被自动装箱),都可以直接被赋给Object类型的变量。 163 | 164 | #### 数组 165 | 166 | ```TypeScript 167 | // 方式1 168 | let names: string[] = ['Alice', 'Bob', 'Carol'] 169 | let firstName = names[0] 170 | 171 | // 方式2 172 | let names: Array = ['Jack', 'Rose'] 173 | console.log(firstName) 174 | ``` 175 | 176 | 两种方式都可以 177 | 178 | 179 | ##### for循环 180 | 181 | 当一个对象实现了Symbol.iterator属性时,我们认为它是可迭代的。 182 | 一些内置的类型如Array、Map、Set、string、等都具有可迭代性。 183 | 184 | - fori 185 | - for in 186 | - for of 187 | 188 | ```TypeScript 189 | let someArray = [1, 'string', false] 190 | for (let item of someArray) { 191 | console.log(item) 192 | } 193 | 194 | for (let item in soneArray) { 195 | console.log(item) 196 | } 197 | ``` 198 | 199 | 200 | #### 元组 201 | 202 | 元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。 203 | 204 | ```TypeScript 205 | let x: [string, number] 206 | x = ['hello', 10] // 正确 207 | x = [10, 'hello'] // 错误 208 | ``` 209 | 210 | 211 | #### 枚举 212 | 213 | 214 | enum类型,又称枚举类型,是预先定义的一组命名值的值类型,其中命名值又称为枚举常量。 使用枚举常量时必须以枚举类型名称为前缀。 215 | 216 | ```TypeScript 217 | 218 | enum Msg { 219 | HI = 'hi', 220 | HELLO = 'hello' 221 | } 222 | 223 | // 如果不赋值,默认的值就是0、1、2累加 224 | enum Color { Red, Green, Blue } 225 | let c: Color = Color.Red 226 | ``` 227 | 228 | #### unknown 229 | 230 | 有时候,我们会想要为那些在编译阶段还不清楚类型的变量指定一个类型。那么可以用unknown类型来标记这些变量。 231 | 232 | ```TypeScript 233 | let notSure: unknown = 4 234 | notSure = 'hello' 235 | notSure = false 236 | ``` 237 | 238 | #### void 239 | 240 | 和Java一样,当一个函数没有返回值,通常会见到其返回类型是void。 241 | 242 | #### null和undefined 243 | 244 | TypeScript里,undefined和null两者各自有自己的类型,分别叫做undefined和null。 245 | 246 | ```TypeScript 247 | let u: undefined = undefined 248 | let n: null = null 249 | ``` 250 | 251 | #### 联合类型 252 | 253 | union类型,即联合类型,是由多个类型组合成的引用类型。联合类型包含了变量可能的所有类型。 254 | 255 | ```TypeScript 256 | class Cat { 257 | // ... 258 | } 259 | class Dog { 260 | // ... 261 | } 262 | class Frog { 263 | // ... 264 | } 265 | type Animal = Cat | Dog | Frog | number 266 | // Cat、Dog、Frog是一些类型(类或接口) 267 | 268 | let animal: Animal = new Cat() 269 | animal = new Frog() 270 | animal = 42 271 | // 可以将类型为联合类型的变量赋值为任何组成类型的有效值 272 | ``` 273 | 274 | #### 别名 275 | 276 | 为匿名类型(数组、函数、对象字面量或联合类型)提供名称,或为已有类型提供替代名称。 277 | 278 | ```TypeScript 279 | type Matrix = number[][] 280 | type Handler = (s: string, no: number) => string 281 | type Predicate = (x: T) => Boolean 282 | type NullableObject = Object | null 283 | ``` 284 | 285 | 286 | 其他运算符、表达式、三元运算符都和Java基本一样。 287 | 288 | ```TypeScript 289 | function process (a: number, b: number) { 290 | try { 291 | let res = divide(a, b) 292 | console.log(res) 293 | } catch (x) { 294 | console.log('some error') 295 | } finally { 296 | 297 | } 298 | } 299 | ``` 300 | 301 | 302 | ##### 函数 303 | 304 | 可选参数的格式可为name?: Type。 305 | 306 | ```TypeScript 307 | function hello(name?: string) { 308 | if (name == undefined) { 309 | console.log('Hello!') 310 | } else { 311 | console.log('Hello, ${name}!') 312 | } 313 | } 314 | 315 | 316 | 317 | ``` 318 | 319 | 可选参数的另一种形式为设置的参数默认值。如果在函数调用中这个参数被省略了,则会使用此参数的默认值作为实参。 320 | 321 | ```TypeScript 322 | function multiply(n: number, coeff: number = 2): number { 323 | return n * coeff 324 | } 325 | multiply(2) // 返回2*2 326 | multiply(2, 3) // 返回2*3 327 | 328 | // 匿名函数 329 | let sun = function(n: number): number { 330 | return x + 1; 331 | } 332 | ``` 333 | 支持函数重载,和Java一样。 334 | 335 | 336 | ##### 箭头函数或Lambda函数 337 | 函数可以定义为箭头函数,箭头函数是定义匿名函数的简写语法,类似lambda表达式,它省略的function关键字。 338 | 339 | ```TypeScript 340 | ([param1, param2...]) => { 341 | .... 342 | } 343 | ``` 344 | 345 | 例如: 346 | 347 | ```TypeScript 348 | 349 | let sum = (x: number, y: number): number => { 350 | return x + y 351 | } 352 | ``` 353 | 354 | 355 | 箭头函数的返回类型可以省略;省略时,返回类型通过函数体推断。 356 | 357 | 表达式可以指定为箭头函数,使表达更简短,因此以下两种表达方式是等价的: 358 | 359 | ```TypeScript 360 | let sum1 = (x: number, y: number) => { return x + y } 361 | let sum2 = (x: number, y: number) => x + y 362 | ``` 363 | 364 | 365 | ##### 函数的可选参数 366 | 367 | 可以在参数名旁使用`?`实现可选参数的功能。 例如下面的函数lastName是可选的。 368 | 369 | ```TypeScript 370 | function buildName(firstName: string, lastName?: string) { 371 | ..... 372 | } 373 | 374 | let result1 = buildName('bob') 375 | let result2 = buildName('bob', 'adams') 376 | ``` 377 | 378 | ##### 可变参数 379 | 380 | 可变参数(剩余参数会)被当做个数不限的可选参数。 可以一个都没有,同样也可以有任意个。 可以使用省略号( ...)进行定义: 381 | 382 | ```TypeScript 383 | function getEmployeeName(firstName: string, ...restOfName: string[]) { 384 | return firstName + ' ' + restOfName.join(' '); 385 | } 386 | 387 | let employeeName = getEmployeeName('Joseph', 'Samuel', 'Lucas', 'MacKinzie'); 388 | ``` 389 | 390 | 391 | ##### 闭包 392 | 393 | 箭头函数通常在另一个函数中定义。作为内部函数,它可以访问外部函数中定义的所有变量和函数。 394 | 395 | 为了捕获上下文,内部函数将其环境组合成闭包,以允许内部函数在自身环境之外的访问。 396 | 397 | ```TypeScript 398 | function f(): () => number { 399 | let count = 0 400 | return (): number => { count++; return count } 401 | } 402 | 403 | let z = f() 404 | console.log(z()) // 输出:1 405 | console.log(z()) // 输出:2 406 | ``` 407 | 408 | 在以上示例中,箭头函数闭包捕获count变量。 409 | 410 | 411 | 412 | ##### 类 413 | 414 | TypeScript具备面向对象编程的基本语法,例如interface、class、enum等。 415 | 也具备封装、继承、多态等面向对象基本特征。 416 | 417 | ```TypeScript 418 | class Person { 419 | name: string = '' 420 | surname: string = '' 421 | constructor (n: string, sn: string) { 422 | this.name = n 423 | this.surname = sn 424 | } 425 | fullName(): string { 426 | return this.name + ' ' + this.surname 427 | } 428 | } 429 | ``` 430 | 431 | 定义类后,可以使用关键字new创建实例: 432 | 433 | ```TypeScript 434 | let p = new Person('John', 'Smith') 435 | console.log(p.fullName()) 436 | ``` 437 | 438 | 或者,可以使用对象字面量创建实例,对象字面量是一个表达式,可用于创建类实例并提供一些初始值。 439 | 440 | 它在某些情况下更方便,可以用来代替new表达式。 441 | 442 | 443 | ```TypeScript 444 | class Point { 445 | x: number = 0 446 | y: number = 0 447 | } 448 | let p: Point = {x: 42, y: 42} 449 | ``` 450 | 451 | ArkTS是静态类型语言,如上述示例所示,对象字面量只能在可以推导出该字面量类型的上下文中使用。 452 | 453 | 其他正确的例子: 454 | 455 | ```ArkTS 456 | class C { 457 | n: number = 0 458 | s: string = '' 459 | } 460 | 461 | function foo(c: C) {} 462 | 463 | let c: C 464 | 465 | c = {n: 42, s: 'foo'} // 使用变量的类型 466 | foo({n: 42, s: 'foo'}) // 使用参数的类型 467 | 468 | function bar(): C { 469 | return {n: 42, s: 'foo'} // 使用返回类型 470 | } 471 | 472 | ``` 473 | 474 | 也可以在数组元素类型或类字段类型中使用: 475 | 476 | ```ArkTS 477 | class C { 478 | n: number = 0 479 | s: string = '' 480 | } 481 | 482 | let cc: C[] = [{n: 1, s: 'a'}, {n: 2, s: 'b'}] 483 | ``` 484 | 485 | 486 | 487 | ##### 静态字段 488 | 使用关键字static将字段声明为静态。静态字段属于类本身,类的所有实例共享一个静态字段。 489 | 490 | 491 | ##### 字段初始化 492 | 为了减少运行时的错误和获得更好的执行性能, ArkTS要求所有字段在声明时或者构造函数中显式初始化。 493 | 494 | 495 | 496 | 接下来的代码展示了如果name的值可以是undefined,那么应该如何写代码。 497 | 498 | ```TypeScript 499 | class Person { 500 | name ?: string // 可能为`undefined` 501 | 502 | setName(n:string): void { 503 | this.name = n 504 | } 505 | 506 | // 编译时错误:name可以是"undefined",所以将这个API的返回值类型标记为string 507 | getNameWrong(): string { 508 | return this.name 509 | } 510 | 511 | getName(): string | undefined { // 返回类型匹配name的类型 512 | return this.name 513 | } 514 | } 515 | 516 | let jack = new Person() 517 | // 假设代码中没有对name赋值,例如调用"jack.setName('Jack')" 518 | 519 | // 编译时错误:编译器认为下一行代码有可能会访问undefined的属性,报错 520 | console.log(`${jack.getName().length}`); // 编译失败 521 | 522 | console.log(`${jack.getName()?.length}`); // 编译成功,没有运行时错误 523 | ``` 524 | 525 | 526 | #### 继承和实现 527 | 528 | ```TypeScript 529 | class Person { 530 | name: string = '' 531 | private _age = 0 532 | get age(): number { 533 | return this._age 534 | } 535 | } 536 | class Employee extends Person { 537 | salary: number = 0 538 | calculateTaxes(): number { 539 | return this.salary * 0.42 540 | } 541 | } 542 | 543 | 544 | interface DateInterface { 545 | now(): string; 546 | } 547 | class MyDate implements DateInterface { 548 | now(): string { 549 | // 在此实现 550 | return 'now is now' 551 | } 552 | } 553 | ``` 554 | 555 | ##### 可见性修饰符 556 | 类的方法和属性都可以使用可见性修饰符。 557 | 558 | 可见性修饰符包括:private、protected和public。默认可见性为public。 559 | 560 | 561 | 562 | ##### 泛型类型和函数 563 | 泛型类型和函数允许创建的代码在各种类型上运行,而不仅支持单一类型。 564 | 565 | 类和接口可以定义为泛型,将参数添加到类型定义中,如以下示例中的类型参数Element: 566 | 567 | ```TypeScript 568 | class Stack { 569 | public pop(): Element { 570 | // ... 571 | } 572 | public push(e: Element):void { 573 | // ... 574 | } 575 | } 576 | ``` 577 | 要使用类型Stack,必须为每个类型参数指定类型实参: 578 | 579 | ```TypeScript 580 | let s = new Stack 581 | s.push('hello') 582 | ``` 583 | 584 | 编译器在使用泛型类型和函数时会确保类型安全。参见以下示例: 585 | 586 | ```TypeScript 587 | let s = new Stack 588 | s.push(55) // 将会产生编译时错误 589 | ``` 590 | 591 | ##### 泛型约束 592 | 泛型类型的类型参数可以绑定。例如,HashMap容器中的Key类型参数必须具有哈希方法,即它应该是可哈希的。 593 | 594 | ```TypeScript 595 | interface Hashable { 596 | hash(): number 597 | } 598 | class HasMap { 599 | public set(k: Key, v: Value) { 600 | let h = k.hash() 601 | // ...其他代码... 602 | } 603 | } 604 | ``` 605 | 606 | 在上面的例子中,Key类型扩展了Hashable,Hashable接口的所有方法都可以为key调用。 607 | 608 | #### 泛型函数 609 | 使用泛型函数可编写更通用的代码。比如返回数组最后一个元素的函数: 610 | 611 | ```TypeScript 612 | function last(x: number[]): number { 613 | return x[x.length - 1] 614 | } 615 | console.log(last([1, 2, 3])) // 输出:3 616 | ``` 617 | 618 | 如果需要为任何数组定义相同的函数,使用类型参数将该函数定义为泛型: 619 | 620 | ```TypeScript 621 | function last(x: T[]): T { 622 | return x[x.length - 1] 623 | } 624 | ``` 625 | 现在,该函数可以与任何数组一起使用。 626 | 627 | 在函数调用中,类型实参可以显式或隐式设置: 628 | 629 | ```TypeScript 630 | // 显式设置的类型实参 631 | console.log(last(['aa', 'bb'])) 632 | console.log(last([1, 2, 3])) 633 | 634 | // 隐式设置的类型实参 635 | // 编译器根据调用参数的类型来确定类型实参 636 | console.log(last([1, 2, 3])) 637 | ``` 638 | 639 | 640 | 641 | 642 | #### 空安全 643 | 默认情况下,ArkTS中的所有类型都是不可为空的,因此类型的值不能为空。这类似于TypeScript的严格空值检查模式(strictNullChecks),但规则更严格。 644 | 645 | 在下面的示例中,所有行都会导致编译时错误: 646 | 647 | ```TypeScript 648 | let x: number = null // 编译时错误 649 | let y: string = null // 编译时错误 650 | let z: number[] = null // 编译时错误 651 | ``` 652 | 可以为空值的变量定义为联合类型T | null。 653 | 654 | ```TypeScript 655 | let x: number | null = null 656 | x = 1 // ok 657 | x = null // ok 658 | if (x != null) { /* do something */ } 659 | ``` 660 | 661 | 662 | 663 | #### 空值合并运算符 664 | 空值合并二元运算符?? 用于检查左侧表达式的求值是否等于null。如果是,则表达式的结果为右侧表达式;否则,结果为左侧表达式。 665 | 666 | 换句话说,a ?? b等价于三元运算符(a != null && a != undefined) ? a : b。 667 | 668 | 在以下示例中,getNick方法如果设置了昵称,则返回昵称;否则,返回空字符串: 669 | 670 | ```TypeScript 671 | class Person { 672 | // ... 673 | nick: string | null = null 674 | getNick(): string { 675 | return this.nick ?? '' 676 | } 677 | } 678 | ``` 679 | 680 | 681 | 682 | 683 | ### 模块 684 | 685 | 程序可划分为多组编译单元或模块。 686 | 687 | 当应用复杂时,我们可以把通用功能都抽取到单独的ts文件中,每个文件都是一个模块(module),模块可以相互加载,提高代码复用性。 688 | 689 | 模块可以相互加载,并可以使用特殊的指令export和import来交换功能,从另一个模块调用一个模块的函数。 690 | 每个模块都有其自己的作用域,即在模块中创建的任何声明(变量、函数、类等)在该模块之外都不可见,除非它们被显式导出。 691 | 692 | 与此相对,从另一个模块导出的变量、函数、类、接口等必须首先导入到模块中。 693 | 694 | 695 | 696 | 697 | ##### 导出 698 | 可以使用关键字export导出顶层的声明。 699 | 700 | 未导出的声明名称被视为私有名称,只能在声明该名称的模块中使用。 701 | 702 | 注意:通过export方式导出,在导入时要加{}。 703 | 704 | ```TypeScript 705 | export class Point { 706 | x: number = 0 707 | y: number = 0 708 | constructor(x: number, y: number) { 709 | this.x = x 710 | this.y = y 711 | } 712 | } 713 | export let Origin = new Point(0, 0) 714 | export function Distance(p1: Point, p2: Point): number { 715 | return Math.sqrt((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y)) 716 | } 717 | ``` 718 | 719 | 720 | ##### 导入 721 | 导入声明用于导入从其他模块导出的实体,并在当前模块中提供其绑定。导入声明由两部分组成: 722 | 723 | - 导入路径,用于指定导入的模块 724 | - 导入绑定,用于定义导入的模块中的可用实体集和使用形式(限定或不限定使用) 725 | 726 | 导入绑定可以有几种形式: 727 | 728 | - 假设模块具有路径“./utils”和导出实体“X”和“Y”。 729 | 730 | 导入绑定* as A表示绑定名称“A”,通过A.name可访问从导入路径指定的模块导出的所有实体: 731 | 732 | ```TypeScript 733 | import * as Utils from './utils' 734 | Utils.X // 表示来自Utils的X 735 | Utils.Y // 表示来自Utils的Y 736 | ``` 737 | 738 | - 导入绑定{ ident1, ..., identN }表示将导出的实体与指定名称绑定,该名称可以用作简单名称: 739 | 740 | ```TypeScript 741 | import { X, Y } from './utils' 742 | X // 表示来自utils的X 743 | Y // 表示来自utils的Y 744 | ``` 745 | 746 | - 如果标识符列表定义了ident as alias,则实体ident将绑定在名称alias下: 747 | 748 | ```TypeScript 749 | import { X as Z, Y } from './utils' 750 | Z // 表示来自Utils的X 751 | Y // 表示来自Utils的Y 752 | X // 编译时错误:'X'不可见 753 | ``` 754 | 755 | 756 | ### 语句 757 | 758 | 和Java一样。 759 | 760 | ```TypeScript 761 | if ... else if ... else 762 | switch ... case ... default ... 763 | ``` 764 | 765 | ### 字段初始化 766 | 767 | 为了减少运行时的错误和获得更好的执行性能, 768 | 769 | ArkTS要求所有字段在声明时或者构造函数中显式初始化。这和标准TS中的strictPropertyInitialization模式一样。 770 | 771 | ### getter和setter 772 | 773 | setter和getter可用于提供对对象属性的受控访问。 774 | 775 | ```TypeScript 776 | class Person { 777 | name: string = '' 778 | private _age: number = 0 779 | get age(): number { return this._age } 780 | set age(x: number) { 781 | if (x < 0) { 782 | throw Error('Invalid age argument') 783 | } 784 | this._age = x 785 | } 786 | } 787 | 788 | let p = new Person() 789 | console.log (p.age) // 将打印输出0 790 | p.age = -42 // 设置无效age值会抛出错误 791 | ``` 792 | 793 | 794 | ### 静态方法 795 | 796 | 使用关键字static将方法声明为静态。静态方法属于类本身,只能访问静态字段。 797 | 798 | 静态方法定义了类作为一个整体的公共行为。 799 | 800 | 所有实例都可以访问静态方法。 801 | 802 | 803 | 804 | ### 空安全 805 | 默认情况下,ArkTS中的所有类型都是不可为空的,因此类型的值不能为空。这类似于TypeScript的严格空值检查模式(strictNullChecks),但规则更严格。 806 | 807 | 在下面的示例中,所有行都会导致编译时错误: 808 | ```TypeScript 809 | let x: number = null // 编译时错误 810 | let y: string = null // 编译时错误 811 | let z: number[] = null // 编译时错误 812 | ``` 813 | 可以为空值的变量定义为联合类型T | null。 814 | 815 | ```TypeScript 816 | let x: number | null = null 817 | x = 1 // ok 818 | x = null // ok 819 | if (x != null) { /* do something */ } 820 | ``` 821 | 822 | #### 非空断言运算符 823 | 824 | 后缀运算符! 可用于断言其操作数为非空。 825 | 826 | 应用于空值时,运算符将抛出错误。否则,值的类型将从T | null更改为T: 827 | ```TypeScript 828 | let x: number | null = 1 829 | let y: number 830 | y = x + 1 // 编译时错误:无法对可空值作加法 831 | y = x! + 1 // ok 832 | ``` 833 | 834 | 835 | 836 | 837 | ------------- 838 | 839 | - [上一篇:1.HarmonyOS简介](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/1.HarmonyOS%E7%AE%80%E4%BB%8B.md) 840 | - [下一篇:3.HarmonyOS开发术语](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/3.HarmonyOS%E5%BC%80%E5%8F%91%E6%9C%AF%E8%AF%AD.md) 841 | 842 | 843 | --- 844 | 845 | - 邮箱 :charon.chui@gmail.com 846 | - Good Luck! 847 | -------------------------------------------------------------------------------- /5.ArkTS声明式UI入门.md: -------------------------------------------------------------------------------- 1 | # 5.ArkTS声明式UI入门 2 | 3 | 4 | ### 方舟开发框架(ArkUI)概述 5 | 6 | 方舟开发框架(简称ArkUI)为OpenHarmony应用的UI开发提供了完整的基础设施,包括简洁的UI语法、丰富的UI功能(组件、布局、动画以及交互事件),以及实时界面预览工具等,可以支持开发者进行可视化界面开发。 7 | 8 | 基本概念 9 | - UI: 即用户界面。开发者可以将应用的用户界面设计为多个功能页面,每个页面进行单独的文件管理,并通过页面路由API完成页面间的调度管理如跳转、回退等操作,以实现应用内的功能解耦。 10 | 11 | - 组件: 页面搭建与显示的最小单位,如列表、网格、按钮、单选框、进度条、文本等。开发者通过多种组件的组合,构建出满足自身应用诉求的完整界面。 12 | 13 | 14 | 15 | ### 声明式UI描述 16 | ArkTS以声明方式组合和扩展组件来描述应用程序的UI,同时还提供了基本的属性、事件和子组件配置方法,帮助开发者实现应用交互逻辑。 17 | 18 | #### 创建组件 19 | 根据组件构造方法的不同,创建组件包含有参数和无参数两种方式。 20 | 21 | 说明: 22 | 23 | 创建组件时不需要new运算符。 24 | 25 | ##### 无参数 26 | 如果组件的接口定义没有包含必选构造参数,则组件后面的“()”不需要配置任何内容。例如,Divider组件不包含构造参数: 27 | ```TypeScript 28 | Column() { 29 | Text('item 1') 30 | Divider() 31 | Text('item 2') 32 | } 33 | ``` 34 | 35 | 36 | ##### 有参数 37 | 如果组件的接口定义包含构造参数,则在组件后面的“()”配置相应参数。 38 | 39 | 40 | 41 | ## 组件分类 42 | 43 | 组件根据功能可以分为以下五大类: 44 | 45 | - 基础组件 46 | - 容器组件 47 | - 媒体组件 48 | - 绘制组件 49 | - 画布组件 50 | 51 | 52 | ### 基础组件 53 | 54 | - Text 55 | - Button 56 | - Image 57 | - TextInput 58 | 59 | ### 容器组件 60 | 61 | - Column 62 | - Row 63 | - Stack 64 | - List 65 | 66 | ### 图片显示 67 | 68 | Image组件用来渲染展示图片,支持gif、svg。 69 | 70 | 71 | `Image(src: string|PixelMap|Resource` 72 | 73 | - string格式,通常用来加载网络图片,需要申请网络访问权限 74 | ```TypeScript 75 | Image('https://xxx.png') 76 | ``` 77 | 权限的配置为app-entry-src-main目录中的module.json5文件: 78 | 79 | ```json 80 | "requestPermissions": [ 81 | { 82 | "name" : "ohos.permission.INTERNET", 83 | } 84 | ] 85 | ``` 86 | 87 | - PixelMap格式,可以加载像素图,常用在图片编辑中 88 | ```TypeScript 89 | Image(pixelMapObject) 90 | ``` 91 | - Resource格式,加载本地图片 92 | ```TypeScript 93 | // 加载app项目resource目录下media文件夹中的图片 94 | Image($r('app.media.xxx')) 95 | // 加载resource目录下rawfile总的图片,注意要带后缀 96 | Image($rawfile('xxx.png')) 97 | ``` 98 | 99 | 100 | #### 缩放类型 101 | 102 | 为了使图片在页面中有更好的显示效果,有时候需要对图片进行缩放处理。您可以使用objectFit属性设置图片的缩放类型,objectFit的参数类型为ImageFit。 103 | 104 | ![image](https://github.com/CharonChui/Pictures/blob/master/arkts_image_type.png?raw=true) 105 | 106 | ImageFit包含以下几种类型: 107 | 108 | - Contain:保持宽高比进行缩小或者放大,使得图片完全显示在显示边界内。 109 | - Cover(默认值):保持宽高比进行缩小或者放大,使得图片两边都大于或等于显示边界。 110 | - Auto:自适应显示。 111 | - Fill:不保持宽高比进行放大缩小,使得图片充满显示边界。 112 | - ScaleDown:保持宽高比显示,图片缩小或者保持不变。 113 | - None:保持原有尺寸显示。 114 | 115 | #### 网络图片 116 | 117 | 加载网络图片时,默认网络超时是5分钟,建议使用alt配置加载时的占位图。如果需要更灵活的网络配置,可以使用SDK中提供的HTTP工具包发送网络请求,接着将返回的数据解码为Image组件中的PixelMap 118 | 119 | 120 | ```TypeScript 121 | // @ts-nocheck 122 | import http from '@ohos.net.http' 123 | import ResponseCode from '@ohos.net.http' 124 | import image from '@ohos.multimedia.image' 125 | 126 | 127 | @Entry 128 | @Component 129 | struct Index { 130 | 131 | // 先创建一个PixelMap状态变量用于接收网络图片 132 | @State image: PixelMap = undefined 133 | 134 | build() { 135 | Column({space: 10}) { 136 | Button("获取网络图片") 137 | .onClick(() => { 138 | this.httpRequest() 139 | }) 140 | Image(this.image).height(100).width(100) 141 | } 142 | .width('100%') 143 | .height('100%') 144 | .padding(10) 145 | } 146 | 147 | // 网络图片请求方法 148 | private httpRequest() { 149 | let httpRequest = http.createHttp() 150 | 151 | httpRequest.request( 152 | "https://www.example.com/xxx.png", // 请填写一个具体的网络图片地址 153 | (error, data) => { 154 | if(error) { 155 | console.log("error code: " + error.code + ", msg: " + error.message) 156 | } else { 157 | let code = data.responseCode 158 | if(ResponseCode.ResponseCode.OK == code) { 159 | let imageSource = image.createImageSource(data.result) 160 | let options = {alphaType: 0, // 透明度 161 | editable: false, // 是否可编辑 162 | pixelFormat: 3, // 像素格式 163 | scaleMode: 1, // 缩略值 164 | size: {height: 100, width: 100}} // 创建图片大小 165 | imageSource.createPixelMap(options).then((pixelMap) => { 166 | this.image = pixelMap 167 | }) 168 | } else { 169 | console.log("response code: " + code) 170 | } 171 | } 172 | } 173 | ) 174 | } 175 | } 176 | ``` 177 | 178 | ### 文本显示 179 | 180 | Text组件用于在界面上展示一段文本信息,可以包含子组件Span。 181 | 182 | ```TypeScript 183 | // 使用字符 184 | Text('xxx') 185 | 186 | // 使用string.json文件中的配置 187 | Text($r('app.string.xxx')) 188 | ``` 189 | 190 | 当文本内容较多超出了Text组件范围的时候,您可以使用textOverflow设置文本截取方式,需配合maxLines使用,单独设置不生效,maxLines用于设置文本显示最大行数。下面的示例代码将textOverflow设置为Ellipsis ,它将显示不下的文本用 “...” 表示: 191 | 192 | ```TypeScript 193 | Text('This is the text content of Text Component This is the text content of Text Component') 194 | .fontSize(16) 195 | .maxLines(1) 196 | .textOverflow({overflow:TextOverflow.Ellipsis}) 197 | .backgroundColor(0xE6F2FD) 198 | ``` 199 | 200 | 201 | 202 | 203 | ### 文本输入框 204 | 205 | ```TypeScript 206 | TextInput(placeHolder: '占位字符', text: '当前内容') 207 | .type(InputType.Password) 208 | .onChange((value: string) => { 209 | 210 | }) 211 | ``` 212 | 213 | 设置光标位置 214 | 可以使用TextInputController动态设置光位置,下面的示例代码使用TextInputController的caretPosition方法,将光标移动到了第二个字符后。 215 | 216 | ```TypeScript 217 | @Entry 218 | @Component 219 | struct TextInputDemo { 220 | controller: TextInputController = new TextInputController() 221 | 222 | build() { 223 | Column() { 224 | TextInput({ controller: this.controller }) 225 | Button('设置光标位置') 226 | .onClick(() => { 227 | this.controller.caretPosition(2) 228 | }) 229 | } 230 | .height('100%') 231 | .backgroundColor(0xE6F2FD) 232 | } 233 | } 234 | ``` 235 | 236 | ### 占位组件 237 | 238 | Blank() 可以将布局内剩余的空间给占满 239 | 240 | ### 按钮组件 241 | ```TypeScript 242 | // 添加按钮,以响应用户点击 243 | Button('戳我') 244 | .type(ButtonType.Capsule) // 按钮风格: 胶囊型按钮(圆角默认为高度的一半) 245 | .margin({ 246 | // 距离上一个的高度 247 | top: 20 248 | }) 249 | .onClick(() => { 250 | router.pushUrl({ url: 'pages/Second' }).then(() => { 251 | console.info('start second page') 252 | }) 253 | }) 254 | .backgroundColor('#0D9FFB') 255 | .width('40%') 256 | .height('5%') 257 | ``` 258 | 259 | Button组件可以包含子组件,让您可以开发出更丰富多样的Button,下面的示例代码中Button组件包含了一个Image组件: 260 | 261 | ```TypeScript 262 | Button({ type: ButtonType.Circle, stateEffect: true }) { 263 | Image($r('app.media.icon_delete')) 264 | .width(30) 265 | .height(30) 266 | } 267 | .width(55) 268 | .height(55) 269 | .backgroundColor(0x317aff) 270 | ``` 271 | 272 | ### 滑动条属性 273 | 274 | ```TypeScript 275 | Slider({ 276 | min: 0, 277 | max: 100, 278 | value: 30, 279 | step: 10, 280 | style: SliderStyle.OutSet, // 滑块在进度条内还是上 281 | direction: Axis.Horizontal, 282 | reverse: false 283 | }).showTips(true) // 滑动时气泡显示进度 284 | .showSteps(true) // 显示步长 285 | .blockColor('#f00') 286 | .trackThickness(8) // 滑动条的粗细 287 | .width('100%') 288 | .onChange((value: number, mode: SliderChangeMode) => { 289 | 290 | }) 291 | ``` 292 | 293 | ### LoadingProgress 294 | 295 | LoadingProgress组件用于显示加载进展,比如应用的登录界面,当我们点击登录的时候,显示的“正在登录”的进度条状态。LoadingProgress的使用非常简单,只需要设置颜色和宽高就可以了。 296 | 297 | ```TypeScript 298 | LoadingProgress() 299 | .color(Color.Blue) 300 | .height(60) 301 | .width(60) 302 | ``` 303 | 304 | ### 日期选择组件 305 | 306 | ```TypeScript 307 | DatePicker(options?: {start?: Date, end?: Date, selected?: Date}) 308 | 309 | onChange(callback: (value: DatePickerResult) => void) 310 | ``` 311 | 312 | ### 线性布局(Row/Column) 313 | 314 | 线性布局(LinearLayout)是开发中最常用的布局,通过线性容器Row和Column构建。 315 | 线性布局是其他布局的基础,其子元素在线性方向上(水平方向和垂直方向)依次排列。 316 | 线性布局的排列方向由所选容器组件决定,Column容器内子元素按照垂直方向排列,Row容器内子元素按照水平方向排列。 317 | 318 | 319 | 在布局容器内,可以通过space属性设置排列方向上子元素的间距,使各子元素在排列方向上有等间距效果。 320 | 就是Android的LinearLayout 321 | 322 | ```TypeScript 323 | Column({ space: 20 }) { 324 | Text('space: 20').fontSize(15).fontColor(Color.Gray).width('90%') 325 | Row().width('90%').height(50).backgroundColor(0xF5DEB3) 326 | Row().width('90%').height(50).backgroundColor(0xD2B48C) 327 | Row().width('90%').height(50).backgroundColor(0xF5DEB3) 328 | }.width('100%') 329 | 330 | ``` 331 | 332 | ![image](https://github.com/CharonChui/Pictures/blob/master/colum_row_1.png?raw=true) 333 | 334 | - 主轴: 当前空间排列方向的轴 335 | - 交叉轴: 与主轴垂直的轴 336 | 337 | 对齐方式: 338 | - justifyContent: 设置子元素在主轴方向的对齐方式, 参数是FlexAlign的枚举 339 | - alignItems: 设置子元素在交叉轴方向的对齐方式, Row容器使用VerticalAlign枚举,Column容器使用HorizontalAlign枚举 340 | 341 | #### justifyContent 342 | 343 | 在布局容器内,可以通过justifyContent属性设置子元素在容器主轴上的排列方式。可以从主轴起始位置开始排布,也可以从主轴结束位置开始排布,或者均匀分割主轴的空间。 344 | 345 | ![image](https://github.com/CharonChui/Pictures/blob/master/column_justcontent_1.png?raw=true) 346 | 347 | 348 | Row和Column的方向不同,方式都是一样的 349 | 350 | #### alignItems 351 | 352 | ![image](https://github.com/CharonChui/Pictures/blob/master/alignitems_1.png?raw=true) 353 | 354 | ### 间距 355 | 356 | 和Android的基本一样。 357 | 358 | - 外边距: margin 359 | - 内边距: padding 360 | 361 | 362 | 363 | 364 | ### 层叠布局(Stack) 365 | 366 | 层叠布局(StackLayout)用于在屏幕上预留一块区域来显示组件中的元素,提供元素可以重叠的布局。 367 | 368 | 369 | 层叠布局通过Stack容器组件实现位置的固定定位与层叠,容器中的子元素依次入栈,后一个子元素覆盖前一个子元素,子元素可以叠加,也可以设置位置。 370 | 371 | 层叠布局具有较强的页面层叠、位置定位能力,其使用场景有广告、卡片层叠效果等。 372 | 这有点类似Android中的FrameLayout 373 | 374 | 375 | ### 弹性布局(Flex) 376 | 377 | 弹性布局是与线性布局类似的布局方式。区别在于弹性布局默认能够使子组件压缩或拉伸。在子组件需要计算拉伸或压缩比例时优先使用此布局,可使得多个容器内子组件能有更好的视觉上的填充容器效果。 378 | 379 | 弹性布局(Flex)提供更加有效的方式对容器中的子元素进行排列、对齐和分配剩余空间。常用于页面头部导航栏的均匀分布、页面框架的搭建、多行数据的排列等。 380 | 381 | 382 | 383 | 384 | ### 相对布局(RelativeContainer) 385 | 386 | RelativeContainer为采用相对布局的容器,支持容器内部的子元素设置相对位置关系。子元素支持指定兄弟元素作为锚点,也支持指定父容器作为锚点,基于锚点做相对位置布局。 387 | 388 | ```TypeScript 389 | @Entry 390 | @Component 391 | struct Index { 392 | build() { 393 | Row() { 394 | RelativeContainer() { 395 | Row() 396 | .width(100) 397 | .height(100) 398 | .backgroundColor('#FF3333') 399 | .alignRules({ 400 | top: { anchor: '__container__', align: VerticalAlign.Top }, //以父容器为锚点,竖直方向顶头对齐 401 | middle: { anchor: '__container__', align: HorizontalAlign.Center } //以父容器为锚点,水平方向居中对齐 402 | }) 403 | .id('row1') //设置锚点为row1 404 | 405 | Row() { 406 | Image($r('app.media.icon')) 407 | } 408 | .height(100).width(100) 409 | .alignRules({ 410 | top: { anchor: 'row1', align: VerticalAlign.Bottom }, //以row1组件为锚点,竖直方向低端对齐 411 | left: { anchor: 'row1', align: HorizontalAlign.Start } //以row1组件为锚点,水平方向开头对齐 412 | }) 413 | .id('row2') //设置锚点为row2 414 | 415 | Row() 416 | .width(100) 417 | .height(100) 418 | .backgroundColor('#FFCC00') 419 | .alignRules({ 420 | top: { anchor: 'row2', align: VerticalAlign.Top } 421 | }) 422 | .id('row3') //设置锚点为row3 423 | 424 | Row() 425 | .width(100) 426 | .height(100) 427 | .backgroundColor('#FF9966') 428 | .alignRules({ 429 | top: { anchor: 'row2', align: VerticalAlign.Top }, 430 | left: { anchor: 'row2', align: HorizontalAlign.End }, 431 | }) 432 | .id('row4') //设置锚点为row4 433 | 434 | Row() 435 | .width(100) 436 | .height(100) 437 | .backgroundColor('#FF66FF') 438 | .alignRules({ 439 | top: { anchor: 'row2', align: VerticalAlign.Bottom }, 440 | middle: { anchor: 'row2', align: HorizontalAlign.Center } 441 | }) 442 | .id('row5') //设置锚点为row5 443 | } 444 | .width(300).height(300) 445 | .border({ width: 2, color: '#6699FF' }) 446 | } 447 | .height('100%').margin({ left: 30 }) 448 | } 449 | } 450 | ``` 451 | 452 | 453 | 454 | ### 栅格布局(GridRow/GridCol) 455 | 456 | 栅格布局是一种通用的辅助定位工具,对移动设备的界面设计有较好的借鉴作用。主要优势包括: 457 | 458 | 提供可循的规律:栅格布局可以为布局提供规律性的结构,解决多尺寸多设备的动态布局问题。通过将页面划分为等宽的列数和行数,可以方便地对页面元素进行定位和排版。 459 | 460 | 统一的定位标注:栅格布局可以为系统提供一种统一的定位标注,保证不同设备上各个模块的布局一致性。这可以减少设计和开发的复杂度,提高工作效率。 461 | 462 | 灵活的间距调整方法:栅格布局可以提供一种灵活的间距调整方法,满足特殊场景布局调整的需求。通过调整列与列之间和行与行之间的间距,可以控制整个页面的排版效果。 463 | 464 | 自动换行和自适应:栅格布局可以完成一对多布局的自动换行和自适应。当页面元素的数量超出了一行或一列的容量时,他们会自动换到下一行或下一列,并且在不同的设备上自适应排版,使得页面布局更加灵活和适应性强。 465 | 466 | GridRow为栅格容器组件,需与栅格子组件GridCol在栅格布局场景中联合使用。 467 | 468 | 469 | 470 | 471 | ### 创建列表(List) 472 | 473 | List是很常用的滚动类容器组件,一般和子组件ListItem一起使用,List列表中的每一个列表项对应一个ListItem组件。 474 | List组件里面的列表项默认是按垂直方向排列的,如果您想让列表沿水平方向排列,您可以将List组件的listDirection属性设置为Axis.Horizontal。 475 | 476 | 477 | 478 | 列表是一种复杂的容器,当列表项达到一定数量,内容超过屏幕大小时,可以自动提供滚动功能。它适合用于呈现同类数据类型或数据类型集,例如图片和文本。 479 | 480 | 在列表中显示数据集合是许多应用程序中的常见要求(如通讯录、音乐列表、购物清单等)。 481 | 482 | 使用列表可以轻松高效地显示结构化、可滚动的信息。 483 | 484 | 通过在List组件中按垂直或者水平方向线性排列子组件ListItemGroup或ListItem,为列表中的行或列提供单个视图,或使用循环渲染迭代一组行或列,或混合任意数量的单个视图和ForEach结构,构建一个列表。 485 | 486 | List组件支持使用条件渲染、循环渲染、懒加载等渲染控制方式生成子组件。 487 | 488 | 类似Android的ListView。 489 | 490 | 491 | 492 | ```TypeScript 493 | List({space: 10}) { 494 | ForEach([1, 2, 3, 4], item => { 495 | ListItem() { 496 | // 列表项内容,只能包含一个根组件 497 | Text('item') 498 | } 499 | }) 500 | } 501 | .width('100%') 502 | // 设置item的对齐方式 503 | .alignListItem(ListItemAlign.Center) 504 | // 设置几列 505 | .lanes(2) 506 | ``` 507 | 508 | #### 设置列表分割线 509 | 510 | List组件子组件ListItem之间默认是没有分割线的,部分场景子组件ListItem间需要设置分割线,这时候您可以使用List组件的divider属性。divider属性包含四个参数: 511 | 512 | - strokeWidth: 分割线的线宽。 513 | - color: 分割线的颜色。 514 | - startMargin:分割线距离列表侧边起始端的距离。 515 | - endMargin: 分割线距离列表侧边结束端的距离。 516 | 517 | List组件提供了一系列事件方法用来监听列表的滚动,您可以根据需要,监听这些事件来做一些操作: 518 | 519 | - onScroll:列表滑动时触发,返回值scrollOffset为滑动偏移量,scrollState为当前滑动状态。 520 | - onScrollIndex:列表滑动时触发,返回值分别为滑动起始位置索引值与滑动结束位置索引值。 521 | - onReachStart:列表到达起始位置时触发。 522 | - onReachEnd:列表到底末尾位置时触发。 523 | - onScrollStop:列表滑动停止时触发。 524 | 525 | ### 循环渲染 526 | 527 | ForEach,这里的渲染循环并不是前面语法说的for循环 528 | 529 | 530 | 531 | ```TypeScript 532 | ForEach ( 533 | arr: Array, // 1.要遍历的数据数组 534 | (item: any, index?: number) => { // 2.页面组件生成函数 535 | 536 | Row() { 537 | Image(item.image) 538 | Column() { 539 | Text(item.name) 540 | Text(item.price) 541 | } 542 | } 543 | 544 | }), 545 | keyGenerator?: (item: any, index?: number): string => { // 3.键生成函数,为数组每一项生成一个唯一标识,组件是否重新渲染的判断标准 546 | 547 | } 548 | ) 549 | ``` 550 | 551 | 552 | ForEach接口基于数组类型数据来进行循环渲染,需要与容器组件配合使用,且接口返回的组件应当是允许包含在ForEach父容器组件中的子组件。例如,ListItem组件要求ForEach的父容器组件必须为List组件。 553 | 554 | 555 | 在ForEach循环渲染过程中,系统会为每个数组元素生成一个唯一且持久的键值,用于标识对应的组件。当这个键值变化时,ArkUI框架将视为该数组元素已被替换或修改,并会基于新的键值创建一个新的组件。 556 | 557 | ForEach提供了一个名为keyGenerator的参数,这是一个函数,开发者可以通过它自定义键值的生成规则。如果开发者没有定义keyGenerator函数,则ArkUI框架会使用默认的键值生成函数,即(item: any, index: number) => { return index + '__' + JSON.stringify(item); }。 558 | 559 | 560 | 561 | 在初始化渲染时,ForEach会加载数据源的所有数据,并为每个数据项创建对应的组件,然后将其挂载到渲染树上。如果数据源非常大或有特定的性能需求,建议使用LazyForEach组件。 562 | 563 | 564 | ### 创建网格(Grid/GridItem) 565 | 566 | Grid组件为网格容器,是一种网格列表,由“行”和“列”分割的单元格所组成,通过指定“项目”所在的单元格做出各种各样的布局。Grid组件一般和子组件GridItem一起使用,Grid列表中的每一个条目对应一个GridItem组件。 567 | 568 | 569 | ArkUI提供了Grid容器组件和子组件GridItem,用于构建网格布局。Grid用于设置网格布局相关参数,GridItem定义子组件相关特征。Grid组件支持使用条件渲染、循环渲染、懒加载等方式生成子组件。 570 | 571 | ```TypeScript 572 | @Entry 573 | @Component 574 | struct GridExample { 575 | // 定义一个长度为16的数组 576 | private arr: string[] = new Array(16).fill('').map((_, index) => `item ${index}`); 577 | 578 | build() { 579 | Column() { 580 | Grid() { 581 | ForEach(this.arr, (item: string) => { 582 | GridItem() { 583 | Text(item) 584 | .fontSize(16) 585 | .fontColor(Color.White) 586 | .backgroundColor(0x007DFF) 587 | .width('100%') 588 | .height('100%') 589 | .textAlign(TextAlign.Center) 590 | } 591 | }, item => item) 592 | } 593 | .columnsTemplate('1fr 1fr 1fr 1fr') 594 | .rowsTemplate('1fr 1fr 1fr 1fr') 595 | .columnsGap(10) 596 | .rowsGap(10) 597 | .height(300) 598 | } 599 | .width('100%') 600 | .padding(12) 601 | .backgroundColor(0xF1F3F5) 602 | } 603 | } 604 | ``` 605 | 606 | - 设置columnsTemplate的值为'1fr 1fr 1fr 1fr',表示这个网格为4列,将Grid允许的宽分为4等分,每列占1份; 607 | - rowsTemplate的值为'1fr 1fr 1fr 1fr',表示这个网格为4行,将Grid允许的高分为4等分,每行占1份。 608 | 609 | 这样就构成了一个4行4列的网格列表,然后使用columnsGap设置列间距为10vp,使用rowsGap设置行间距也为10vp。 610 | 611 | 上面构建的网格布局使用了固定的行数和列数,所以构建出的网格是不可滚动的。然而有时候因为内容较多,我们通过滚动的方式来显示更多的内容,就需要一个可以滚动的网格布局。我们只需要设置rowsTemplate和columnsTemplate中的一个即可。 612 | 613 | 614 | ### LazyForEach 615 | 616 | 数据懒加载 617 | 618 | LazyForEach从提供的数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。当在滚动容器中使用了LazyForEach,框架会根据滚动容器可视区域按需创建组件,当组件滑出可视区域外时,框架会进行组件销毁回收以降低内存占用。 619 | 620 | 621 | ```TypeScript 622 | LazyForEach( 623 | dataSource: IDataSource, // 需要进行数据迭代的数据源 624 | itemGenerator: (item: Object) => void, // 子组件生成函数 625 | keyGenerator?: (item: Object): string => string // 键值生成函数 626 | ): void 627 | ``` 628 | 629 | 630 | 631 | #### 使用限制 632 | LazyForEach必须在容器组件内使用,仅有List、Grid、Swiper以及WaterFlow组件支持数据懒加载(可配置cachedCount属性,即只加载可视部分以及其前后少量数据用于缓冲),其他组件仍然是一次性加载所有的数据。 633 | 634 | LazyForEach在每次迭代中,必须创建且只允许创建一个子组件。 635 | 636 | 生成的子组件必须是允许包含在LazyForEach父容器组件中的子组件。 637 | 638 | 允许LazyForEach包含在if/else条件渲染语句中,也允许LazyForEach中出现if/else条件渲染语句。 639 | 640 | 键值生成器必须针对每个数据生成唯一的值,如果键值相同,将导致键值相同的UI组件被框架忽略,从而无法在父容器内显示。 641 | 642 | LazyForEach必须使用DataChangeListener对象来进行更新,第一个参数dataSource使用状态变量时,状态变量改变不会触发LazyForEach的UI刷新。 643 | 644 | 为了高性能渲染,通过DataChangeListener对象的onDataChange方法来更新UI时,需要生成不同于原来的键值来触发组件刷新。 645 | 646 | ### 性能提升的推荐方法 647 | 648 | 649 | 开发者在使用长列表时,如果直接采用循环渲染方式,如下所示,会一次性加载所有的列表元素,一方面会导致页面启动时间过长,影响用户体验,另一方面也会增加服务器的压力和流量,加重系统负担。 650 | 651 | 我们希望从数据源中按需迭代加载数据并创建相应组件,因此需要使用数据懒加载,如下所示: 652 | #### 使用数据懒加载 653 | 654 | ```TypeScript 655 | class BasicDataSource implements IDataSource { 656 | private listeners: DataChangeListener[] = [] 657 | 658 | public totalCount(): number { 659 | return 0 660 | } 661 | 662 | public getData(index: number): any { 663 | return undefined 664 | } 665 | 666 | registerDataChangeListener(listener: DataChangeListener): void { 667 | if (this.listeners.indexOf(listener) < 0) { 668 | console.info('add listener') 669 | this.listeners.push(listener) 670 | } 671 | } 672 | 673 | unregisterDataChangeListener(listener: DataChangeListener): void { 674 | const pos = this.listeners.indexOf(listener); 675 | if (pos >= 0) { 676 | console.info('remove listener') 677 | this.listeners.splice(pos, 1) 678 | } 679 | } 680 | 681 | notifyDataReload(): void { 682 | this.listeners.forEach(listener => { 683 | listener.onDataReloaded() 684 | }) 685 | } 686 | 687 | notifyDataAdd(index: number): void { 688 | this.listeners.forEach(listener => { 689 | listener.onDataAdd(index) 690 | }) 691 | } 692 | 693 | notifyDataChange(index: number): void { 694 | this.listeners.forEach(listener => { 695 | listener.onDataChange(index) 696 | }) 697 | } 698 | 699 | notifyDataDelete(index: number): void { 700 | this.listeners.forEach(listener => { 701 | listener.onDataDelete(index) 702 | }) 703 | } 704 | 705 | notifyDataMove(from: number, to: number): void { 706 | this.listeners.forEach(listener => { 707 | listener.onDataMove(from, to) 708 | }) 709 | } 710 | } 711 | 712 | class MyDataSource extends BasicDataSource { 713 | private dataArray: string[] = ['item value: 0', 'item value: 1', 'item value: 2'] 714 | 715 | public totalCount(): number { 716 | return this.dataArray.length 717 | } 718 | 719 | public getData(index: number): any { 720 | return this.dataArray[index] 721 | } 722 | 723 | public addData(index: number, data: string): void { 724 | this.dataArray.splice(index, 0, data) 725 | this.notifyDataAdd(index) 726 | } 727 | 728 | public pushData(data: string): void { 729 | this.dataArray.push(data) 730 | this.notifyDataAdd(this.dataArray.length - 1) 731 | } 732 | } 733 | 734 | @Entry 735 | @Component 736 | struct MyComponent { 737 | private data: MyDataSource = new MyDataSource() 738 | 739 | build() { 740 | List() { 741 | LazyForEach(this.data, (item: string) => { 742 | ListItem() { 743 | Row() { 744 | Text(item).fontSize(20).margin({ left: 10 }) 745 | } 746 | } 747 | .onClick(() => { 748 | this.data.pushData('item value: ' + this.data.totalCount()) 749 | }) 750 | }, item => item) 751 | } 752 | } 753 | } 754 | ``` 755 | 756 | #### 设置List组件的宽高 757 | 758 | 759 | 在使用Scroll容器组件嵌套List组件加载长列表时,若不指定List的宽高尺寸,则默认全部加载。 760 | 761 | 说明 762 | Scroll嵌套List时: 763 | 764 | List没有设置宽高,会布局List的所有子组件。 765 | List设置宽高,会布局List显示区域内的子组件。 766 | List使用ForEach加载子组件时,无论是否设置List的宽高,都会加载所有子组件。 767 | List使用LazyForEach加载子组件时,没有设置List的宽高,会加载所有子组件,设置了List的宽高,会加载List显示区域内的子组件。 768 | 769 | 770 | 771 | 772 | #### 减少应用滑动白块 773 | 应用通过增大List/Grid控件的cachedCount参数,调整UI的加载范围。cachedCount表示屏幕外List/Grid预加载item的个数。 774 | 775 | 如果需要请求网络图片,可以在item滑动到屏幕显示之前,提前下载好内容,从而减少滑动白块。 776 | 777 | 778 | ### 创建轮播(Swiper) 779 | Swiper组件提供滑动轮播显示的能力。Swiper本身是一个容器组件,当设置了多个子组件后,可以对这些子组件进行轮播显示。通常,在一些应用首页显示推荐的内容时,需要用到轮播显示的能力。 780 | 781 | #### 布局与约束 782 | Swiper作为一个容器组件,在自身尺寸属性未被设置时,会自动根据子组件的大小设置自身的尺寸。如果开发者对Swiper组件设置了固定的尺寸,则在轮播显示过程中均以该尺寸生效;否则,在轮播过程中,会根据子组件的大小自动调整自身的尺寸。 783 | 784 | 785 | ### 页面和自定义组件生命周期 786 | 在开始之前,我们先明确自定义组件和页面的关系: 787 | 788 | 自定义组件:@Component装饰的UI单元,可以组合多个系统组件实现UI的复用。 789 | 790 | 页面:即应用的UI页面。可以由一个或者多个自定义组件组成,@Entry装饰的自定义组件为页面的入口组件,即页面的根节点,一个页面有且仅能有一个@Entry。只有被@Entry装饰的组件才可以调用页面的生命周期。 791 | 792 | ![image](https://github.com/CharonChui/Pictures/blob/master/arkts_componet_shengming.png?raw=true) 793 | 794 | 页面生命周期,即被@Entry装饰的组件生命周期,提供以下生命周期接口: 795 | 796 | - onPageShow:页面每次显示时触发。 797 | 798 | - onPageHide:页面每次隐藏时触发一次。 799 | 800 | - onBackPress:当用户点击返回按钮时触发。 801 | 802 | 组件生命周期,即一般用@Component装饰的自定义组件的生命周期,提供以下生命周期接口: 803 | 804 | - aboutToAppear:组件即将出现时回调该接口,具体时机为在创建自定义组件的新实例后,在执行其build()函数之前执行。 805 | 806 | - aboutToDisappear:在自定义组件即将析构销毁时执行。 807 | 808 | 生命周期流程如下图所示,下图展示的是被@Entry装饰的组件(首页)生命周期。 809 | 810 | ![image](https://github.com/CharonChui/Pictures/blob/master/zh-cn_image_0000001502372786.png?raw=true) 811 | 812 | ```TypeScript 813 | @Component 814 | struct Child { 815 | @State title: string = 'Hello World'; 816 | // 组件生命周期 817 | aboutToDisappear() { 818 | console.info('[lifeCycle] Child aboutToDisappear') 819 | } 820 | // 组件生命周期 821 | aboutToAppear() { 822 | console.info('[lifeCycle] Child aboutToAppear') 823 | } 824 | 825 | build() { 826 | Text(this.title) 827 | .fontSize(50) 828 | .onClick(() => { 829 | this.title = 'Hello ArkUI'; 830 | }) 831 | } 832 | } 833 | ``` 834 | 835 | 836 | ### 自定义组件 837 | 838 | 839 | #### 方式1:@Component装饰器进行自定义组件抽取 840 | 841 | ##### 1.1当前组件内抽取 842 | 843 | ```TypeScript 844 | @Entry 845 | @Component 846 | struct Second { 847 | // 注意string是小写,不是String 848 | @State message: string = "Hello HarmonyOS" 849 | 850 | build() { 851 | Row() { 852 | Column() { 853 | Header({content: 'haha'}) 854 | Text(this.message) 855 | .fontSize(30) 856 | .fontWeight(FontWeight.Bold) 857 | } 858 | .width('100%') 859 | } 860 | .height('100%') 861 | } 862 | } 863 | 864 | @Component 865 | struct Header { 866 | content: string 867 | build() { 868 | Text(this.content) 869 | .fontSize(15) 870 | } 871 | } 872 | ``` 873 | 874 | ##### 1.2单独文件抽取 875 | 在ets目录下新加conponents文件夹,里面新加Header.ets 876 | ```TypeScript 877 | @Component 878 | // 想要输出的模块,必须加上export 879 | export struct Header { 880 | content: string 881 | build() { 882 | Text(this.content) 883 | .fontSize(15) 884 | } 885 | } 886 | ``` 887 | 888 | 使用: 889 | 890 | ```TypeScript 891 | // 需要先导入组件 892 | import {Header} from '../components/Header' 893 | @Entry 894 | @Component 895 | struct Second { 896 | // 注意string是小写,不是String 897 | @State message: string = "Hello HarmonyOS" 898 | 899 | build() { 900 | Row() { 901 | Column() { 902 | Header({content: 'haha'}) 903 | Text(this.message) 904 | .fontSize(30) 905 | .fontWeight(FontWeight.Bold) 906 | } 907 | .width('100%') 908 | } 909 | .height('100%') 910 | } 911 | } 912 | 913 | ``` 914 | 915 | #### 方式2:@Builder装饰器进行自定义构建函数 916 | 前面章节介绍了如何创建一个自定义组件。该自定义组件内部UI结构固定,仅与使用方进行数据传递。ArkUI还提供了一种更轻量的UI元素复用机制@Builder,@Builder所装饰的函数遵循build()函数语法规则,开发者可以将重复使用的UI元素抽象成一个方法,在build方法里调用。 917 | 918 | 为了简化语言,我们将@Builder装饰的函数也称为“自定义构建函数”。 919 | 920 | 921 | ##### 组件状态管理 922 | 923 | ArkUI框架提供了多种管理状态的装饰器来修饰变量,使用这些装饰器修饰的变量即称为状态变量。 924 | 925 | 在组件范围传递的状态管理常见的场景如下: 926 | ![image](https://github.com/CharonChui/Pictures/blob/master/arkts_state_change.jpg?raw=true) 927 | 928 | 929 | - @State装饰的变量是组件内部的状态数据,当这些状态数据被修改时,将会调用所在组件的build方法进行UI刷新。 930 | 931 | - @Prop与@State有相同的语义,但初始化方式不同。@Prop装饰的变量必须使用其父组件提供的@State变量进行初始化,允许组件内部修改@Prop变量,但更改不会通知给父组件,即@Prop属于单向数据绑定。 932 | 933 | - @Link装饰的变量可以和父组件的@State变量建立双向数据绑定,需要注意的是:@Link变量不能在组件内部进行初始化。 934 | 935 | 936 | 937 | `@Link`装饰的变量可以和父组件的`@State`变量建立双向数据绑定,任何一方所做的修改都会反应给另一方: 938 | 939 | 子组件可以通过参数传递父组件中的@State值,而且可以通过`$`操作符创建“引用”,达到双向绑定的作用: 940 | ```TypeScript 941 | @Entry 942 | @Component RankPage { 943 | @State isSwitchDataSource: boolean = true 944 | 945 | build() { 946 | Column() { 947 | // 对子组件的@Link成员变量进行初始化,必须使用$操作符创建引用达到双向绑定的目的 948 | TileComponent({isRefreshData: $isSwitchDataSource}) { 949 | .... 950 | } 951 | } 952 | } 953 | } 954 | 955 | @Component { 956 | export struct TileComponent { 957 | // @Link修饰的变量不能初始化,要父组件进行初始化 958 | @Link isRefreshData: boolean 959 | 960 | build() { 961 | Row() { 962 | Button('click') 963 | .onClick() => { 964 | // 会引起父组件的刷新 965 | this.isRefreshData = !this.isRefreshData 966 | } 967 | } 968 | } 969 | } 970 | } 971 | ``` 972 | 973 | 管理组件拥有的状态,即图中Components级别的状态管理: 974 | 975 | - @State:@State装饰的变量拥有其所属组件的状态,可以作为其子组件单向和双向同步的数据源。当其数值改变时,会引起相关组件的渲染刷新。 976 | 977 | - @Prop:@Prop装饰的变量可以和父组件建立单向同步关系,@Prop装饰的变量是可变的,但修改不会同步回父组件。 978 | 979 | - @Link:@Link装饰的变量和父组件构建双向同步关系的状态变量,父组件会接受来自@Link装饰的变量的修改的同步,父组件的更新也会同步给@Link装饰的变量。 980 | 981 | - @Provide/@Consume:@Provide/@Consume装饰的变量用于跨组件层级(多层组件)同步状态变量,可以不需要通过参数命名机制传递,通过alias(别名)或者属性名绑定。 982 | 983 | - @Observed:@Observed装饰class,需要观察多层嵌套场景的class需要被@Observed装饰。单独使用@Observed没有任何作用,需要和@ObjectLink、@Prop连用。 984 | 985 | - @ObjectLink:@ObjectLink装饰的变量接收@Observed装饰的class的实例,应用于观察多层嵌套场景,和父组件的数据源构建双向同步。 986 | 987 | ##### 2.1自定义组件内自定义构建函数 988 | 989 | ArkUI提供了一种轻量的UI元素复用机制@Builder。 990 | 991 | 为了简化语言,我们将@Builder装饰的函数也称为"自定义构建函数"。 992 | 993 | 994 | 在外组件struct内部定义: 995 | 定义的语法: 996 | 997 | ```TypeScript 998 | // 组件内的构建函数不用加function 999 | @Builder MyBuilderFunction() { ... } 1000 | ``` 1001 | 1002 | 使用方法: 1003 | ```TypeScript 1004 | // 组件内的构建函数需要使用this 1005 | this.MyBuilderFunction() { ... } 1006 | ``` 1007 | 1008 | 允许在自定义组件内定义一个或多个@Builder方法,该方法被认为是该组件的私有、特殊类型的成员函数。 1009 | 1010 | 自定义构建函数可以在所属组件的build方法和其他自定义构建函数中调用,但不允许在组件外调用。 1011 | 1012 | 在自定义函数体中,this指代当前所属组件,组件的状态变量可以在自定义构建函数内访问。建议通过this访问自定义组件的状态变量而不是参数传递。 1013 | 1014 | ##### 2.2全局自定义构建函数 1015 | 1016 | 定义在当前组件文件内,但是在当前组件struct之外 1017 | 定义的语法: 1018 | 1019 | ```TypeScript 1020 | // 全局构建函数需要用function 1021 | @Builder function MyGlobalBuilderFunction() { ... } 1022 | ``` 1023 | 1024 | 使用方法: 1025 | 1026 | ```TypeScript 1027 | // 直接调用,不用this 1028 | MyGlobalBuilderFunction() 1029 | ``` 1030 | 1031 | - 全局的自定义构建函数可以被整个应用获取,不允许使用this和bind方法。 1032 | 1033 | - 如果不涉及组件状态变化,建议使用全局的自定义构建方法。 1034 | 1035 | 自定义构建函数的参数传递有按值传递和按引用传递两种,均需遵守以下规则: 1036 | 1037 | - 参数的类型必须与参数声明的类型一致,不允许undefined、null和返回undefined、null的表达式。 1038 | 1039 | - 在@Builder修饰的函数内部,不允许改变参数值。 1040 | 1041 | - @Builder内UI语法遵循UI语法规则。 1042 | 1043 | ##### 按引用传递参数 1044 | 1045 | 按引用传递参数时,传递的参数可为状态变量,且状态变量的改变会引起@Builder方法内的UI刷新。ArkUI提供$$作为按引用传递参数的范式。 1046 | 1047 | ```TypeScript 1048 | class ABuilderParam { 1049 | paramA1: string = '' 1050 | paramB1: string = '' 1051 | } 1052 | 1053 | @Builder function ABuilder($$ : ABuilderParam) {...} 1054 | ``` 1055 | 1056 | 1057 | ```TypeScript 1058 | class ABuilderParam { 1059 | paramA1: string = '' 1060 | } 1061 | 1062 | @Builder function ABuilder($$: ABuilderParam) { 1063 | Row() { 1064 | Text(`UseStateVarByReference: ${$$.paramA1} `) 1065 | } 1066 | } 1067 | @Entry 1068 | @Component 1069 | struct Parent { 1070 | @State label: string = 'Hello'; 1071 | build() { 1072 | Column() { 1073 | // 在Parent组件中调用ABuilder的时候,将this.label引用传递给ABuilder 1074 | ABuilder({ paramA1: this.label }) 1075 | Button('Click me').onClick(() => { 1076 | // 点击“Click me”后,UI从“Hello”刷新为“ArkUI” 1077 | this.label = 'ArkUI'; 1078 | }) 1079 | } 1080 | } 1081 | } 1082 | ``` 1083 | 1084 | $$运算符:给内置组件提供TS变量的引用,使得TS变量和内置组件的内部状态保持同步。 1085 | 1086 | 1087 | 1088 | 1089 | 1090 | ##### 按值传递参数 1091 | 调用@Builder装饰的函数默认按值传递。当传递的参数为状态变量时,状态变量的改变不会引起@Builder方法内的UI刷新。所以当使用状态变量的时候,推荐使用按引用传递。 1092 | 1093 | ```TypeScript 1094 | @Builder function ABuilder(paramA1: string) { 1095 | Row() { 1096 | Text(`UseStateVarByValue: ${paramA1} `) 1097 | } 1098 | } 1099 | @Entry 1100 | @Component 1101 | struct Parent { 1102 | label: string = 'Hello'; 1103 | build() { 1104 | Column() { 1105 | ABuilder(this.label) 1106 | } 1107 | } 1108 | } 1109 | ``` 1110 | 1111 | 1112 | ### 自定义组件通用信息 1113 | 1114 | 外部自定义样式: 1115 | 1116 | ```TypeScript 1117 | @Entry 1118 | @Component 1119 | struct Second { 1120 | // 注意string是小写,不是String 1121 | @State message: string = "Hello HarmonyOS" 1122 | 1123 | build() { 1124 | Row() { 1125 | Column() { 1126 | Text(this.message) 1127 | .fillScreen(); 1128 | } 1129 | .width('100%') 1130 | } 1131 | .height('100%') 1132 | } 1133 | } 1134 | 1135 | // 全局公共样式函数 1136 | @Styles function fillScreen() { 1137 | .width('100%') 1138 | .height('100%') 1139 | .backgroundColor('#f00') 1140 | .padding('20') 1141 | } 1142 | ``` 1143 | 内部自定义样式函数,同样是去掉function: 1144 | ```TypeScript 1145 | @Entry 1146 | @Component 1147 | struct Second { 1148 | // 注意string是小写,不是String 1149 | @State message: string = "Hello HarmonyOS" 1150 | 1151 | @Styles fillScreen() { 1152 | .width('100%') 1153 | .height('100%') 1154 | .backgroundColor('#f00') 1155 | .padding('20') 1156 | } 1157 | 1158 | build() { 1159 | Row() { 1160 | Column() { 1161 | Text(this.message) 1162 | .fillScreen() 1163 | } 1164 | .width('100%') 1165 | } 1166 | .height('100%') 1167 | } 1168 | } 1169 | ``` 1170 | 1171 | 上面的属性都是所有组件通用的属性,但是如果我想对Text里面的fontSize、fontColor进行抽取呢? 这个时候因为不是通用的,用@Style就不行了。那就要用@Extend来抽取。 1172 | 1173 | #### @Extend装饰器:自定义扩展组件样式 1174 | 1175 | 类似Kotlin的扩展函数 1176 | 1177 | @Extend,用于扩展原生组件信息(非通用组件信息)。 1178 | 1179 | 使用规则: 1180 | 1181 | - 和@Styles不同,@Extend仅支持在全局定义,不支持在组件内部定义。 1182 | 1183 | - 和@Styles不同,@Extend支持的方法支持参数,开发者可以在调用时传递参数a。 1184 | 1185 | - @Extend的参数可以为状态变量,当状态变量改变时,UI可以正常的被刷新渲染。 1186 | 1187 | 1188 | **注意: @Extend只能写在全局,不能写在组件内部** 1189 | 1190 | ```TypeScript 1191 | // @Extend(Text)可以支持Text的私有属性fontColor 1192 | @Extend(Text) function fancy () { 1193 | .fontColor(Color.Red) 1194 | } 1195 | // superFancyText可以调用预定义的fancy 1196 | @Extend(Text) function superFancyText(size:number) { 1197 | .fontSize(size) 1198 | .fancy() 1199 | } 1200 | ``` 1201 | 1202 | 1203 | ### 组件通用信息 1204 | 1205 | 所有组件都有的通用属性或事件,分为三类: 1206 | 1207 | 1208 | - 通用事件 1209 | - 通用属性 1210 | - 手势处理 1211 | 1212 | 鼠标在组件上右键,菜单会显示Show in Api Reference。 1213 | ![image](https://github.com/CharonChui/Pictures/blob/master/arkts_show_api_reference.png?raw=true) 1214 | 1215 | 1216 | 点击进去可以看到组件的文档,里面有通用属性的介绍 1217 | ![image](https://github.com/CharonChui/Pictures/blob/master/arkts_common_info.png?raw=true) 1218 | 1219 | 1220 | - 点击事件 1221 | 1222 | ```TypeScript 1223 | onClick(event: (event?: ClickEvent) => void) 1224 | ``` 1225 | 1226 | #### stateStyles:多态样式 1227 | 1228 | @Styles和@Extend仅仅应用于静态页面的样式复用,stateStyles可以依据组件的内部状态的不同,快速设置不同样式。 1229 | 1230 | stateStyles是属性方法,可以根据UI内部状态来设置样式,类似于css伪类,但语法不同。ArkUI提供以下几种状态: 1231 | 1232 | - focused:获焦态。 1233 | 1234 | - normal:正常态。 1235 | 1236 | - pressed:按压态。 1237 | 1238 | - disabled:不可用态。 1239 | 1240 | - selected:选中态。 1241 | 1242 | ```TypeScript 1243 | Button('Click me') 1244 | .stateStyles({ 1245 | focused: { 1246 | .backgroundColor(Color.Pink) 1247 | }, 1248 | pressed: { 1249 | .backgroundColor(Color.Black) 1250 | }, 1251 | normal: { 1252 | .backgroundColor(Color.Yellow) 1253 | } 1254 | }) 1255 | ``` 1256 | 1257 | 1258 | ### 使用资源引用类型 1259 | 1260 | Resource是资源引用类型,用于设置组件属性的值。推荐大家优先使用Resource类型,将资源文件(字符串、图片、音频等)统一存放于resources目录下,便于开发者统一维护。同时系统可以根据当前配置加载合适的资源,例如,开发者可以根据屏幕尺寸呈现不同的布局效果,或根据语言设置提供不同的字符串。 1261 | 1262 | 1263 | 1264 | 在string.json中定义Button显示的文本。 1265 | ```json 1266 | { 1267 | "string": [ 1268 | { 1269 | "name": "login_text", 1270 | "value": "登录" 1271 | } 1272 | ] 1273 | } 1274 | 在float.json中定义Button的宽高和字体大小。 1275 | 1276 | { 1277 | "float": [ 1278 | { 1279 | "name": "button_width", 1280 | "value": "300vp" 1281 | }, 1282 | { 1283 | "name": "button_height", 1284 | "value": "40vp" 1285 | }, 1286 | { 1287 | "name": "login_fontSize", 1288 | "value": "18fp" 1289 | } 1290 | ] 1291 | } 1292 | ``` 1293 | 在color.json中定义Button的背景颜色。 1294 | 1295 | ```json 1296 | { 1297 | "color": [ 1298 | { 1299 | "name": "button_color", 1300 | "value": "#1890ff" 1301 | } 1302 | ] 1303 | } 1304 | ``` 1305 | 然后在Button组件通过“$r('app.type.name')”的形式引用应用资源。app代表应用内resources目录中定义的资源;type代表资源类型(或资源的存放位置),可以取“color”、“float”、“string”、“plural”、“media”;name代表资源命名,由开发者定义资源时确定。 1306 | 1307 | 1308 | ### build函数 1309 | 1310 | 所有声明在build()函数的语言,我们统称为UI描述,UI描述需要遵循以下规则: 1311 | 1312 | - @Entry装饰的自定义组件,其build()函数下的根节点唯一且必要,且必须为容器组件,其中ForEach禁止作为根节点。 1313 | 1314 | - @Component装饰的自定义组件,其build()函数下的根节点唯一且必要,可以为非容器组件,其中ForEach禁止作为根节点。 1315 | 1316 | - 不允许声明本地变量。 1317 | 1318 | - 不允许在UI描述里直接使用console.info,但允许在方法或者函数里使用。 1319 | 1320 | - 不允许使用switch语法,如果需要使用条件判断,请使用if。 1321 | 1322 | - 不允许直接改变状态变量。 1323 | 不能在自定义组件的build()或@Builder方法里直接改变状态变量,这可能会造成循环渲染的风险。 1324 | 1325 | 1326 | 1327 | 1328 | 1329 | 1330 | 1331 | ---------- 1332 | 1333 | 1334 | - [上一篇:4.HarmonyOS应用开发初探](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/4.HarmonyOS%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91%E5%88%9D%E6%8E%A2.md) 1335 | - [下一篇:6.常用组件](https://github.com/CharonChui/HarmonyOSNextStudyNote/blob/main/6.%E5%B8%B8%E7%94%A8%E7%BB%84%E4%BB%B6.md) 1336 | 1337 | 1338 | 1339 | --- 1340 | 1341 | - 邮箱 :charon.chui@gmail.com 1342 | - Good Luck! 1343 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------