关于css:鸿蒙HarmonyOS应用开发初体验

2次阅读

共计 10310 个字符,预计需要花费 26 分钟才能阅读完成。

近期(4.12 ~ 4.25)鸿蒙 OS 正在举办开发者日流动,趁机加入并理解一下鸿蒙 OS 的现状和利用开发体验。

  1. 开发环境搭建

下载安装 IDE(以后版本 2.1 Beta3)
华为为 Harmony 利用开发提供了配套的 IDE:DevEco Studio(心田比拟排挤这种带 Eco 字眼儿的命名,PPT 怎么吹无所谓,开发工具咱能不能求实一点儿?)

下载 IDE 须要登录 Huawei 账号,我装置的是 Mac 版,下载后的装置过程还是比拟顺畅的

image.png

启动界面显示 DevEco Studio 依然是基于 IntelliJ 的定制 IDE

下载 SDK
跟 Android 一样,IDE 启动第一件事件是下载 Harmony SDK

image.png

每个版本的 SDK 中都提供了三套 API 用来开发 Java、Js、C++ 代码,版本上须要保持一致。不同的华为设施对 SDK 版本有不同要求,比方在测试中发现,我的 API4 的代码无奈运行在 P40 上,改为 API5 就 OK 了

对于 SDK 源码
须要留神,目前无奈通过 SDKManager 打包下载源码,源码须要通过 gitee 独自下载

gitee.com/openharmony

这为代码调试带来阻碍,不晓得前期是否能够像 Andoird 那样与 SDK 一起打包下载源码

创立我的项目
Harmony 主打多端协同,所以很器重设施多样性,可面向不同设施创立模板我的项目

Screen Shot 2021-04-18 at 10.28.09 AM.png

相比 AndroidStudio,Harmony 提供了更加丰盛的我的项目模板,模板中除了 UI 以外还提供了局部数据层代码,基本上是一个能够二次开发的 APP。

Screen Shot 2021-04-18 at 5.48.55 PM.png

  1. 鸿蒙我的项目构造

IDE 界面
试着创立了一个 News Feature Ability(新闻流)的模板我的项目,胜利在 IDE 中关上:

image.png

IDE 窗口与 AndroidStudio 相似,值得一提的 Harmony 左边提供的 Preview 窗口,能够对 xml 或者 Ablitiy 文件进行预览,有点 Compose 的 Preview 的感觉,然而只能动态预览,无奈交互

工程文件
image.png

工程文件和 Android 相似,甚至能够找到一一对应的关系

Harmony Android 阐明
entry app 默认启动模块(主模块), 相当于 app_module
MyApplication XXXApplication 鸿蒙的 MyApplication 是 AbilityPackage 的子类
MainAbility MainActivity 入口页。鸿蒙中将四大组件的概念对立成 Ability
MainAbilityListSlice XXXFragment Slice 相似 Fragment,UI 的根本组成单元
Component View Component 类相当于 View,后文介绍
config.json AndroidManifest.xml 鸿蒙应用 json 代替 xml 进行 Manifest 配置,配置我的项目差不多
resources/base/… res/… 包含 Layout 文件在内的各种资源文件仍旧应用 xml
resources/rawfile/ assets/ rawfile 存储任意格局原始资源,相当于 assets
build.gradle build.gradle 编译脚本,两者一样
build/outpus/…/.hap build/outputs/…/.apk 鸿蒙的产物是 hap(harmony application package)
解压后外面有一个同名的.apk 文件,
这后续是因为鸿蒙须要同时反对 apk 装置的兼容计划
Ability
Ability 是利用所具备能力的形象,Harmony 反对利用以 Ability 为单位进行部署。一个利用由一个或多个 FA(Feature Ability)或 PA(Particle Ability)组成。FA 有 UI 界面,提供与用户交互的能力;而 PA 无 UI 界面,提供后盾运行工作的能力以及对立的数据拜访形象

FA 反对 Page Ability:
Page Ability 用于提供与用户交互的能力。一个 Page 能够由一个或多个 AbilitySlice 形成,AbilitySlice 之间能够进行页面导航
image.png

PA 反对 Service Ability 和 Data Ability:
Service Ability:用于提供后盾运行工作的能力。
Data Ability:用于对外部提供对立的数据拜访形象。
能够感觉到,各种 Ability 能够对照 Android 的四大组件来了解

Harmony Android
Page Ability(FA)Activity
Service Ability(PA)Service
Data Ability(PA)ContentProvider
AbilitySlice Fragment
代码一览
MainAbility
以预置的 News Feature Ability 为例子,这是一个领有两个 Slice 的 Page Ability,通过 Router 注册两个 Slice

public class MainAbility extends Ability {@Override public void onStart(Intent intent) {super.onStart(intent); super.setMainRoute(MainAbilityListSlice.class.getName()); // 增加路由:ListSlice addActionRoute(“action.detail”, MainAbilityDetailSlice.class.getName());//DetailSlice … } } 复制代码 1.2.3.4.5.6.7.8.9.10.11.12.
1.
以下是在模拟器中运行两个 Slice 的页面成果

MainAbilityListSlice MainAbilityDetailSlice
image.png image.png
MainAbilityListSlice
次要看一下列表的显示逻辑

public class MainAbilityListSlice extends AbilitySlice {… @Override public void onStart(Intent intent) {super.onStart(intent); super.setUIContent(ResourceTable.Layout_news_list_layout); initView(); initData(); // 加载数据 initListener(); newsListContainer.setItemProvider(newsListAdapter); //Adatper 设置到 View newsListAdapter.notifyDataChanged(); // 刷新数据} private void initListener() { newsListContainer.setItemClickedListener((listContainer, component, i, l) -> {// 路由跳转 ”action.detail” LogUtil.info(TAG, “onItemClicked is called”); Intent intent = new Intent(); Operation operation = new Intent.OperationBuilder() .withBundleName(getBundleName()) .withAbilityName(“com.example.myapplication.MainAbility”) .withAction(“action.detail”) .build(); intent.setOperation(operation); startAbility(intent); }); } private void initData() { … totalNewsDatas = new ArrayList<>(); newsDatas = new ArrayList<>(); initNewsData();// 填充 newsDatas newsListAdapter = new NewsListAdapter(newsDatas, this);// 设置到 Adapter } … } 复制代码 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.
1.
相似 ListView 的用法,通过 Adatper 加载数据; setItemClickedListener 中通过路由跳转 MainAbilityDetailSlice。

Layout_news_list_layout 布局文件定义如下,ListContainer 即 ListView,是 Comopnent 的一个子类,Component 就是 HarmonyOS 中的 View

<?xml version=”1.0″ encoding=”utf-8″?> <DirectionalLayout xmlns:ohos=”http://schemas.huawei.com/res/ohos” ohos:height=”match_parent” ohos:width=”match_parent” ohos:orientation=”vertical”> <ListContainer ohos:id=”$+id:selector_list” ohos:height=”40vp” ohos:width=”match_parent” ohos:orientation=”horizontal” /> <Component ohos:height=”0.5vp” ohos:width=”match_parent” ohos:background_element=”#EAEAEC” /> <ListContainer ohos:id=”$+id:news_container” ohos:height=”match_parent” ohos:width=”match_parent”/> </DirectionalLayout> 复制代码 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.
1.
看一下 Adapter 的实现, 继承自 BaseItemProvider

/ News list adapter / public class NewsListAdapter extends BaseItemProvider {private List<NewsInfo> newsInfoList; private Context context; public NewsListAdapter(List<NewsInfo> listBasicInfo, Context context) {this.newsInfoList = listBasicInfo; this.context = context;} @Override public int getCount() { return newsInfoList == null ? 0 : newsInfoList.size(); } @Override public Object getItem(int position) {return Optional.of(this.newsInfoList.get(position)); } @Override public long getItemId(int position) {return position;} @Override public Component getComponent(int position, Component componentP, ComponentContainer componentContainer) {ViewHolder viewHolder = null; Component component = componentP; if (component == null) {component = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_item_news_layout, null, false); viewHolder = new ViewHolder(); Component componentTitle = component.findComponentById(ResourceTable.Id_item_news_title); Component componentImage = component.findComponentById(ResourceTable.Id_item_news_image); if (componentTitle instanceof Text) {viewHolder.title = (Text) componentTitle; } if (componentImage instanceof Image) {viewHolder.image = (Image) componentImage; } component.setTag(viewHolder); } else {if (component.getTag() instanceof ViewHolder) {viewHolder = (ViewHolder) component.getTag();} } if (null != viewHolder) {viewHolder.title.setText(newsInfoList.get(position).getTitle()); viewHolder.image.setScaleMode(Image.ScaleMode.STRETCH); } return component; } / ViewHolder which has title and image / private static class ViewHolder {Text title; Image image;} } 复制代码 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.
1.
基本上就是规范的 ListAdatper,把 View 替换成 Component 而已。

对于模拟器
代码实现后能够再模拟器中运行。对于模拟器有几点想说的:

Harmony 的模拟器启动十分快,无需下载镜像,因为这个模拟器并非本地运行,而只是一个远端设施的 VNC,因而必须在线应用,而且不够晦涩时有丢帧景象。尽管真机调试成果更好,但不是人人都买得起 P40 的

模拟器嵌入到 IDE 窗口显示(像 Preview 窗口一样),非独立窗口,这会带来一个问题,当同时关上多个 IDE 时,模拟器可能会显示在另一个 IDE 中(就像 Logcat 跑偏一样)。

想应用模拟器必须进过开发者认证,官网举荐应用银行卡认证。模拟器远端链接的是一台实在设施,难道是为将来租用设施要计费??image.png 记得以前看过一篇文章,如果是来自国外地区的注册账号能够免认证应用模拟器,然而懒得折腾了

  1. 开发 JS 利用

除了 Java,鸿蒙还反对基于 JS 开发利用,借助前端技术欠缺其跨平台能力。

鸿蒙为 JS 工程提供了多种罕用 UI 组件,然而没有采纳当下支流的 react、vue 那样 JS 组件,依然是基于 CSS3/HTML5/JS 这种传统形式进行开发。JS 工程构造如下

image.png

目录 阐明
common 可选,用于寄存公共资源文件,如媒体资源、自定义组件和 JS 文档等
i18n 可选,用于寄存多语言的 json 文件
pages/index/index.hml hml 文件定义了页面的布局构造,应用到的组件,以及这些组件的层级关系
pages/index/index.css css 文件定义了页面的款式与布局,蕴含款式选择器和各种款式属性等
pages/index/index.js js 文件形容了页面的行为逻辑,此文件里定义了页面里所用到的所有的逻辑关系,比方数据、事件等
resources 可选,用于寄存资源配置文件,比方:游戏全局款式、多分辨率加载等配置文件
app.js 全局的 JavaScript 逻辑文件和利用的生命周期治理。

  1. 跨设施迁徙

通过后面的介绍,可能感觉和 Android 大同小异,然而 HarmonyOS 最牛逼之处是多端合作能力,例如能够将 Page 在同一用户的不同设施间迁徙,实现无缝切换。

以 Page 从设施 A 迁徙到设施 B 为例,迁徙动作次要步骤如下:

设施 A 上的 Page 申请迁徙。
HarmonyOS 回调设施 A 上 Page 的保留数据办法,用于保留迁徙必须的数据。
HarmonyOS 在设施 B 上启动同一个 Page,并回调其复原数据办法。
通过调用 continueAbility()申请迁徙。如下,获取设施列表,配对胜利后申请迁徙

doConnectImg.setClickedListener(clickedView -> { // 通过 FLAG_GET_ONLINE_DEVICE 标记取得在线设施列表 List deviceInfoList = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE); if (deviceInfoList.size() < 1) {WidgetHelper.showTips(this, “ 无在网设施 ”); } else {DeviceSelectDialog dialog = new DeviceSelectDialog(this); // 点击后迁徙到指定设施 dialog.setListener(deviceInfo -> { LogUtil.debug(TAG, deviceInfo.getDeviceName()); LogUtil.info(TAG, “continue button click”); try {// 开始工作迁徙 continueAbility(); LogUtil.info(TAG, “continue button click end”); } catch (IllegalStateException | UnsupportedOperationException e) {WidgetHelper.showTips(this, ResourceTable.String_tips_mail_continue_failed); } dialog.hide();}); dialog.show();} }); 复制代码 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.
1.
Page 迁徙波及到数据传递,此时须要借助 IAbilityContinuation 进行通信。

跨设施通信 IAbilityContinuation
跨设施迁徙的 Page 须要实现 IAbilityContinuation 接口。

Note: 一个利用可能蕴含多个 Page,仅须要在反对迁徙的 Page 中通过以下办法实现 IAbilityContinuation 接口。同时,此 Page 所蕴含的所有 AbilitySlice 也须要实现此接口。

public class MainAbility extends Ability implements IAbilityContinuation {… @Override public void onCompleteContinuation(int code) {} @Override public boolean onRestoreData(IntentParams params) {return true;} @Override public boolean onSaveData(IntentParams params) {return true;} @Override public boolean onStartContinuation() { return true;} } public class MailEditSlice extends AbilitySlice implements IAbilityContinuation {… @Override public boolean onStartContinuation() {LogUtil.info(TAG, “is start continue”); return true; } @Override public boolean onSaveData(IntentParams params) {… LogUtil.info(TAG, “begin onSaveData:” + mailData); … LogUtil.info(TAG, “end onSaveData”); return true; } @Override public boolean onRestoreData(IntentParams params) {LogUtil.info(TAG, “begin onRestoreData”); … LogUtil.info(TAG, “end onRestoreData, mail data: ” + cachedMailData); return true; } @Override public void onCompleteContinuation(int i) {LogUtil.info(TAG, “onCompleteContinuation”); terminateAbility();} } 复制代码 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.
1.
onStartContinuation(): Page 申请迁徙后,零碎首先回调此办法,开发者能够在此回调中决策以后是否能够执行迁徙,比方,弹框让用户确认是否开始迁徙。

onSaveData(): 如果 onStartContinuation()返回 true,则零碎回调此办法,开发者在此回调中保留必须传递到另外设施上以便复原 Page 状态的数据。

onRestoreData(): 源侧设施上 Page 实现保留数据后,零碎在指标侧设施上回调此办法,开发者在此回调中承受用于复原 Page 状态的数据。留神,在指标侧设施上的 Page 会重新启动其生命周期,无论其启动模式如何配置。且零碎回调此办法的机会在 onStart()之前。

onCompleteContinuation(): 指标侧设施上复原数据一旦实现,零碎就会在源侧设施上回调 Page 的此办法,以便告诉利用迁徙流程已完结。开发者能够在此查看迁徙后果是否胜利,并在此解决迁徙完结的动作,例如,利用能够在迁徙实现后终止本身生命周期。

以 Page 从设施 A 迁徙到设施 B 为例,具体的流程如下:

设施 A 上的 Page 申请迁徙。
零碎回调设施 A 上 Page 及其 AbilitySlice 栈中所有 AbilitySlice 实例的 IAbilityContinuation.onStartContinuation()办法,以确认以后是否能够立刻迁徙。
如果能够立刻迁徙,则零碎回调设施 A 上 Page 及其 AbilitySlice 栈中所有 AbilitySlice 实例的 IAbilityContinuation.onSaveData()办法,以便保留迁徙后复原状态必须的数据。
如果保留数据胜利,则零碎在设施 B 上启动同一个 Page,并复原 AbilitySlice 栈,而后回调 IAbilityContinuation.onRestoreData()办法,传递此前保留的数据;尔后设施 B 上此 Page 从 onStart()开始其生命周期回调。
零碎回调设施 A 上 Page 及其 AbilitySlice 栈中所有 AbilitySlice 实例的 IAbilityContinuation.onCompleteContinuation()办法,告诉数据恢复胜利与否。

  1. 总结和感想

从 SDK 到 IDE 与 Android 都高度类似,任何 Android 开发者基本上就是一个准鸿蒙程序员
AndroidStudio 的性能迭代很快,DevEco Studio 在性能上还存在较大差距
须要实名认证开发者之后能力应用 IDE 的各种残缺性能,心田抗拒
源码须要另外下载,对调试不敌对
以后还不反对 Kotlin。大势所趋,所以将来肯定会反对,而且 Kotlin 是开源的问题不大
JS UI 框架的技术架构同样有些过期
杀手锏是对多端合作的反对,但这可能须要更多的厂商退出能力真正施展威力
目前人们对于鸿蒙的态度出现两极化,有的人追捧有的人贬斥,我感觉都大可不必,多给鸿蒙一些空间和急躁,静观其变、乐见其成。当然首选须要华为做到本人不被动炒作,真正静下心来打磨鸿蒙,只有华为有信心有急躁,作为开发者的咱们为什么不反对呢?

正文完
 0