HarmonyOS Next 实战卡片开发 03

简介: 本文详细介绍了基于 HarmonyOS Next 的卡片开发实战,涵盖从项目创建到功能实现的全流程。首先通过新建项目和服务卡片搭建基础框架,并设置沉浸式体验优化界面。接着实现了首页轮播图功能,包括申请网络权限、初始化数据和构建轮播组件。随后深入讲解了卡片 id 的处理,涉及获取、返回、持久化存储及移除操作,确保卡片与应用间的高效通信。此外,封装了下载图片工具类,支持卡片发起通知获取网络图片,增强功能扩展性。最后实现了卡片同步轮播功能,使首页与卡片轮播状态保持一致。整个流程注重细节,结合实际案例,为开发者提供了全面的参考。

HarmonyOS Next 实战卡片开发 03

在前面两张,我们基本掌握了卡片的使用流程,本章节就通过一个实战来加强对卡片使用的理解。

要完成的案例

image-20241024175411585


PixPin_2024-10-24_17-54-18

新建项目和新建服务卡片

image-20241031111045865


image-20241031111130741

设置沉浸式

entry/src/main/ets/entryability/EntryAbility.ets

image-20241031111326923

首页显示轮播图数据

PixPin_2024-10-31_11-27-05

1. 申请网络权限

entry/src/main/module.json5

image-20241031112238553

2. 新建工具文件 /utils/index.ets

entry/src/main/ets/utils/index.ets

export const swiperInit = () => {
   
  AppStorage.setOrCreate("swiperList", [
    "https://env-00jxhf99mujs.normal.cloudstatic.cn/card/1.webp?expire_at=1729734506&er_sign=e51cb3b4f4b28cb2da96fd53701eaa69",
    "https://env-00jxhf99mujs.normal.cloudstatic.cn/card/2.webp?expire_at=1729734857&er_sign=b2ffd42585568a094b9ecfb7995a9763",
    "https://env-00jxhf99mujs.normal.cloudstatic.cn/card/3.webp?expire_at=1729734870&er_sign=50d5f210191c113782958dfd6681cd2d",
  ]);
  AppStorage.setOrCreate("activeIndex", 0);
};
AI 代码解读

3. 初始化

entry/src/main/ets/entryability/EntryAbility.ets

image-20241031111803516

4. 页面中使用

entry/src/main/ets/pages/Index.ets


@Entry
@Component
struct Index {
   
  @StorageProp("swiperList")
  swiperList: string[] = []
  @StorageLink("activeIndex")
  activeIndex: number = 0


  build() {
   
    Column() {
   
      Swiper() {
   
        ForEach(this.swiperList, (img: string) => {
   
          Image(img)
            .width("80%")
        })
      }
      .loop(true)
      .autoPlay(true)
      .interval(3000)

      .onChange(index => this.activeIndex = index)
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
    .backgroundImage(this.swiperList[this.activeIndex])
    .backgroundBlurStyle(BlurStyle.Thin)
    .backgroundImageSize(ImageSize.Cover)
    .animation({
    duration: 500 })
  }
}
AI 代码解读

5. 效果

PixPin_2024-10-31_11-27-05

创建卡片时,获取卡片 id

PixPin_2024-10-31_13-06-50

image-20241031222230532

1. 获取和返回卡片 id

这里解析下为什么要返回 id 给卡片组件,因为后期卡片想要向应用通信时,应用响应数据要根据卡片 id 来响应。

另外 formExtensionAbility 进程不能常驻后台,即在卡片生命周期回调函数中无法处理长时间的任务,在生命周期调度完成后会继续存在 10 秒,如 10 秒内没有新的

生命周期回调触发则进程自动退出。针对可能需要 10 秒以上才能完成的业务逻辑,建议拉起主应用进行处理,处理完成后使用updateForm通知卡片进行刷新

entry/src/main/ets/entryformability/EntryFormAbility.ets

  onAddForm(want: Want) {
   
    class FormData {
   
      // 获取卡片id
      formId: string = want.parameters!['ohos.extra.param.key.form_identity'].toString();
    }

    let formData = new FormData()
    return formBindingData.createFormBindingData(formData);
  }
AI 代码解读

2. 接受和显示卡片 id

entry/src/main/ets/widget/pages/WidgetCard.ets

const localStorage = new LocalStorage()

@Entry(localStorage)
@Component
struct WidgetCard {
   
  @LocalStorageProp("formId")
  formId: string = ""

  build() {
   
    Row() {
   
      Text(this.formId)
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
    .padding(10)

  }
}
AI 代码解读

3. 效果

PixPin_2024-10-31_13-06-50

记录卡片 id,持久化存储

image-20241031223836426

主要流程如下:

  1. 封装持久化存储卡片 id 的工具类
  2. 初始化卡片 id 工具类
  3. 卡片主动上传卡片 id
  4. 应用 Aibility 接收卡片 id
  5. 接收卡片 id 并且持久化
  6. 移除卡片时,删除卡片 id

1. 封装持久化存储卡片 id 的工具类

此时接收到卡片 id 后,需要将卡片 id 持久化存储,避免重新打卡手机时,无法联系到已经创建的卡片

entry/src/main/ets/utils/index.ets

export class FormIdStore {
   
  static key: string = "wsy_collect";
  static dataPreferences: preferences.Preferences | null = null;
  static context: Context | null = null;

  //  初始化
  static init(context?: Context) {
   
    if (!FormIdStore.dataPreferences) {
   
      if (context) {
   
        FormIdStore.context = context;
      }
      FormIdStore.dataPreferences = preferences.getPreferencesSync(
        FormIdStore.context || getContext(),
        {
    name: FormIdStore.key }
      );
    }
  }

  //  获取卡片id 数组
  static getList() {
   
    FormIdStore.init();
    const str = FormIdStore.dataPreferences?.getSync(FormIdStore.key, "[]");
    const list = JSON.parse(str as string) as string[];
    console.log("list卡片", list);
    return list;
  }

  // 新增卡片数组
  static async set(item: string) {
   
    FormIdStore.init();
    const list = FormIdStore.getList();
    if (!list.includes(item)) {
   
      list.push(item);
      FormIdStore.dataPreferences?.putSync(
        FormIdStore.key,
        JSON.stringify(list)
      );
      await FormIdStore.dataPreferences?.flush();
    }
  }

  // 删除元素
  static async remove(item: string) {
   
    FormIdStore.init();
    const list = FormIdStore.getList();
    const index = list.indexOf(item);
    if (index !== -1) {
   
      list.splice(index, 1);
      FormIdStore.dataPreferences?.putSync(
        FormIdStore.key,
        JSON.stringify(list)
      );
      await FormIdStore.dataPreferences?.flush();
    }
  }
}
AI 代码解读

2. 初始化卡片 id 工具类

  1. onCreate 中初始化

    entry/src/main/ets/entryability/EntryAbility.ets

      onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
         
        FormIdStore.init(this.context)
    
    AI 代码解读
  2. onAddForm 中初始化

    onAddForm(want: Want) {
          
      FormIdStore.init(this.context)
    
    AI 代码解读

3. 卡片主动上传卡片 id

利用 watch 监听器来触发上传

entry/src/main/ets/widget/pages/WidgetCard.ets

const localStorage = new LocalStorage()

@Entry(localStorage)
@Component
struct WidgetCard {
   
  @LocalStorageProp("formId")
  @Watch("postData")
  formId: string = ""

  // 上传卡片id
  postData() {
   
    postCardAction(this, {
   
      action: 'call',
      abilityName: 'EntryAbility',
      params: {
   
        method: 'createCard',
        formId: this.formId
      }
    });
  }

  build() {
   
    Row() {
   
      Text(this.formId)
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
    .padding(10)

  }
}
AI 代码解读

4. 应用 Aibility 接收卡片 id

entry/src/main/ets/entryability/EntryAbility.ets

// callee中要求返回的数据类型
class MyPara implements rpc.Parcelable {
   
  marshalling(dataOut: rpc.MessageSequence): boolean {
   
    return true
  }

  unmarshalling(dataIn: rpc.MessageSequence): boolean {
   
    return true
  }
}

onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
   
    FormIdStore.init(this.context)
    // 监听事件
    this.callee.on("createCard", (data: rpc.MessageSequence) => {
   
      // 接收id
      const formId = (JSON.parse(data.readString() as string) as Record<string, string>).formId

      return new MyPara()
    })
  }
AI 代码解读

5. 接收卡片 id 并且持久化

  1. 开启后台运行权限 "ohos.permission.KEEP_BACKGROUND_RUNNING"

    entry/src/main/module.json5

        "requestPermissions": [
          {
         
            "name": "ohos.permission.INTERNET"
          },
          {
         
            "name": "ohos.permission.KEEP_BACKGROUND_RUNNING"
          }
        ],
    
    AI 代码解读
  2. 持久化

     onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
         
        FormIdStore.init(this.context)
        // 监听事件
        this.callee.on("createCard", (data: rpc.MessageSequence) => {
         
          // 接收id
          const formId = (JSON.parse(data.readString() as string) as Record<string, string>).formId
          // 2 持久化
          FormIdStore.set(formId)
    
          return new MyPara()
        })
      }
    
    AI 代码解读

6. 移除卡片时,删除卡片 id

entry/src/main/ets/entryformability/EntryFormAbility.ets

  onRemoveForm(formId: string) {
    FormIdStore.remove(formId)
  }
AI 代码解读

封装下载图片工具类

将下载图片和拼接卡片需要格式的代码封装到文件中 该工具类可以同时下载多张图片,使用了 Promise.all 来统一接收结果

entry/src/main/ets/utils/CardDonwLoad.ets

1. 封装的工具说明

interface IDownFile {
   
  fileName: string;
  imageFd: number;
}

// 卡片显示 需要的数据结构
export class FormDataClass {
   
  // 卡片需要显示图片场景, 必填字段(formImages 不可缺省或改名), fileName 对应 fd
  formImages: Record<string, number>;

  constructor(formImages: Record<string, number>) {
   
    this.formImages = formImages;
  }
}

export class CardDownLoad {
   
  context: Context | null;
  then: Function | null = null;
  imgFds: number[] = [];

  constructor(context: Context) {
   
    this.context = context;
  }

  // 下载单张图片
  async downLoadImage(netFile: string) {
   }

  // 下载一组图片
  async downLoadImages(netFiles: string[]) {
   }

  // 私有下载网络图片的方法
  private async _down(netFile: string) {
   }

  // 手动关闭文件
  async closeFile() {
   
    this.imgFds.forEach((fd) => fileIo.closeSync(fd));
    this.imgFds = [];
  }
}
AI 代码解读

2. 封装的实现

import {
    http } from "@kit.NetworkKit";
import {
    fileIo } from "@kit.CoreFileKit";

interface IDownFile {
   
  fileName: string;
  imageFd: number;
}

// 卡片显示 需要的数据结构
export class FormDataClass {
   
  // 卡片需要显示图片场景, 必填字段(formImages 不可缺省或改名), fileName 对应 fd
  formImages: Record<string, number>;

  constructor(formImages: Record<string, number>) {
   
    this.formImages = formImages;
  }
}

export class CardDownLoad {
   
  context: Context | null;
  then: Function | null = null;
  imgFds: number[] = [];

  constructor(context: Context) {
   
    this.context = context;
  }

  // 下载单张图片
  async downLoadImage(netFile: string) {
   
    const obj = await this._down(netFile);
    let imgMap: Record<string, number> = {
   };
    imgMap[obj.fileName] = obj.imageFd;
    if (!this.imgFds.includes(obj.imageFd)) {
   
      this.imgFds.includes(obj.imageFd);
    }
    return new FormDataClass(imgMap);
  }

  // 下载一组图片
  async downLoadImages(netFiles: string[]) {
   
    let imgMap: Record<string, number> = {
   };

    const promiseAll = netFiles.map((url) => {
   
      const ret = this._down(url);
      return ret;
    });
    const resList = await Promise.all(promiseAll);
    resList.forEach((v) => {
   
      imgMap[v.fileName] = v.imageFd;
      if (!this.imgFds.includes(v.imageFd)) {
   
        this.imgFds.includes(v.imageFd);
      }
    });

    return new FormDataClass(imgMap);
    // return resList.map(v => `memory://${v.fileName}`)
  }

  // 私有下载网络图片的方法
  private async _down(netFile: string) {
   
    let tempDir = this.context!.getApplicationContext().tempDir;
    let fileName = "file" + Date.now();
    let tmpFile = tempDir + "/" + fileName;

    let httpRequest = http.createHttp();
    let data = await httpRequest.request(netFile);
    if (data?.responseCode == http.ResponseCode.OK) {
   
      let imgFile = fileIo.openSync(
        tmpFile,
        fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE
      );

      await fileIo.write(imgFile.fd, data.result as ArrayBuffer);

      const obj: IDownFile = {
   
        fileName,
        imageFd: imgFile.fd,
      };
      // setTimeout(() => {
   
      // }, 0)
      // fileIo.close(imgFile);
      httpRequest.destroy();
      return obj;
    } else {
   
      httpRequest.destroy();
      return Promise.reject(null);
    }
  }

  // 手动关闭文件
  async closeFile() {
   
    this.imgFds.forEach((fd) => fileIo.closeSync(fd));
    this.imgFds = [];
  }
}
AI 代码解读

卡片发起通知,获取网络图片

PixPin_2024-10-31_20-52-33

image-20241031224019379

  1. 准备好卡片代码,用来接收返回的网络图片数据
  2. 应用 Ability 接收卡片通知,下载网络图片,并且返回给卡片

1. 准备好卡片代码,用来接收返回的网络图片数据

const localStorage = new LocalStorage()

@Entry(localStorage)
@Component
struct WidgetCard {
   
  // 用来显示图片的数组
  @LocalStorageProp("imgNames")
  imgNames: string[] = []
  // 卡片id
  @LocalStorageProp("formId")
  @Watch("postData")
  formId: string = ""
  // 当前显示的大图 -  和 应用-首页保持同步
  @LocalStorageProp("activeIndex")
  activeIndex: number = 0

  postData() {
   
    postCardAction(this, {
   
      action: 'call',
      abilityName: 'EntryAbility',
      params: {
   
        method: 'createCard',
        formId: this.formId
      }
    });
  }

  build() {
   
    Row() {
   
      ForEach(this.imgNames, (url: string, index: number) => {
   
        Image(url)
          .border({
    width: 1 })
          .layoutWeight(this.activeIndex === index ? 2 : 1)
          .height(this.activeIndex === index ? "90%" : "60%")
          .borderRadius(this.activeIndex === index ? 12 : 5)
          .animation({
    duration: 300 })
      })
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
    .padding(10)
    .backgroundImage(this.imgNames[this.activeIndex])
    .backgroundBlurStyle(BlurStyle.Thin)
    .backgroundImageSize(ImageSize.Cover)
    .animation({
    duration: 300 })
  }
}
AI 代码解读

2. 应用 Ability 接收卡片通知,下载网络图片,并且返回给卡片

entry/src/main/ets/entryability/EntryAbility.ets

// callee中要求返回的数据类型

class MyPara implements rpc.Parcelable {
   
  marshalling(dataOut: rpc.MessageSequence): boolean {
   
    return true;
  }

  unmarshalling(dataIn: rpc.MessageSequence): boolean {
   
    return true;
  }
}

export default class EntryAbility extends UIAbility {
   
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
   
    // 监听事件
    this.callee.on("createCard", (data: rpc.MessageSequence) => {
   
      // 接收id
      const formId = (
        JSON.parse(data.readString() as string) as Record<string, string>
      ).formId;
      // 持久化
      FormIdStore.set(formId);

      class FormData {
   
        imgName?: string[] = [];
        activeIndex?: number = AppStorage.get("activeIndex")!;
      }

      const formInfo = formBindingData.createFormBindingData(new FormData());
      // 先响应空数据 等待网络图片下载完毕后,再响应网络图片数据
      formProvider.updateForm(formId, formInfo);
      const cardDownLoad = new CardDownLoad(this.context);
      cardDownLoad
        .downLoadImages(AppStorage.get("swiperList") as string[])
        .then((ret) => {
   
          const urls = Object.keys(ret.formImages).map((v) => `memory://${
     v}`);
          // 返回卡片数组
          class CimgNames {
   
            imgNames: string[] = urls;
            formImages: Record<string, number> = ret.formImages;
          }

          const formInfo = formBindingData.createFormBindingData(
            new CimgNames()
          );
          formProvider.updateForm(formId, formInfo);
          //   关闭文件
          cardDownLoad.closeFile();
        });

      // 临时处理、防止报错
      return new MyPara();
    });
  }
}
AI 代码解读

3. 效果

PixPin_2024-10-31_20-52-33

卡片同步轮播

image-20241031224212664

该功能主要是首页在图片轮播时,通知所有的卡片同时更新

entry/src/main/ets/pages/Index.ets

1. 监听轮播图 onChange 事件,设置当前显示的下标

      Swiper() {
   
        ForEach(this.swiperList, (img: string) => {
   
          Image(img)
            .width("80%")
        })
      }
      .loop(true)
      .autoPlay(true)
      .interval(3000)
      .onChange(index => this.activeIndex = index)
AI 代码解读

2. 监听下标的改变,通知持久化存储中所有的卡片进行更新

  @StorageLink("activeIndex")
  @Watch("changeIndex")
  activeIndex: number = 0

  // 通知所有卡片一并更新
  changeIndex() {
   
    const list = FormIdStore.getList()
    const index = this.activeIndex
    list.forEach(id => {
   
      class FdCls {
   
        activeIndex: number = index
      }

      const formInfo = formBindingData.createFormBindingData(new FdCls())
      formProvider.updateForm(id, formInfo)
    })
  }
AI 代码解读

3. 效果

PixPin_2024-10-31_22-18-40

总结

FormExtensionAbility 进程不能常驻后台,即在卡片生命周期回调函数中无法处理长时间的任务,在生命周期调度完成后会继续存在 10 秒,如 10 秒内没有新的

生命周期回调触发则进程自动退出。针对可能需要 10 秒以上才能完成的业务逻辑,建议拉起主应用进行处理,处理完成后使用updateForm通知卡片进行刷

新。

1. 项目开发流程

  1. 新建项目与服务卡片:创建新的项目和服务卡片,为后续开发搭建基础框架。
  2. 设置沉浸式体验:在EntryAbility.ets中进行相关设置,优化用户视觉体验。

2. 首页轮播图数据显示

  1. 申请网络权限:在module.json5中申请,为数据获取做准备。
  2. 新建工具文件:在/utils/index.ets中创建swiperInit函数,用于初始化轮播图数据,包括设置轮播图列表和初始索引。
  3. 初始化操作:在EntryAbility.ets中进行初始化。
  4. 页面使用:在Index.ets中构建轮播图组件,通过SwiperForEach等实现轮播效果,轮播图可自动播放、循环,并能响应索引变化。

3. 卡片 id 的处理

  1. 获取与返回卡片 id:在EntryFormAbility.etsonAddForm函数中获取卡片 id,并返回给卡片组件。原因是后期卡片向应用通信时,应用需根据卡片 id 响应,同时注意formExtensionAbility进程的后台限制。
  2. 接受与显示卡片 id:在WidgetCard.ets中接受并显示卡片 id。
  3. 卡片 id 的持久化存储
    • 封装工具类:在/utils/index.ets中封装FormIdStore类,实现初始化、获取卡片 id 列表、新增和删除卡片 id 等功能。
    • 初始化工具类:在EntryAbility.etsonCreateonAddForm中初始化。
    • 卡片主动上传:在WidgetCard.ets中利用watch监听器触发上传卡片 id。
    • 应用接收与持久化:在EntryAbility.ets中接收卡片 id 并持久化,同时需开启后台运行权限。
    • 移除卡片时处理:在EntryFormAbility.etsonRemoveForm中删除卡片 id。

4. 图片相关操作

  1. 封装下载图片工具类:在CardDonwLoad.ets中封装,包括下载单张或一组图片的功能,以及手动关闭文件功能,涉及网络请求和文件操作。
  2. 卡片发起通知获取网络图片
    • 卡片准备接收数据:在WidgetCard.ets中准备接收网络图片数据的代码,包括显示图片数组、卡片 id 等相关变量和操作。
    • 应用处理与返回数据:在EntryAbility.ets中接收卡片通知,下载网络图片并返回给卡片,先响应空数据,下载完成后再更新卡片数据。

5. 卡片同步轮播功能

  1. 监听轮播图 onChange 事件:在Index.ets中通过Swiper组件的onChange事件设置当前显示下标。
  2. 通知卡片更新:在Index.ets中监听下标改变,通知持久化存储中的所有卡片更新,实现首页与卡片轮播同步。
目录
打赏
0
20
20
1
133
分享
相关文章
【04】优雅草星云物联网AI智控系统从0开发鸿蒙端适配-deveco studio-自定义一个设置输入小部件组件-完成所有设置setting相关的页面-优雅草卓伊凡
【04】优雅草星云物联网AI智控系统从0开发鸿蒙端适配-deveco studio-自定义一个设置输入小部件组件-完成所有设置setting相关的页面-优雅草卓伊凡
151 92
【03】优雅草星云物联网AI智控系统从0开发鸿蒙端适配-deveco studio-在lib目录新建自定义库UtilsLibrary,ComponentLibrary,CommonConstLibrary完成设置SettingsView.ets初始公共类书写-优雅草卓伊凡
【03】优雅草星云物联网AI智控系统从0开发鸿蒙端适配-deveco studio-在lib目录新建自定义库UtilsLibrary,ComponentLibrary,CommonConstLibrary完成设置SettingsView.ets初始公共类书写-优雅草卓伊凡
53 23
【03】优雅草星云物联网AI智控系统从0开发鸿蒙端适配-deveco studio-在lib目录新建自定义库UtilsLibrary,ComponentLibrary,CommonConstLibrary完成设置SettingsView.ets初始公共类书写-优雅草卓伊凡
【02】优雅草星云物联网AI智控系统从0开发鸿蒙端适配-deveco studio-登录页面LoginView.ets完成-并且详细解释关于arkui关于 CommonConst, commonColor, InputDataModel-优雅草卓伊凡
【02】优雅草星云物联网AI智控系统从0开发鸿蒙端适配-deveco studio-登录页面LoginView.ets完成-并且详细解释关于arkui关于 CommonConst, commonColor, InputDataModel-优雅草卓伊凡
45 14
【02】优雅草星云物联网AI智控系统从0开发鸿蒙端适配-deveco studio-登录页面LoginView.ets完成-并且详细解释关于arkui关于 CommonConst, commonColor, InputDataModel-优雅草卓伊凡
鸿蒙开发难题多到崩溃?然而 10 亿终端暗藏财富密码-卓伊凡
鸿蒙开发难题多到崩溃?然而 10 亿终端暗藏财富密码-卓伊凡
41 5
鸿蒙开发难题多到崩溃?然而 10 亿终端暗藏财富密码-卓伊凡
|
5天前
|
鸿蒙NEXT如何保证应用安全:详解鸿蒙NEXT数字签名和证书机制
本文对鸿蒙NEXT公开资料进行了深入分析和解读,梳理了鸿蒙单框架应用的签名机制,拆解每一步的实操过程和背后的实现原理,并对源码分析整理签名的校验机制。从中管中窥豹,探究鸿蒙系统的安全设计思路,给从事鸿蒙研发的同学提供一些借鉴。
50 3
鸿蒙NEXT开发App相关工具类(ArkTs)
这段代码展示了一个名为鸿蒙NEXT开发 `AppUtil` 的工具类,主要用于管理鸿蒙应用的上下文、窗口、状态栏、导航栏等配置。它提供了多种功能,例如设置灰阶模式、颜色模式、字体类型、屏幕亮度、窗口属性等,并支持获取应用包信息(如版本号、包名等)。该工具类需在 UIAbility 的 `onWindowStageCreate` 方法中初始化,以便缓存全局变量。代码由鸿蒙布道师编写,适用于鸿蒙系统应用开发,帮助开发者更便捷地管理和配置应用界面及系统属性。
|
17天前
鸿蒙开发:如何实现文本跑马灯效果
如果只是一个普通的跑马灯效果,而且Text文本组件中的TextOverflow.MARQUEE可以满足需求,以Text为主,如果你想控制文本的速度,暂停等功能,可以使用Marquee,如果你想实现复杂的场景滚动,比如图片,各种组件嵌套滚动,这种只能自己定义了。
鸿蒙开发:如何实现文本跑马灯效果
鸿蒙开发:使用Rect绘制矩形
几何矩形,在实际的开发中,有多种的实现方式,并非一定需要Rect组件,但是,如果有需要用到矩形的场景,建议还是使用Rect组件,因为Rect组件自身携带了很多样式属性,可以满足我们日常的不同的需求。
鸿蒙开发:使用Rect绘制矩形
鸿蒙开发:动态添加节点
流程就是,通过typeNode来创建自己的组件,然后使用追加到FrameNode节点中,然后将自定义节点挂载到NodeContainer上即可,主要使用场景,需要动态创建组件的场景。
鸿蒙开发:动态添加节点
HarmonyOS Next 简单上手元服务开发
本文介绍了 HarmonyOS Next 中元服务的开发流程与关键特性。元服务是一种轻量级应用程序形态,支持免安装、秒开直达,适用于听音乐、打车等场景,大幅提升服务获取效率。文章详细讲解了元服务的开发旅程,包括在 AGC 平台上新建项目、修改名称与图标、新增卡片等内容,并提供了代码示例,如 AtomicServiceTabs 的 tab 切换和标题设置、AtomicServiceNavigation 的路由管理等。此外,还探讨了 AtomicServiceWeb 的使用方法,涵盖鸿蒙页面与 h5 页面的数据传递及方法调用。
87 20
HarmonyOS Next 简单上手元服务开发

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等