【HarmonyOS】项目工程化工具 持续更迭...

在 HarmonyOS 开发中,工具类的封装可以极大提升开发效率和代码可维护性。本文将详细讲解我封装的一套工具类,涵盖用户认证、沉浸式全屏、日志打印、网络请求、截图保存、状态栏控制、轻提示等常用功能,并提供完整的使用示例。

FullScreen------沉浸式全屏设置

功能说明:

  • 开启/关闭沉浸式全屏(穿透手机顶底栏)
  • 获取状态栏和导航栏高度,用于页面布局适配

FullScreen封装的工具类中需要获取上下文this

  1. EntryAbilityonCreate生命周期钩子函数中:
typescript 复制代码
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  ...
   // 通过应用状态管理对上下文进行状态创建设置
   AppStorage.setOrCreate('context', this.context)
}
  1. FullScreen工具类中进行获取使用
typescript 复制代码
class FullScreen {
  //开启沉浸式全屏
  async enable() {
    try {
      // 获取当前窗口Context对象
      const ctx = AppStorage.get<Context>(CONTEXT)
      ...
    }
  }
  //关闭沉浸式全屏
  async disable() {
    try {
      // 获取当前窗口Context对象
      const ctx = AppStorage.get<Context>(CONTEXT)
      ...
    }
  }
}

工具类方法:

typescript 复制代码
/**
 * description: [沉侵式系统:穿透手机顶底栏]
 * 1.通过内置window对象获取当前窗口对象
 * 2.调用window对象setWindowLayoutFullScreen方法开启沉浸式全屏
 * 3.通过window对象getWindowAvoidArea方法获取状态栏高度和导航条高度
 * 4.将状态栏高度和导航条高度存储在AppStorage中
 * */

import { CONTEXT, NAVIGATION_BAR_HEIGHT, STATUS_BAR_HEIGHT } from '../constants'
import { window } from '@kit.ArkUI';
import { logger } from './Logger';

class FullScreen {
  //开启沉浸式全屏
  async enable() {
    try {
      // 获取当前窗口Context对象
      const ctx = AppStorage.get<Context>(CONTEXT)
      if (ctx) {
        // 获取窗口对象
        const win = await window.getLastWindow(ctx);
        // 开启全屏(沉浸式)
        await win.setWindowLayoutFullScreen(true)
        // 获取状态栏高度
        const statusBarHeight = win.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM)
        // 存储状态栏高度
        AppStorage.setOrCreate(STATUS_BAR_HEIGHT, px2vp(statusBarHeight.topRect.height))
        logger.info('FullScreen StatusBarHeight', px2vp(statusBarHeight.topRect.height) + '')
        // 获取导航条高度
        const navigationBarHeight = win.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR)
        // 存储导航条高度
        AppStorage.setOrCreate(NAVIGATION_BAR_HEIGHT, px2vp(navigationBarHeight.bottomRect.height))
        logger.info('FullScreen StatusBarHeight', px2vp(navigationBarHeight.bottomRect.height) + '')
      }
    } catch (err) {
      logger.error('FullScreen Open Error', err)
    }
  }

  //关闭沉浸式全屏
  async disable() {
    try {
      // 获取当前窗口Context对象
      const ctx = AppStorage.get<Context>(CONTEXT)
      if (ctx) {
        // 获取窗口对象
        const win = await window.getLastWindow(ctx)
        // 关闭全屏(沉浸式)
        win.setWindowLayoutFullScreen(false)

        // 恢复状态栏高度和导航条高度为0
        AppStorage.setOrCreate(STATUS_BAR_HEIGHT, 0)
        AppStorage.setOrCreate(NAVIGATION_BAR_HEIGHT, 0)
      }
    } catch (err) {
      logger.error('FullScreen Close Error', err)
    }
  }
}

/**
 * 【使用方法】
 * 开启沉浸式全屏 fullScreen.enable()
 * 1.可在UIAbility中生命周期onWindowStageCreate钩子函数中开启沉浸式全屏
 * 2.通过`@StorageProp(...)`获取本地存储的状态栏高度和导航条高度
 * 3.页面中以获取的本地存储值动态设置状态栏高度和导航条高度为上下padding进行避让
 *
 * 相关沉侵式系统API:
 * expandSafeArea 控制组件扩展其安全区域
 * setKeyboardAvoidMode 控制虚拟键盘抬起时页面的避让模式
 * getKeyboardAvoidMode 返回虚拟键盘抬起时页面的避让模式
 *
 * 关闭沉浸式全屏 fullScreen.disable()
 * */

export const fullScreen = new FullScreen()

StatusBar ------ 状态栏文字颜色控制

功能说明:

  • 切换手机顶部状态栏文字颜色(深色/浅色)

工具类方法:

typescript 复制代码
/**
 * description: [顶部状态栏文字颜色切换 黑色/白色]
 * */

import { CONTEXT } from "../constants"
import { window } from "@kit.ArkUI"

class StatusBar {
  // 深色模式
  setDarkBar() {
    this.setBar({ statusBarContentColor: '#000000' })
  }

  // 浅色模式
  ssetLightBar() {
    this.setBar({ statusBarContentColor: '#ffffff' })
  }

  // 设置颜色API
  async setBar(config: window.SystemBarProperties) {
    //获取context对象
    const ctx = AppStorage.get<Context>(CONTEXT)
    //判断context对象是否存在 健壮性检测
    if (ctx) {
      //获取当前窗口对象
      const win = await window.getLastWindow(ctx)
      //调用API设置状态栏颜色
      win.setWindowSystemBarProperties(config)
    }
  }
}


/**
 * 【使用方法】
 * statusBar.setDarkBar() //设置顶部状态栏【时间/电量..】文字深色模式为 黑色
 * statusBar.setLightBar() //设置顶部状态栏【时间/电量..】文字浅色模式为 白色
 * */

export const statusBar = new StatusBar()

Logger ------ 日志打印封装

功能说明:

  • 引用鸿蒙三方库 @abner/log二次封装配置,便于使用
  • 支持多种日志级别(info/debug/warn/error/fatal)
  • 自动识别 JSON 数据并格式化输出

二次封装:

typescript 复制代码
/**
 * description: [日志打印] 引用三方库 @abner/log 进行二次封装
 * */

import { Log } from '@abner/log'
import { describe } from '@ohos/hypium'

Log.init({
  tag: 'hm_interview', //打印的标签,默认为: hm_interview
  domain: 0x0000, //输出日志所对应的业务领域,默认为0x0000
  close: false, //关闭日志输出,默认true为关闭,false为开启
  isHilog: true, //是否为鸿蒙日志系统
  showLogLocation: true, //是否展示点击的位置,默认为true是展示 ,false为不展示
  logSize: 800 //日志每次输出大小,最大1024字节
})

/**
 * 【使用方法】
 * logger.info("我是一个info类型日志", "testTag")
 * logger.debug("我是一个debug类型日志", "testTag")
 * logger.warn("我是一个warn类型日志", "testTag")
 * logger.error("我是一个error类型日志", "testTag")
 * logger.fatal("我是一个fatal类型日志", "testTag")
 *
 * 直接传递JSON对象 会自动转换成JSON字符串
 * logger.info({ "name": "AbnerMing", "age": 18 })
 *
 * */

export { Log as logger }

SaveAlbum ------ 组件截图并保存至相册(截图分享)

✅ 使用说明

  • 作用.id('share') 为当前组件设置一个唯一的标识符,便于后续通过 componentSnapshot.get('share') 获取该组件的视图快照。
  • 应用场景:常用于分享弹窗、海报生成等需要对 UI 组件进行截图并保存的场景。
  • 配合工具类 :与 SaveAlbum 工具类结合使用,实现截图 → 保存至沙箱 → 写入相册的完整流程。

工具类方法:

typescript 复制代码
import { componentSnapshot } from '@kit.ArkUI'
import { image } from '@kit.ImageKit'
import { fileIo, fileUri } from '@kit.CoreFileKit'
import { photoAccessHelper } from '@kit.MediaLibraryKit'
import { CONTEXT } from '../constants'

class SaveAlbum {
  // 组件截图
  static async componentShot(compoentID: string) {
    // 获取组件截图
    const img = await componentSnapshot.get(compoentID)
    // 创建imagePacker对象实例
    const imagePacker = image.createImagePacker()
    // 获取到图片arrayBuffer 返回 图片二进制流
    return await imagePacker.packToData(img, { format: 'image/png', quality: 99 })
  }

  // 图片写入沙箱
  static imgWriteToSandbox(imgArrayBuffer: ArrayBuffer) {
    // 获取上下文
    const ctx = AppStorage.get<Context>(CONTEXT)
    // 创建图片路径
    const imgPath = ctx?.cacheDir + '/' + Date.now() + '.png'
    // 创建文件
    const file = fileIo.openSync(imgPath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE)
    // 写入文件
    fileIo.writeSync(file.fd, imgArrayBuffer)
    // 关闭文件
    fileIo.closeSync(file.fd)
    return imgPath
  }

  // 将沙箱图片写入相册
  static async sandboxImgToAlbum(imgPath: string) {
    // 获取上下文
    const ctx = AppStorage.get<Context>(CONTEXT)
    // 获取图片uri
    const uri = fileUri.getUriFromPath(imgPath)
    // 创建相册资源请求
    const photoRequest = photoAccessHelper.MediaAssetChangeRequest.createImageAssetRequest(ctx, uri)
    // 获取相册访问实例
    const photoHelper = photoAccessHelper.getPhotoAccessHelper(ctx)
    // 写入相册
    await photoHelper.applyChanges(photoRequest)
  }
}


export { SaveAlbum }

📌 示例调用方式

typescript 复制代码
// 截图组件
const imgArrayBuffer = await SaveAlbum.componentShot('share')

// 图片写入沙箱
const imgPath = SaveAlbum.imgWriteToSandbox(imgArrayBuffer)

// 将沙箱图片写入相册
await SaveAlbum.sandboxImgToAlbum(imgPath)

ShowToast - 轻提示封装

功能说明:

  • 引用鸿蒙三方库 @jxt/xt_hud二次封装配置,便于使用
  • 引用鸿蒙原生promptAction二次封装,提供统一调用接口
  • 对于简单的提示场景,直接使用 showToastmsg 即可
  • 对于复杂交互或需要 Loading、Progress的场景,可以结合三方库调用

ShowToast封装的工具类中需要获取上下文this

  1. EntryAbilityonCreate生命周期钩子函数中:
typescript 复制代码
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  ...
   // 通过应用状态管理对上下文进行状态创建设置
   AppStorage.setOrCreate('context', this.context)
}
  1. XTPromptInit工具方法获取窗口对象中进行获取使用
typescript 复制代码
async XTPromptInit() {
    try {
      const ctx = AppStorage.get<Context>(CONTEXT)
      if (ctx) {
        // 获取窗口对象
        const win = await window.getLastWindow(ctx);
        ...
      }
    }
  }
}

⚠️ 三方库 @jxt/xt_hud 需要先在 windowStage.loadContent 中进行初始化配置

typescript 复制代码
onWindowStageCreate(windowStage: window.WindowStage): void {
  windowStage.loadContent('pages/Index', (err) => {
    if (err.code) {
      return;
    }
    // XTPromptHUD 全局初始化
    showToast.XTPromptInit()
  });

工具类方法:

typescript 复制代码
import promptAction from '@ohos.promptAction';
import { IShowToast, EToastTypeColor, IShowDialog } from '../../commons/types'
import { CONTEXT } from '../constants';
import { window } from '@kit.ArkUI';
import { logger } from '.'

import {
  XTPromptHUD,
  XTHUDToastOptions,
  XTHUDLoadingOptions,
  XTHUDProgressOptions,
  XTHUDReactiveBaseOptions
} from '@jxt/xt_hud'

/**
 * ShowToast 类
 * 功能:封装统一的提示工具,包括轻提示(Toast)、弹窗确认(Dialog)和第三方 HUD 的初始化配置。
 */
class ShowToast {

  /**
   * 显示轻提示 Toast
   * 默认类型为 Info,时长为 2000 毫秒。
   *
   * @param opt - 提示参数对象,包含 message、type 和 duration
   */
  showToastmsg(opt: IShowToast) {
    promptAction.showToast({
      message: opt.message,
      textColor: opt.type || EToastTypeColor.Info, // 文字颜色,默认 Info 类型颜色
      duration: opt.duration || 2000, // 显示时长,默认 2 秒
    })
  }

  /**
   * 显示确认对话框 Dialog
   * 支持自定义标题、内容和按钮。默认按钮为"取消"和"确定"。
   *
   * @param opt - 弹窗参数对象,包含 title、message 和 buttons
   * @returns Promise<number> 点击按钮的索引(0 取消,1 确定)
   */
  showDialog(opt: IShowDialog) {
    return promptAction.showDialog({
      title: opt.title || '提示',  // 标题,默认 "提示"
      message: opt.message || '确定?', // 内容,默认 "确定?"
      buttons: [
        {
          text: (opt.buttons && opt.buttons[0].text) || '取消',
          color: $r('app.color.common_gray_03'), // 取消按钮颜色
        },
        {
          text: (opt.buttons && opt.buttons[0].text) || '确定',
          color: $r('app.color.common_main_color'),// 确定按钮颜色
        },
      ],
    })
  }

  /**
   * 初始化第三方 HUD 组件(XTPromptHUD)
   * 配置全局样式和行为,如模态显示、队列模式等。
   * 使用前需确保已正确引入 [@jxt/xt_hud]库。
   */
  async XTPromptInit() {
    try {
      const ctx = AppStorage.get<Context>(CONTEXT) // 获取上下文
      if (ctx) {
        // 获取窗口对象
        const win = await window.getLastWindow(ctx);
        
        // Toast 全局配置:启用队列模式
        XTPromptHUD.globalConfigToast(win.getUIContext(),
          (options: XTHUDToastOptions) => {
            options.isQueueMode = true
          })

        // Loading 全局配置:模态显示,遮罩层透明度为 50%,字体大小为 12
        XTPromptHUD.globalConfigLoading(win.getUIContext(),
          (options: XTHUDLoadingOptions) => {
            options.isModal = true
            options.maskColor = 'rgba(0,0,0,0.5)'
            options.fontSize = 12
          })

        // Progress 全局配置:模态显示
        XTPromptHUD.globalConfigProgress(win.getUIContext(),
          (options: XTHUDProgressOptions) => {
            options.isModal = true
          })

        // ReactiveBase 全局配置(用于进度条等):模态显示
        XTPromptHUD.globalConfigProgress(win.getUIContext(),
          (options: XTHUDReactiveBaseOptions) => {
            options.isModal = true
          })
      }
    } catch (err) {
      logger.error('ShowToast XTPromptInit Error', err)
    }
  }
}

// 导出单例实例
export const showToast = new ShowToast();

使用方法

typescript 复制代码
// 显示轻提示 Toast
showToast.showToastmsg({ message: '这是一条提示', type: EToastTypeColor.Info });


// 显示确认对话框 Dialog
showToast.showDialog({ title: '退出登录', message: '是否确认退出?',
  buttons: [
    { text: '取消', color: '#999' },
    { text: '确定', color: '#FF0000' }
  ]
})
const res = await showToast.showDialog({ title: '提示',message: '确定执行此操作?'})
 if (res.index === 1) {  
    // 用户点击了"确定"
 }
 
// XTPromptHUD
import { XTPromptHUD } from '@jxt/xt_hud'

// Toast 轻提示
XTPromptHUD.showToast('success')

//Loading 加载状态
XTPromptHUD.showLoading('加载中...')  //开启Loading
XTPromptHUD.hideLoading()  //关闭Loading

//Progress 加载进度条
this.progress = 0
this.interval = setInterval(() => {
  this.progress++
  XTPromptHUD.showProgress(this.progress)
  if (this.progress >= 100) {
    clearInterval(this.interval)
    this.interval = null
  }
}, 100)

Request - 网络请求封装(基于 Axios)

功能说明:

  • instance创建 axios 实例并设置基础 URL 和超时时间
  • request interceptor自动在请求头中携带 Token
  • response interceptor 统一处理响应数据、Token 失效跳转、提示等
  • Request class封装请求方法,支持泛型调用
  • http 实例导出可复用的请求工具实例

使用方法:

typescript 复制代码
import axios, { 
  AxiosRequestConfig, 
  AxiosResponse, 
  AxiosError, 
  InternalAxiosRequestConfig 
} from '@ohos/axios'

import { 
  BASE_URL,        // 请求基础地址
  TIME_OUT,        // 请求超时时间
  SUCCESS_CODE,    // 成功状态码
  TOKEN_INVALID_CODE // Token 失效状态码
} from '../constants'

import { 
  authUser,   // 用户认证工具类
  logger,     // 日志记录工具
  showToast   // 提示工具类
} from '../utils'

import { router } from '@kit.ArkUI';         // HarmonyOS 路由模块
import { EToastTypeColor } from '../types';  // Toast 颜色类型枚举


/**
 * 创建一个 axios 实例,并配置基础请求参数。
 *
 * baseURL: 所有请求的基础路径
 * timeout: 请求超时时间(毫秒)
 */
const instance = axios.create({
  baseURL: BASE_URL,
  timeout: TIME_OUT,
})


/**
 * 请求拦截器
 * 在请求发送前对配置进行处理,例如添加 token 到请求头
 *
 * @param config - 当前请求的配置对象
 * @returns 修改后的请求配置对象
 */
instance.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    // 获取用户 token 并添加到 Authorization 请求头
    const token = authUser.getToken()
    config.headers['Authorization'] = token ? 'Bearer ' + token : ''
    return config;
  },
  (error: AxiosError) => {
    // 请求出错时的日志记录
    logger.info(error)
    return Promise.reject(error);
  }
)


/**
 * 响应拦截器
 * 对响应数据进行统一处理,如判断是否登录过期、跳转登录页等
 *
 * @param response - 响应数据
 * @returns 响应中的业务数据或抛出错误
 */
instance.interceptors.response.use(
  (response: AxiosResponse) => {
    // 如果返回的状态码为成功码,返回 data 字段数据
    if (response.data.code === SUCCESS_CODE) {
      return response.data.data
    }
    // 否则以 reject 的方式返回错误信息,便于 catch 捕获
    return Promise.reject(response.data)
  },
  (error: AxiosError) => {
    // 如果是 Token 失效,则清除用户信息并跳转登录页
    if (error.response?.status === TOKEN_INVALID_CODE) {
      authUser.clearUser()
      router.pushUrl({ url: 'pages/LoginPage' }, router.RouterMode.Single)
      showToast.showToastmsg({ 
        message: '登录已过期,请重新登录', 
        type: EToastTypeColor.Warning 
      })
    }
    // 返回错误信息
    return Promise.reject(error.message)
  }
)


/**
 * Request 类
 * 封装基于 axios 实例的通用请求方法
 */
class Request {

  /**
   * 发起网络请求
   *
   * @param config - 请求配置对象
   * @returns 返回泛型 R 类型的数据
   */
  request<R, Q>(config: AxiosRequestConfig<Q>) {
    return instance<R, R, Q>(config)
  }
}


/**
 * 导出封装好的 http 实例
 * 使用方式:http.request<ResponseData,RequestData>({method: 'GET',url: '/api/user'})
 */
export const http = new Request()

AuthUser - 用户鉴权

功能说明:

  • init()初始化本地持久化存储(用户信息和 Token)
  • getUserInfo()获取当前用户信息
  • setUserInfo() 设置用户信息并更新 Token,触发事件通知
  • setToken()单独设置 Token
  • getToken()获取 Token
  • clearUser()清除用户信息和 Token,触发事件通知
  • authRoutePermission()控制页面跳转权限,无 Token 则跳转登录页

🧠 使用建议:

  • 初始化调用 :在应用启动时调用 authUser.init()
  • 用户登录后 :使用 authUser.setUserInfo(userInfo) 设置用户信息。
  • 用户登出时 :调用 authUser.clearUser() 清除信息。
  • 页面跳转鉴权 :使用 authUser.authRoutePermission({ url: 'pages/Profile' }) 控制访问权限。
typescript 复制代码
import { 
  USER_INFO,   // 用户信息存储 key
  TOKEN_KEY,   // Token 存储 key
  EMIT_EVENT   // 用户状态变更事件名
} from '../../commons/constants'

import { IUserData } from '../../models/types' // 用户数据类型定义
import { router } from '@kit.ArkUI'    // HarmonyOS 路由模块
import emitter from '@ohos.events.emitter'  // 事件发射器,用于全局通信


/**
 * AuthUser 类
 * 功能:用户认证与权限管理工具类
 *
 * 包括:
 * - 初始化本地持久化存储
 * - 获取/设置用户信息和 Token
 * - 清除用户信息
 * - 页面跳转鉴权控制
 */
class AuthUser {

  /**
   * 初始化本地持久化存储
   * 将用户信息和 Token 持久化到 AppStorage 中
   */
  init() {
    PersistentStorage.persistProps([{ 
      key: USER_INFO, 
      defaultValue: {} as IUserData 
    }])
    PersistentStorage.persistProps([{ 
      key: TOKEN_KEY, 
      defaultValue: '' as string 
    }])
  }

  /**
   * 获取当前用户信息
   * @returns 用户信息对象或空对象
   */
  getUserInfo(): IUserData | object {
    return AppStorage.get(USER_INFO) || {}
  }

  /**
   * 设置用户信息并同步保存 Token
   * @param userInfo 用户数据对象
   */
  setUserInfo(userInfo: IUserData) {
    AppStorage.set(USER_INFO, userInfo)
    this.setToken(userInfo.token)
    // 触发用户信息变更事件
    emitter.emit(EMIT_EVENT)
  }

  /**
   * 设置 Token 到 AppStorage
   * @param token 用户 Token 字符串
   */
  setToken(token: string) {
    AppStorage.set(TOKEN_KEY, token)
  }

  /**
   * 获取当前用户的 Token
   * @returns Token 字符串 或 空字符串
   */
  getToken(): string {
    return AppStorage.get(TOKEN_KEY) || ''
  }

  /**
   * 清除用户信息和 Token,并触发事件
   */
  clearUser() {
    AppStorage.set(USER_INFO, {} as IUserData)
    AppStorage.set(TOKEN_KEY, '')
    // 触发用户信息变更事件
    emitter.emit(EMIT_EVENT)
  }

  /**
   * 路由跳转鉴权控制
   * 如果未登录,则跳转至登录页
   *
   * @param opt - 跳转参数或回调函数
   */
  authRoutePermission(opt: router.RouterOptions | Function) {
    const isHasToken = this.getToken()
    
    if (typeof opt === 'function') {
      // 如果是函数,则根据是否有 Token 决定是否执行
      isHasToken ? opt() : router.pushUrl({ url: 'pages/LoginPage' })
    } else {
      // 如果是路由配置对象,则包装参数后跳转
      const jumpToLogin = () => {
        const target_url = opt.url
        opt.params = { url: target_url, params: opt.params }
        router.pushUrl({ url: 'pages/LoginPage', params: opt })
      }
      isHasToken ? router.pushUrl(opt) : jumpToLogin()
    }
  }
}

// 导出单例实例
export const authUser = new AuthUser()

🎯 总结

本篇文章详细介绍了我在 HarmonyOS 开发中常用的工具类及其使用方式,涵盖了从用户认证、网络请求、UI 控制到日志打印等多个方面,适合刚入门的小白直接复制粘贴使用。合理利用这些工具类,可以大幅提升开发效率和代码质量。

如需进一步扩展,也可以基于这些基础工具类添加更多业务逻辑封装。希望对你有所帮助!🚀

相关推荐
lucky志3 小时前
探秘鸿蒙 HarmonyOS NEXT:实战用 CodeGenie 构建鸿蒙应用页面
harmonyos·arkts
WLY2903 小时前
【HarmonyOS 5】------DevEco Studio 常见问题解决指南(新手友好版)
harmonyos
xq95273 小时前
鸿蒙next ndk入门到精通第一集
harmonyos
陈奕昆3 小时前
4.1 HarmonyOS NEXT原生AI能力集成:盘古大模型端侧部署与多模态交互实战
人工智能·交互·harmonyos
半路下车3 小时前
【Harmony OS 5】深入解析DevEco Testing
harmonyos
上海张律师3 小时前
仿微信登录页面制作
harmonyos
初晴4 小时前
【HarmonyOS5】在鸿蒙应用开发中使用第三方库 Axios 的实践指南#DevEco Studio#
harmonyos
飞龙AI4 小时前
鸿蒙NEXT上传图片功能PhotoViewPicker核心功能解析
harmonyos·arkts
HarmonyOS小助手4 小时前
【鸿蒙生态学堂04】ArkUI开发基础(上)
harmonyos·鸿蒙·harmonyos next·arkui(方舟ui框架)介绍·使用常用组件构建页面·harmonyos 5.0·鸿蒙5·鸿蒙课程·鸿蒙生态
枫叶丹47 小时前
【HarmonyOS Next之旅】DevEco Studio使用指南(三十)
华为·harmonyos·deveco studio·harmonyos next