大型网站开发 框架,软件开发服务外包,做网站的过程中有哪些问题,南充建设公司网站ArkTS语言 前言正文一、声明式UI二、数据列表① 创建ArkTS文件② 添加资源③ 样式④ 组件⑤ 标题组件⑥ 列表头组件⑦ 列表Item组件⑧ 组件生命周期⑨ 渲染列表数据⑩ 单选 三、源码 随着华为宣布鸿蒙后续的版本不再兼容Android应用之后#xff0c;对于现在的开发环境来说有一… ArkTS语言 前言正文一、声明式UI二、数据列表① 创建ArkTS文件② 添加资源③ 样式④ 组件⑤ 标题组件⑥ 列表头组件⑦ 列表Item组件⑧ 组件生命周期⑨ 渲染列表数据⑩ 单选 三、源码 随着华为宣布鸿蒙后续的版本不再兼容Android应用之后对于现在的开发环境来说有一些冲击一部分人想做鸿蒙应用开发另一部分人觉得鸿蒙现在就想替换安卓还为之尚早不管怎么说学习是没有错的哪怕是作为知识储备也是好的今天就简单说一下鸿蒙应用开发支持的主流语言
ArkTS。 前言 说到ArkTS就得说一下DevEco Studio的演变过程在我写一篇关于鸿蒙的文章时DevEco Studio才刚推出不久当时所支持的语言是Java、JS、C等在后续的版本中逐渐去掉了JavaC最终使用到了ArkTS那么我们下面来了解一下ArkTS的由来。
正文 ArkTS是HarmonyOS主力应用开发语言它在TypeScript简称TS的基础上匹配ArkUI框架扩展了声明式UI、状态管理等相应的能力让开发者以更简洁、更自然的方式开发跨端应用如果你之前接触过Flutter的Dart、Kotlin的Compose那么你对于这个ArkTS的使用应该问题不大。 ArkTS的构成如下图所示 JavaScript、TypeScript和 ArkTS的关系
JavaScript是一种属于网络的高级脚本语言已经被广泛用于Web应用开发常用来为网页添加各式各样的动态功能为用户提供更流畅美观的浏览效果。TypeScript是JavaScript的一个超集它扩展了JavaScript的语法通过在JavaScript的基础上添加静态类型定义构建而成是一个开源的编程语言。ArkTS基于TypeScript语言拓展了声明式UI、状态管理、并发任务等能力。
一、声明式UI 声明式UI有两个特征分别是声明式描述和状态驱动视图更新那么怎么体现这一点呢我们结合代码来说明首先打开DevEco Studio这里我使用的版本是DevEco Studio 3.1.1 Release创建工程。 点击Next。 输入项目名称和包名这里会说明使用的方式和语言及SDK版本和使用的设备类型修改好之后点击Finish完成项目的创建。 完成创建之后我们就可以看到index.ets的代码如图所示下面我们对这段代码进行一个解析
装饰器用来装饰类、结构体、方法以及变量赋予其特殊的含义如上述示例中 Entry 、 Component 、 State 都是装饰器。具体而言 Component 表示这是个自定义组件 Entry 则表示这是个入口组件 State 表示组件中的状态变量此状态变化会引起 UI 变更。自定义组件可复用的 UI 单元可组合其它组件如上述被 Component 装饰的 struct Index。UI 描述声明式的方式来描述 UI 的结构如上述 build() 方法内部的代码块。内置组件框架中默认内置的基础和布局组件可直接被开发者调用比如示例中的 Row、Column、Text。事件方法用于添加组件对事件的响应逻辑统一通过事件方法进行设置比如为组件添加onClick()。属性方法用于组件属性的配置统一通过属性方法进行设置如fontSize()、width()、height()、color() 等可通过链式调用的方式设置多项属性。
下面我们预览看一下点击右侧边栏的Previewer等待一小会。 下面我们修改index.ets中的代码然后Ctrl S保存一下右侧的预览画面就会更新。 预览更新后 点击Test按钮之后 这里我们添加了一个按钮同时添加了点击事件事件中修改了message的值而message是由State修饰的那么就会出发UI刷新刷新后Text组件所显示的内容就会从Hello World变成Hello ArkTS这就是声明式UI的另一特征状态驱动视图更新。
二、数据列表 上面的示例比较简单下面我们做一个有点难度的示例该示例源于鸿蒙学堂官网感兴趣的可以去学习。 ① 创建ArkTS文件 首先我们在ets目录下创建一个model目录model目录下创建一个RankData.ets文件代码如下所示
export class RankData {id: number | stringname: Resourcevote: stringconstructor(id: number | string, name: Resource, vote: string) {this.id id;this.name name;this.vote vote;}
}这里的代码简单说明一下export 表示可以在其他模块中使用这里的含义就在于我们将RankData反正model目录下如果我们pages下要使用这个RankData则RankData本身就需要支持调用才行因此需要export进行修饰调用的地方则使用import作为插入。
而id属性的定义是一个联合类型这属于TypeScript的基础数据类型表示取值可以为多种类型中的一种。number表示数字string就是字符串Resource就表示资源比如string.app_name这种方式。构造函数就没有什么好说的了就是属性赋值而已。
② 添加资源 我们看到和ets目录平级的是resources该目录下毫无疑问就是资源目录目录下有三个文件夹base属于基础资源目录里面可以放置文字、颜色、音频、配置文件等en_US就是英文下的文字资源zh_CN就是中文下的文字资源三个目录下的文字资源文件都是json格式的下面我们修改base/element/string.json和en_US/element/string.json中的代码
{string: [{name: module_desc,value: module description},{name: EntryAbility_desc,value: description},{name: EntryAbility_label,value: label},{name: page_type,value: variety},{name: page_number,value: ranking},{name: page_vote,value: vote},{name: prompt_text,value: Press again to exit the app},{name: title,value: Ranking List},{name: title_default,value: },{name: fruit_watermelon,value: watermelon},{name: fruit_apple,value: apple},{name: fruit_banana,value: banana},{name: fruit_grapes,value: grapes},{name: fruit_red_grape,value: grape},{name: fruit_pears,value: pears},{name: fruit_pineapple,value: pineapple},{name: fruit_durian,value: durian},{name: fruit_guava,value: guava},{name: fruit_carambola,value: carambola}]
}再修改zh_CN/element/string.json中的代码
{string: [{name: module_desc,value: 模块描述},{name: EntryAbility_desc,value: description},{name: EntryAbility_label,value: label},{name: page_type,value: 种类},{name: page_number,value: 排名},{name: page_vote,value: 得票数},{name: prompt_text,value: 再按一次退出程序},{name: title,value: 排行榜},{name: title_default,value: },{name: fruit_watermelon,value: 西瓜},{name: fruit_apple,value: 苹果},{name: fruit_banana,value: 香蕉},{name: fruit_grapes,value: 葡萄},{name: fruit_red_grape,value: 红提},{name: fruit_pears,value: 梨子},{name: fruit_pineapple,value: 菠萝},{name: fruit_durian,value: 榴莲},{name: fruit_guava,value: 番石榴},{name: fruit_carambola,value: 杨桃}]
}下面我们制造一些假数据在model包下新建一个DataModel.ets文件代码如下所示
import { RankData } from ./RankDataexport {rankData1, rankData2}const rankData1: RankData[] [new RankData(1, $r(app.string.fruit_apple), 10000),new RankData(2, $r(app.string.fruit_grapes), 10320),new RankData(3, $r(app.string.fruit_watermelon), 9801),new RankData(4, $r(app.string.fruit_banana), 8431),new RankData(5, $r(app.string.fruit_pineapple), 7546),new RankData(6, $r(app.string.fruit_durian), 7431),new RankData(7, $r(app.string.fruit_red_grape), 7187),new RankData(8, $r(app.string.fruit_pears), 7003),new RankData(9, $r(app.string.fruit_carambola), 6794),new RankData(10, $r(app.string.fruit_guava), 6721)
]const rankData2: RankData[] [new RankData(11, $r(app.string.fruit_watermelon), 8836),new RankData(12, $r(app.string.fruit_apple), 8521),new RankData(13, $r(app.string.fruit_banana), 8431),new RankData(14, $r(app.string.fruit_grapes), 7909),new RankData(15, $r(app.string.fruit_red_grape), 7547),new RankData(16, $r(app.string.fruit_pears), 7433),new RankData(17, $r(app.string.fruit_pineapple), 7186),new RankData(18, $r(app.string.fruit_durian), 7023),new RankData(19, $r(app.string.fruit_guava), 6794),new RankData(20, $r(app.string.fruit_carambola), 6721)
];这里我们首先导入RankData然后创建了两个数组数组中通过RankData构建函数进行bean的构建注意这里的id我可以使用number也可以使用string同时资源的引用是 $rr就表示resource使用app.string引用文字资源你还可以app.color等一些方式引用其他类型资源构建了两个数组然后导出这两个数组在其他文件中使用。 现在有了模拟数据之后我们可以再创建一个类去提供模拟数据在model包下新建一个RankViewModel.ets文件代码如下所示
import { RankData } from ./RankData;
import { rankData1, rankData2 } from ./DataModel;export class RankViewModel {loadRankDataSource1(): RankData[] {return rankData1;}loadRankDataSource2(): RankData[] {return rankData2;}
}这里导入了RankData和DataModel通过在RankViewModel中进行返回数据得到具体的数据数组。这个其实和Android的MVI架构差不多下面我们再添加一些colors资源在后面的样式上会用到修改base/element/color.json文件代码如下所示
{color: [{name: start_window_background,value: #FFFFFF},{name: white,value: #FFFFFF},{name: rank_first_gradient_start,value: #FFFF9A},{name: rank_first_gradient_end,value: #CCA538},{name: rank_first_border,value: #9E8A24},{name: rank_first_text,value: #9E8A24},{name: rank_secondary_gradient_start,value: #B8B8B8},{name: rank_secondary_gradient_end,value: #9C9C9C},{name: rank_secondary_border,value: #7E7E7E},{name: rank_secondary_text,value: #FFFFFF},{name: rank_third_gradient_start,value: #B9A185},{name: rank_third_gradient_end,value: #AE8659},{name: rank_third_border,value: #775C3E},{name: rank_third_text,value: #FFFFFF},{name: rank_view_color_holder,value: #FFFFFF},{name: item_color,value: #007DFF},{name: item_color_black,value: #182431},{name: background,value: #F1F3F5},{name: font_description,value: #989A9C},{name: circle_text_background,value: #007dff}]
}除此之外还有三个图标你可以在我的源码中获取放在resources/base/media下 其中icon.png是创建工程时自带的图标如果你觉得Project模式下文件过多你可以切换为Ohos模式。 这样看起来比较简洁只不过你需要熟悉文件结构才行。
③ 样式 在进行鸿蒙应用开发时通常会将样式和代码进行分离这一点是很常见了我们在ets目录下新建一个constants文件夹该目录下新建一个Constants.ets文件代码如下
/*** 字体大小*/
export enum FontSize {SMALL 14,MIDDLE 16,LARGE 20,
};/*** 字体粗细*/
export enum FontWeight {BOLD 400,BOLDER 500,
};/*** 权重是组件大小的全局默认值。*/
export const WEIGHT 100%;/*** Toast 出现的时间*/
export const TIME 1000;/*** App退出的间隔时间*/
export const APP_EXIT_INTERVAL: number 4500;/*** 页面TAG*/
export const TAG: string Index;/*** 标题内容*/
export const TITLE: Resource $r(app.string.title);class style {RANK_PADDING: number 15; // 排名填充CONTENT_WIDTH: string 90%; // 内容的宽度BORDER_RADIUS: number 20; // 边界半径STROKE_WIDTH: number 1; // 描边宽度HEADER_MARGIN_TOP: number 20; // 距离上边距HEADER_MARGIN_BOTTOM: number 15;// 距离下边距LIST_HEIGHT: string 65%; // List高度
}/*** 页面样式*/
export const Style: style {RANK_PADDING: 15,CONTENT_WIDTH: 90%,BORDER_RADIUS: 20,STROKE_WIDTH: 1,HEADER_MARGIN_TOP: 20,HEADER_MARGIN_BOTTOM: 15,LIST_HEIGHT: 65%
};class listHeaderStyle {FONT_WEIGHT: number 400; // 字体粗细LAYOUT_WEIGHT_LEFT: string 30%; // 左边的布局权重LAYOUT_WEIGHT_CENTER: string 50%; // 中间的布局权重LAYOUT_WEIGHT_RIGHT: string 20%; // 右边的布局权重
}/*** 列表标题样式*/
export const ListHeaderStyle: listHeaderStyle {FONT_WEIGHT: 400,LAYOUT_WEIGHT_LEFT: 30%,LAYOUT_WEIGHT_CENTER: 50%,LAYOUT_WEIGHT_RIGHT: 20%,
};class itemStyle {TEXT_LAYOUT_SIZE: number 24; // 文本的行高CIRCLE_TEXT_BORDER_RADIUS: number 24; // 圆形文本的边框半径CIRCLE_TEXT_SIZE: number 24; // 圆圈文本的大小CIRCLE_TEXT_COLOR_STOP_1: number 0.5; // 渐变色比例1CIRCLE_TEXT_COLOR_STOP_2: number 1.0; // 渐变色比例2BAR_HEIGHT: number 48; // item高度LAYOUT_WEIGHT_LEFT: string 30%; // 左边的布局权重LAYOUT_WEIGHT_CENTER: string 50%; // 中间的布局权重LAYOUT_WEIGHT_RIGHT: string 20%; // 右边的布局权重BORDER_WIDTH: number 1; // 边框宽度COLOR_BLUE: Resource $r(app.color.item_color); // 文字蓝色COLOR_BLACK: Resource $r(app.color.item_color_black); // 文字黑色
}/*** 列表Item样式*/
export const ItemStyle: itemStyle {TEXT_LAYOUT_SIZE: 24,CIRCLE_TEXT_BORDER_RADIUS: 24,CIRCLE_TEXT_SIZE: 24,CIRCLE_TEXT_COLOR_STOP_1: 0.5,CIRCLE_TEXT_COLOR_STOP_2: 1.0,BAR_HEIGHT: 48,LAYOUT_WEIGHT_LEFT: 30%,LAYOUT_WEIGHT_CENTER: 50%,LAYOUT_WEIGHT_RIGHT: 20%,BORDER_WIDTH: 1,COLOR_BLUE: $r(app.color.item_color),COLOR_BLACK: $r(app.color.item_color_black)
};class titleBarStyle {IMAGE_BACK_SIZE: number 21; // 后退按钮的图像大小IMAGE_BACK_MARGIN_RIGHT: number 18; // 后退按钮的右边距IMAGE_LOADING_SIZE: number 22; // 刷新按钮的图像大小BAR_HEIGHT: number 47; // 标题栏的高度BAR_MARGIN_HORIZONTAL: number 26; // 标题组件的水平边距BAR_MARGIN_TOP: number 10; // 标题组件的上边距WEIGHT: string 50%; // 行布局的权重
}/*** 标题栏样式*/
export const TitleBarStyle: titleBarStyle {IMAGE_BACK_SIZE: 21,IMAGE_BACK_MARGIN_RIGHT: 18,IMAGE_LOADING_SIZE: 22,BAR_HEIGHT: 47,BAR_MARGIN_HORIZONTAL: 26,BAR_MARGIN_TOP: 10,WEIGHT: 50%,
};这里的代码乍一看很多不好理解其实我们分析一下就知道是写什么属性首先我们定义了页面字体大小和粗细的枚举类型用于设置标题文字和其他文字然后就是页面的权重、退出App的提示时间等、接着就是定义页面样式、标题栏样式、列表头样式、列表Item样式通过注释你可以你知道每一个样式是什么意思熟能生巧你现在觉得不适应是因为不熟悉的缘故。
④ 组件 在ArkTS中组件是一个比较重要的知识点组件也分为三个类型基础组件、容器组件和自定义组件。
基础组件比如Text、Button、Image、TextInput等。容器组件比如Column、Row、Stack、List等。自定义组件则是根据实际的功能需求由开发者自己组合使用基础组件和容器组件变成新的功能组件。比如页面的标题栏左侧是返回按钮中间是标题文字可能还会有副标题右侧是功能按钮这种就是自定义组件。
⑤ 标题组件 下面我们来自定义一个组件做一个标题栏组件效果如下图所示 首先我们在ets目录下新建一个view文件夹该目录下新建一个TitleComponent.ets文件代码如下
/*** 自定义页面标题组件*/
import AppContext from ohos.app.ability.common
import { FontSize, TitleBarStyle, WEIGHT } from ../constants/ConstantsComponent
export struct TitleComponent {Link isRefreshData: boolean //是否刷新数据State title: Resource $r(app.string.title_default)build() {Row() {Row() {//返回图标Image($r(app.media.ic_public_back)).height(TitleBarStyle.IMAGE_BACK_SIZE).width(TitleBarStyle.IMAGE_BACK_SIZE).margin({ right: TitleBarStyle.IMAGE_BACK_MARGIN_RIGHT }).onClick(() {let handler getContext(this) as AppContext.UIAbilityContexthandler.terminateSelf() //杀死程序})//标题文字Text(this.title).fontSize(FontSize.LARGE)}.width(TitleBarStyle.WEIGHT).height(WEIGHT).justifyContent(FlexAlign.Start) //内容左对齐Row() {//刷新图标Image($r(app.media.loading)).height(TitleBarStyle.IMAGE_LOADING_SIZE).width(TitleBarStyle.IMAGE_LOADING_SIZE).onClick(() {this.isRefreshData !this.isRefreshData //修改刷新状态})}.width(TitleBarStyle.WEIGHT).height(WEIGHT).justifyContent(FlexAlign.End) //内容右对齐}.width(WEIGHT).padding({ left: TitleBarStyle.BAR_MARGIN_HORIZONTAL,right: TitleBarStyle.BAR_MARGIN_HORIZONTAL }).margin({ top: TitleBarStyle.BAR_MARGIN_TOP }).height(TitleBarStyle.BAR_HEIGHT).justifyContent(FlexAlign.SpaceAround) // 占满剩余空间}
}下面我们来分析一下这些代码首先我们导入一些需要用到的样式和App上下文因为点击返回键需要退出App然后就是通过Component装饰的struct表示TitleComponent结构体具有组件化能力能够成为一个独立的组件。 然后我们使用到了Link 修饰isRefreshData作为刷新数据的标识但是在标题组件中并没有对此变量进行初始化需要父组件在创建标题组件时对isRefreshData进行赋值在DevEco Studio中如果你对一个修饰符或者一个API不了解你可以将鼠标放在上面例如将鼠标放在Link上面会出现一个弹窗。 我们点击Show in API Reference编辑器右侧就会出现API的说明。 这个功能还是很Nice的好了我们接着来看isRefreshData变量在点击刷新图标时会进行更改通过Link装饰的变量可以和父组件的State变量建立双向数据绑定就会将对应该的值传递到父组件父组件会更新UI更新UI的时候根据状态切换渲染的数据源。同时定义了一个title其实我们可以简单的来看你就把isRefreshDatatitle当成标题组件的两个参数父组件要使用子组件则必须要传两个值进来。自定义组件必须定义build()方法在其中进行UI描述。 接下来就是一个Row表示横向布局Row里面放了两个Row第一个左对齐装载返回图标和标题第二个Row放刷新图标标题组件就介绍完了下面我们可以将它装载的父组件中使用了修改Index.ets中的代码如下所示
import { TITLE, WEIGHT } from ../constants/Constants;
import { TitleComponent } from ../view/TitleComponent;Entry
Component
struct Index {// 是否切换RankList的数据State isSwitchDataSource: boolean truebuild() {Column() {TitleComponent({ isRefreshData: $isSwitchDataSource, title: TITLE })}.backgroundColor($r(app.color.background)).height(WEIGHT).width(WEIGHT)}
}这里我们就是在Index父组件中进行使用标题组件通过 $ 操作符来创建引用使子组件中isRefreshData和父组件中的isSwitchDataSource建立双向数据绑定当isRefreshData值变化时父组件Index中的isSwitchDataSource值也会随着改变修改代码之后保存一下然后可以看到预览页面发生了变化 ⑥ 列表头组件
下面我们来写列表头组件在view包下新建一个ListHeaderComponent.ets文件里面的代码如下所示
/*** 列表头自定义组件*/
import { FontSize, ListHeaderStyle } from ../constants/Constants
Component
export struct ListHeaderComponent {paddingValue: Padding | Length 0widthValue: Length 0build() {Row() {Text($r(app.string.page_number)).fontSize(FontSize.SMALL).width(ListHeaderStyle.LAYOUT_WEIGHT_LEFT).fontWeight(ListHeaderStyle.FONT_WEIGHT).fontColor($r(app.color.font_description))Text($r(app.string.page_type)).fontSize(FontSize.SMALL).width(ListHeaderStyle.LAYOUT_WEIGHT_CENTER).fontWeight(ListHeaderStyle.FONT_WEIGHT).fontColor($r(app.color.font_description))Text($r(app.string.page_vote)).fontSize(FontSize.SMALL).width(ListHeaderStyle.LAYOUT_WEIGHT_RIGHT).fontWeight(ListHeaderStyle.FONT_WEIGHT).fontColor($r(app.color.font_description))}.width(this.widthValue).padding(this.paddingValue)}
}这里的代码就相对来说简单很多了就是三个文字描述就没有什么好说的下面我们直接在Index.ets中使用
import { Style, TITLE, WEIGHT } from ../constants/Constants;
import { ListHeaderComponent } from ../view/ListHeaderComponent;
import { TitleComponent } from ../view/TitleComponent;Entry
Component
struct Index {// 是否切换RankList的数据State isSwitchDataSource: boolean truebuild() {Column() {//标题栏TitleComponent({ isRefreshData: $isSwitchDataSource, title: TITLE })//列表头ListHeaderComponent({paddingValue: {left: Style.RANK_PADDING,right: Style.RANK_PADDING},widthValue: Style.CONTENT_WIDTH}).margin({top: Style.HEADER_MARGIN_TOP,bottom: Style.HEADER_MARGIN_BOTTOM})}.backgroundColor($r(app.color.background)).height(WEIGHT).width(WEIGHT)}
}然后保存一下再看预览效果 ⑦ 列表Item组件
最后我们来看列表item组件在view包下新建一个ListItemComponent.ets文件代码如下所示
import { FontSize, FontWeight, ItemStyle, WEIGHT } from ../constants/Constants;
/*** 列表Item组件*/
Component
export struct ListItemComponent {index: number;name: Resource;vote: string;// 是否切换数据源isSwitchDataSource: boolean false;// 是否改变文字选中文字颜色State isChange: boolean false;build() {Row() {//排名Column() {if (this.isRenderCircleText()) {//渲染if (this.index ! undefined) {this.CircleText(this.index);}} else {//不渲染Text(this.index?.toString()).lineHeight(ItemStyle.TEXT_LAYOUT_SIZE).textAlign(TextAlign.Center).width(ItemStyle.TEXT_LAYOUT_SIZE).fontWeight(FontWeight.BOLD).fontSize(FontSize.SMALL)}}.width(ItemStyle.LAYOUT_WEIGHT_LEFT).alignItems(HorizontalAlign.Start)//种类Text(this.name).width(ItemStyle.LAYOUT_WEIGHT_CENTER).fontWeight(FontWeight.BOLDER).fontSize(FontSize.MIDDLE).fontColor(this.isChange ? ItemStyle.COLOR_BLUE : ItemStyle.COLOR_BLACK) //根据选中状态修改文字颜色//得票数Text(this.vote).width(ItemStyle.LAYOUT_WEIGHT_RIGHT).fontWeight(FontWeight.BOLD).fontSize(FontSize.SMALL).fontColor(this.isChange ? ItemStyle.COLOR_BLUE : ItemStyle.COLOR_BLACK) //根据选中状态修改文字颜色}.height(ItemStyle.BAR_HEIGHT).width(WEIGHT).onClick(() { //item 点击事件this.isChange !this.isChange;})}/*** 圆形背景文字* param index*/Builder CircleText(index: number) {Row() {Text(index.toString()).fontWeight(FontWeight.BOLD).fontSize(FontSize.SMALL).fontColor(Color.White);}.justifyContent(FlexAlign.Center).borderRadius(ItemStyle.CIRCLE_TEXT_BORDER_RADIUS).size({ width: ItemStyle.CIRCLE_TEXT_SIZE,height: ItemStyle.CIRCLE_TEXT_SIZE }).backgroundColor($r(app.color.circle_text_background))}/*** 是否渲染圆圈文本* returns*/isRenderCircleText(): boolean {// 列表中第三个元素的渲染圆圈文本return this.index 1 || this.index 2 || this.index 3;}
}这个列表Item组件里面的代码比较多我们来分析一下首先导入的样式就没有什么好说的然后我们看ListItemComponent组件里面定义的5个参数前三个是Item显示的内容而isChange是用来控制item中种类和得票数点击效果的然后看到build()方法里面首先是横向布局然后处理第一个数据排名因为我们希望前3个数据标注一下所以在ListItemComponent组件中写了一个isRenderCircleText()函数用于判断是否需要进行样式渲染这里你会看到这里index判断的是1、2和3但是下标是从0开始的因此在传index进来的时候index就是1的你不会看到那个排行榜从0开始然后就是写了一个CircleText()函数通过这个函数传递index进去创建一个圆形背景白色文字的样式UI。再往下走就是种类、得票数的渲染在设置fontColor(this.isChange ? ItemStyle.COLOR_BLUE : ItemStyle.COLOR_BLACK)中对isChange 进行判断从而设置不同的文字颜色最后就是当前item的点击事件在点击事件中更改isChange的值因为是State装饰的所以会触发UI更新从而修改文字颜色那么相信列表Item组件你都了解了下面我们回到Index父组件。
⑧ 组件生命周期 在父组件使用子组件之前我们再来了解一些关于组建的知识点通过Entry装饰的自定义组件用作页面的默认入口组件加载页面是将首先创建并呈现Entry装饰的自定义组件比如当前的Index一个页面有且仅能有一个Entry这一点很重要只有被Entry修饰的组件或者其子组件才会在页面上显示为什么要说这么多呢 这是因为Entry和Component所修饰的组件的生命周期有所不同。 通过Component所修饰组件生命周期如下图所示 这是自定义组件创建到销毁的过程在这个过程中系统提供了生命周期回调函数aboutToAppear()和aboutToDisappear()用于通知开发者该自定义组件所处的阶段aboutToAppear()在创建自定义组件实例后到执行起build()函数之前执行你可以在aboutToAppear()函数中对UI需要展示的数据进行初始化或者申请定时器资源等操作这样在后续build()函数中可以使用这些数据和资源来进行UI展示。可以在aboutToDisappear()函数中释放不再使用的资源避免资源泄露。 还需要注意一点由于这些回调函数是私有的系统会在特定的时间下自动调用是无法手动调用这些回调函数的。 通过Entry所修饰的页面入口组件生命周期如下图所示 可以看到相对于自定义组件页面入口组件多了onPageShow()、onBackPress()和onPageHide()三个生命周期函数当用户从手机桌面打开应用应用进入前台时页面显示触发onPageShow()函数当用户点击home键回到桌面时应用进入后台时页面消失触发onPageHide()函数而当通过系统方式执行返回操作时触发onBackPress()函数。这里提到了生命周期是因为下面我们需要用到生命周期。
⑨ 渲染列表数据
我们回到Index.ets然后修改一些代码修改后如下所示
import promptAction from ohos.promptAction;
import { APP_EXIT_INTERVAL, Style, TIME, TITLE, WEIGHT } from ../constants/Constants;
import { RankData } from ../model/RankData;
import { RankViewModel } from ../model/RankViewModel;
import { ListHeaderComponent } from ../view/ListHeaderComponent;
import { ListItemComponent } from ../view/ListItemComponent;
import { TitleComponent } from ../view/TitleComponent;let rankModel: RankViewModel new RankViewModel()Entry
Component
struct Index {State dataSource1: RankData[] []State dataSource2: RankData[] []// 是否切换RankList的数据State isSwitchDataSource: boolean true// 记录点击系统导航返回按钮的时间private clickBackTimeRecord: number 0;/*** 是否显示Toast* returns*/isShowToast(): boolean {return new Date().getTime() - this.clickBackTimeRecord APP_EXIT_INTERVAL}/*** 页面显示回调 - 生命周期*/aboutToAppear() {this.dataSource1 rankModel.loadRankDataSource1()this.dataSource2 rankModel.loadRankDataSource2()}/*** 页面返回回调* returns*/onBackPress() {if (this.isShowToast()) {promptAction.showToast({message: $r(app.string.prompt_text), duration: TIME})this.clickBackTimeRecord new Date().getTime();return false}return false}build() {Column() {//标题栏TitleComponent({ isRefreshData: $isSwitchDataSource, title: TITLE })//列表头ListHeaderComponent({paddingValue: {left: Style.RANK_PADDING,right: Style.RANK_PADDING},widthValue: Style.CONTENT_WIDTH}).margin({top: Style.HEADER_MARGIN_TOP,bottom: Style.HEADER_MARGIN_BOTTOM})//列表this.RankList(Style.CONTENT_WIDTH)}.backgroundColor($r(app.color.background)).height(WEIGHT).width(WEIGHT)}/*** 配置列表* param widthValue*/Builder RankList(widthValue: Length) {Column() {List() {ForEach(this.isSwitchDataSource ? this.dataSource1 : this.dataSource2,(item: RankData, index?: number) {ListItem() {// 加载ItemListItemComponent({ index: (Number(index) 1), name: item.name, vote: item.vote })}}, (item: RankData) JSON.stringify(item))}.width(WEIGHT).height(Style.LIST_HEIGHT).divider({ strokeWidth: Style.STROKE_WIDTH})}.padding({left: Style.RANK_PADDING,right: Style.RANK_PADDING}).borderRadius(Style.BORDER_RADIUS).width(widthValue).alignItems(HorizontalAlign.Center).backgroundColor(Color.White)}
}下面我们进行解析首先是初始化一个rankModel这里我们前面写好的一个类用于提供数据源然后在Index中创建两个数组在回调函数aboutToAppear()中进行初始化然后在onBackPress()回调函数中处理是否需要显示退出应用时的Toastreturn false表示系统处理返回事件return true表示用户自己处理。接下来最重要的就是我们在Index中增加了RankList()函数函数中就是通过List()组件装载ListItem()ForEach遍历当前的数据源再通过调用ListItemComponent()组件构建每一个列表Item注意这里index 1所以012就变成了123列表就写好了。最后在build()函数中调用RankList()函数即可完成整个页面功能。下面我们运行一下看看效果。 ⑩ 单选 在上面的处理中我们是通过改变Item的状态来达到选中之后的文字颜色改变当选了其他的Item之后之前的Item并没有什么变化那么如果我想做单选的效果呢 从UI上来看单选我们首先要记录一个选中位置然后在点击Item的时候更新选中位置修改文字颜色同时要更新整个列表更新列表的时候自然也会更新Item那么这里就需要使用到Link来装饰选中位置下面我们修改一下列表Item组件中的代码 首先增加一个属性然后根据值匹配当前Item的Index来设置文字颜色并在点击Item的时候对选中位置重新赋值。 然后回到Index这里我们增加一个selectedIndex 再构建Item时将这个值传进去 这样就实现了单选功能我就不贴动图了因为没有真机这个动图制作起来太麻烦了你保存一下在预览效果中也可以测试出来。
三、源码
如果对你有所帮助的话不妨 Star 或 Fork山高水长后会有期~
源码地址MyApplication