当前位置:   article > 正文

OpenHarmony开发实战:图片编辑(ArkTS)_openharmony 图片b编辑裁剪框

openharmony 图片b编辑裁剪框

本篇Codelab是基于ArkTS的声明式开发范式的样例,主要介绍了图片编辑实现过程。样例主要包含以下功能:

  1. 图片的解码。
  2. 使用PixelMap进行图片编辑,如裁剪、旋转、亮度、透明度、饱和度等。
  3. 图片的编码。

相关概念

  • 图片解码:读取不同格式的图片文件,无压缩的解码为位图格式。
  • PixelMap:图片解码后的状态,用于对图片像素进行处理。
  • 图片编码:图片经过像素处理完成之后,需要重新进行编码打包,生成需要的图片格式。

环境搭建

软件要求

  • DevEco Studio版本:DevEco Studio 3.1 Release。
  • OpenHarmony SDK版本:API version 9。

硬件要求

环境搭建

完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:

  1. 获取OpenHarmony系统版本:标准系统解决方案(二进制)。以3.2 Release版本为例:

  2. 搭建烧录环境。

    1. 完成DevEco Device Tool的安装
    2. 完成RK3568开发板的烧录
  3. 搭建开发环境。

    1. 开始前请参考工具准备,完成DevEco Studio的安装和开发环境配置。
    2. 开发环境配置完成后,请参考使用工程向导创建工程(模板选择“Empty Ability”)。
    3. 工程创建完成后,选择使用真机进行调测

代码结构解读

本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在gitee中提供。

  1. ├──entry/src/main/ets // 代码区
  2. │ ├──common
  3. │ │ └──constant
  4. │ │ └──CommonConstant.ets // 常量类
  5. │ ├──entryability
  6. │ │ └──EntryAbility.ts // 本地启动ability
  7. │ ├──pages
  8. │ │ └──HomePage.ets // 本地主页面
  9. │ ├──utils
  10. │ │ ├──AdjustUtil.ets // 调节工具类
  11. │ │ ├──CropUtil.ets // 裁剪工具类
  12. │ │ ├──DecodeUtil.ets // 解码工具类
  13. │ │ ├──DrawingUtils.ets // Canvas画图工具类
  14. │ │ ├──EncodeUtil.ets // 编码工具类
  15. │ │ ├──LoggerUtil.ets // 日志工具类
  16. │ │ ├──MathUtils.ets // 坐标转换工具类
  17. │ │ └──OpacityUtil.ets // 透明度调节工具类
  18. │ ├──view
  19. │ │ ├──AdjustContentView.ets // 色域调整视图
  20. │ │ └──ImageSelect.ets // Canvas选择框实现类
  21. │ ├──viewmodel
  22. │ │ ├──CropShow.ets // 选择框显示控制类
  23. │ │ ├──CropType.ets // 按比例选取图片
  24. │ │ ├──IconListViewModel.ets // icon数据
  25. │ │ ├──ImageEditCrop.ets // 图片编辑操作类
  26. │ │ ├──ImageFilterCrop.ets // 图片操作收集类
  27. │ │ ├──ImageSizeItem.ets // 图片尺寸
  28. │ │ ├──Line.ets // 线封装类
  29. │ │ ├──MessageItem.ets // 多线程封装消息
  30. │ │ ├──OptionViewModel.ets // 图片处理封装类
  31. │ │ ├──PixelMapWrapper.ets // PixelMap封装类
  32. │ │ ├──Point.ets // 点封装类
  33. │ │ ├──Ratio.ets // 比例封装类
  34. │ │ ├──Rect.ets // 矩形封装类
  35. │ │ ├──RegionItem.ets // 区域封装类
  36. │ │ └──ScreenManager.ts // 屏幕尺寸计算工具类
  37. │ └──workers
  38. │ ├──AdjustBrightnessWork.ts // 亮度异步调节
  39. │ └──AdjustSaturationWork.ts // 饱和度异步调节
  40. └──entry/src/main/resources // 资源文件目录

图片解码

在这个章节中,需要完成图片解码的操作,并将解码后的图片展示。效果如图所示:

在进行图片编辑前需要先加载图片,当前文档是在生命周期aboutToAppear开始加载。具体实现步骤。

  1. 读取资源文件。
  2. 将获取的fd创建成图片实例,通过实例获取其pixelMap。
  3. 将解析好的pixelMap通过Image组件加载显示。
  1. // HomePage.ets
  2. aboutToAppear() {
  3. this.pixelInit();
  4. ...
  5. }
  6. build() {
  7. Column() {
  8. ...
  9. Column() {
  10. if (this.isCrop && this.showCanvas && this.statusBar > 0) {
  11. if (this.isSaveFresh) {
  12. ImageSelect({
  13. statusBar: this.statusBar
  14. })
  15. }
  16. ...
  17. } else {
  18. if (this.isPixelMapChange) {
  19. Image(this.pixelMap)
  20. .scale({ x: this.imageScale, y: this.imageScale, z: 1 })
  21. .objectFit(ImageFit.None)
  22. }
  23. ...
  24. }
  25. }
  26. ...
  27. }
  28. ...
  29. }
  30. async getResourceFd(filename: string) {
  31. const resourceMgr = getContext(this).resourceManager;
  32. const context = getContext(this);
  33. if (filename === CommonConstants.RAW_FILE_NAME) {
  34. let imageBuffer = await resourceMgr.getMediaContent($r("app.media.ic_low"))
  35. let filePath = context.cacheDir + '/' + filename;
  36. let file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
  37. let writeLen = fs.writeSync(file.fd, imageBuffer.buffer);
  38. fs.copyFileSync(filePath, context.cacheDir + '/' + CommonConstants.RAW_FILE_NAME_TEST);
  39. return file.fd;
  40. } else {
  41. let filePath = context.cacheDir + '/' + filename;
  42. let file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
  43. return file.fd;
  44. }
  45. }
  46. async getPixelMap(fileName: string) {
  47. const fd = await this.getResourceFd(fileName);
  48. const imageSourceApi = image.createImageSource(fd);
  49. if (!imageSourceApi) {
  50. Logger.error(TAG, 'imageSourceAPI created failed!');
  51. return;
  52. }
  53. const pixelMap = await imageSourceApi.createPixelMap({
  54. editable: true
  55. });
  56. return pixelMap;
  57. }

图片处理

当前章节需要完成图片的裁剪、旋转、色域调节(本章只介绍亮度、透明度、饱和度)等功能。

  • 裁剪:选取图片中的部分进行裁剪生成新的图片。
  • 旋转:将图片按照不同的角度进行旋转,生成新的图片。
  • 色域调节:当前Codelab色域调节的亮度、透明度和饱和度,使用色域模型RGB-HSV来实现的。
    • RGB:是我们接触最多的颜色空间,分别为红色(R),绿色(G)和蓝色(B)。
    • HSV:是用色相H,饱和度S,明亮度V来描述颜色的变化。
      • H:色相H取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。
      • S:饱和度S越高,颜色则深而艳。光谱色的白光成分为0,饱和度达到最高。通常取值范围为0%~100%,值越大,颜色越饱和。
      • V:明度V表示颜色明亮的程度,对于光源色,明度值与发光体的光亮度有关;对于物体色,此值和物体的透射比或反射比有关。通常取值范围为0%(黑)到100%(白)。
  1. // AdjustUtil.ets
  2. // rgb转hsv
  3. function rgb2hsv(red: number, green: number, blue: number) {
  4. let hsvH: number = 0, hsvS: number = 0, hsvV: number = 0;
  5. const rgbR: number = colorTransform(red);
  6. const rgbG: number = colorTransform(green);
  7. const rgbB: number = colorTransform(blue);
  8. const maxValue = Math.max(rgbR, Math.max(rgbG, rgbB));
  9. const minValue = Math.min(rgbR, Math.min(rgbG, rgbB));
  10. hsvV = maxValue * CommonConstants.CONVERT_INT;
  11. if (maxValue === 0) {
  12. hsvS = 0;
  13. } else {
  14. hsvS = Number((1 - minValue / maxValue).toFixed(CommonConstants.DECIMAL_TWO)) * CommonConstants.CONVERT_INT;
  15. }
  16. if (maxValue === minValue) {
  17. hsvH = 0;
  18. }
  19. if (maxValue === rgbR && rgbG >= rgbB) {
  20. hsvH = Math.floor(CommonConstants.ANGLE_60 * ((rgbG - rgbB) / (maxValue - minValue)));
  21. }
  22. if (maxValue === rgbR && rgbG < rgbB) {
  23. hsvH = Math.floor(CommonConstants.ANGLE_60 * ((rgbG - rgbB) / (maxValue - minValue)) + CommonConstants.ANGLE_360);
  24. }
  25. if (maxValue === rgbG) {
  26. hsvH = Math.floor(CommonConstants.ANGLE_60 * ((rgbB - rgbR) / (maxValue - minValue)) + CommonConstants.ANGLE_120);
  27. }
  28. if (maxValue === rgbB) {
  29. hsvH = Math.floor(CommonConstants.ANGLE_60 * ((rgbR - rgbG) / (maxValue - minValue)) + CommonConstants.ANGLE_240);
  30. }
  31. return [hsvH, hsvS, hsvV];
  32. }
  33. // hsv转rgb
  34. function hsv2rgb(hue: number, saturation: number, value: number) {
  35. let rgbR: number = 0, rgbG: number = 0, rgbB: number = 0;
  36. if (saturation === 0) {
  37. rgbR = rgbG = rgbB = Math.round((value * CommonConstants.COLOR_LEVEL_MAX) / CommonConstants.CONVERT_INT);
  38. return { rgbR, rgbG, rgbB };
  39. }
  40. const cxmC = (value * saturation) / (CommonConstants.CONVERT_INT * CommonConstants.CONVERT_INT);
  41. const cxmX = cxmC * (1 - Math.abs((hue / CommonConstants.ANGLE_60) % CommonConstants.MOD_2 - 1));
  42. const cxmM = (value - cxmC * CommonConstants.CONVERT_INT) / CommonConstants.CONVERT_INT;
  43. const hsvHRange = Math.floor(hue / CommonConstants.ANGLE_60);
  44. switch (hsvHRange) {
  45. case AngelRange.ANGEL_0_60:
  46. rgbR = (cxmC + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
  47. rgbG = (cxmX + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
  48. rgbB = (0 + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
  49. break;
  50. case AngelRange.ANGEL_60_120:
  51. rgbR = (cxmX + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
  52. rgbG = (cxmC + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
  53. rgbB = (0 + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
  54. break;
  55. case AngelRange.ANGEL_120_180:
  56. rgbR = (0 + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
  57. rgbG = (cxmC + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
  58. rgbB = (cxmX + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
  59. break;
  60. case AngelRange.ANGEL_180_240:
  61. rgbR = (0 + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
  62. rgbG = (cxmX + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
  63. rgbB = (cxmC + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
  64. break;
  65. case AngelRange.ANGEL_240_300:
  66. rgbR = (cxmX + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
  67. rgbG = (0 + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
  68. rgbB = (cxmC + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
  69. break;
  70. case AngelRange.ANGEL_300_360:
  71. rgbR = (cxmC + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
  72. rgbG = (0 + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
  73. rgbB = (cxmX + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
  74. break;
  75. default:
  76. break;
  77. }
  78. return [
  79. Math.round(rgbR),
  80. Math.round(rgbG),
  81. Math.round(rgbB)
  82. ];
  83. }

图片裁剪

  1. 通过pixelMap获取图片尺寸,为后续裁剪做准备。
  2. 确定裁剪的方式,当前裁剪默认有自由选取、1:1选取、4:3选取、16:9选取。
  3. 通过pixelMap调用接口crop()进行裁剪操作。

 说明: 当前裁剪功能采用pixelMap裁剪能力直接做切割,会有叠加效果,后续会通过增加选取框对当前功能进行优化。

  1. // HomePage.ets
  2. cropImage(index: CropType) {
  3. this.currentCropIndex = index;
  4. switch (this.currentCropIndex) {
  5. case CropType.ORIGINAL_IMAGE:
  6. this.cropRatio = CropRatioType.RATIO_TYPE_FREE;
  7. break;
  8. case CropType.SQUARE:
  9. this.cropRatio = CropRatioType.RATIO_TYPE_1_1;
  10. break;
  11. case CropType.BANNER:
  12. this.cropRatio = CropRatioType.RATIO_TYPE_4_3;
  13. break;
  14. case CropType.RECTANGLE:
  15. this.cropRatio = CropRatioType.RATIO_TYPE_16_9;
  16. break;
  17. default:
  18. this.cropRatio = CropRatioType.RATIO_TYPE_FREE;
  19. break;
  20. }
  21. }
  22. // ImageFilterCrop.ets
  23. cropImage(pixelMap: PixelMapWrapper, realCropRect: RectF, callback: () => void) {
  24. let offWidth = realCropRect.getWidth();
  25. let offHeight = realCropRect.getHeight();
  26. if (pixelMap.pixelMap!== undefined) {
  27. pixelMap.pixelMap.crop({
  28. size:{ height: vp2px(offHeight), width: vp2px(offWidth) },
  29. x: vp2px(realCropRect.left),
  30. y: vp2px(realCropRect.top)
  31. }, callback);
  32. }
  33. }

图片旋转

  1. 确定旋转方向,当前支持顺时针和逆时针旋转。
  2. 通过pixelMap调用接口rotate()进行旋转操作。

  1. // HomePage.ets
  2. rotateImage(rotateType: RotateType) {
  3. if (rotateType === RotateType.CLOCKWISE) {
  4. try {
  5. if (this.pixelMap !== undefined) {
  6. this.pixelMap.rotate(CommonConstants.CLOCK_WISE)
  7. .then(() => {
  8. this.flushPixelMapNew();
  9. })
  10. }
  11. } catch (error) {
  12. Logger.error(TAG, `there is a error in rotate process with ${error?.code}`);
  13. }
  14. }
  15. if (rotateType === RotateType.ANTI_CLOCK) {
  16. try {
  17. if (this.pixelMap !== undefined) {
  18. this.pixelMap.rotate(CommonConstants.ANTI_CLOCK)
  19. .then(() => {
  20. this.flushPixelMapNew();
  21. })
  22. }
  23. } catch (error) {
  24. Logger.error(TAG, `there is a error in rotate process with ${error?.code}`);
  25. }
  26. }
  27. }

亮度调节

  1. 将pixelMap转换成ArrayBuffer。
  2. 将生成好的ArrayBuffer发送到worker线程。
  3. 对每一个像素点的亮度值按倍率计算。
  4. 将计算好的ArrayBuffer发送回主线程。
  5. 将ArrayBuffer写入pixelMap,刷新UI。

 说明: 当前亮度调节是在UI层面实现的,未实现细节优化算法,只做简单示例。调节后的图片会有色彩上的失真。

  1. // AdjustContentView.ets
  2. // 转化成pixelMap及发送buffer到worker,返回数据刷新ui
  3. postToWorker(type: AdjustId, value: number, workerName: string) {
  4. let sliderValue = type === AdjustId.BRIGHTNESS ? this.brightnessLastSlider : this.saturationLastSlider;
  5. try {
  6. let workerInstance = new worker.ThreadWorker(workerName);
  7. const bufferArray = new ArrayBuffer(this.pixelMap.getPixelBytesNumber());
  8. this.pixelMap.readPixelsToBuffer(bufferArray).then(() => {
  9. let message = new MessageItem(bufferArray, sliderValue, value);
  10. workerInstance.postMessage(message);
  11. if (this.postState) {
  12. this.deviceListDialogController.open();
  13. }
  14. this.postState = false;
  15. workerInstance.onmessage = (event: MessageEvents) => {
  16. this.updatePixelMap(event)
  17. };
  18. if (type === AdjustId.BRIGHTNESS) {
  19. this.brightnessLastSlider = Math.round(value);
  20. } else {
  21. this.saturationLastSlider = Math.round(value);
  22. }
  23. workerInstance.onexit = () => {
  24. if (workerInstance !== undefined) {
  25. workerInstance.terminate();
  26. }
  27. }
  28. });
  29. } catch (error) {
  30. Logger.error(`Create work instance fail, error message: ${JSON.stringify(error)}`)
  31. }
  32. }
  33. // AdjustBrightnessWork.ts
  34. // worker线程处理部分
  35. workerPort.onmessage = function(event : MessageEvents) {
  36. let bufferArray = event.data.buf;
  37. let last = event.data.last;
  38. let cur = event.data.cur;
  39. let buffer = adjustImageValue(bufferArray, last, cur);
  40. workerPort.postMessage(buffer);
  41. workerPort.close();
  42. }
  43. // AdjustUtil.ets
  44. // 倍率计算部分
  45. export function adjustImageValue(bufferArray: ArrayBuffer, last: number, cur: number) {
  46. return execColorInfo(bufferArray, last, cur, HSVIndex.VALUE);
  47. }

透明度调节

  1. 获取pixelMap。
  2. 调用接口opacity()进行透明度调节。

  1. // OpacityUtil.ets
  2. export async function adjustOpacity(pixelMap: PixelMap, value: number) {
  3. if (!pixelMap) {
  4. return;
  5. }
  6. const newPixelMap = pixelMap;
  7. await newPixelMap.opacity(value / CommonConstants.SLIDER_MAX);
  8. return newPixelMap;
  9. }

饱和度调节

  1. 将pixelMap转换成ArrayBuffer。
  2. 将生成好的ArrayBuffer发送到worker线程。
  3. 对每一个像素点的饱和度按倍率计算。
  4. 将计算好的ArrayBuffer发送回主线程。
  5. 将ArrayBuffer写入pixelMap,刷新UI。

 说明: 当前饱和度调节是在UI层面实现的,未实现细节优化算法,只做简单示例。调节后的图片会有色彩上的失真。

  1. // AdjustContentView.ets
  2. // 转化成pixelMap及发送buffer到worker,返回数据刷新ui
  3. postToWorker(type: AdjustId, value: number, workerName: string) {
  4. let sliderValue = type === AdjustId.BRIGHTNESS ? this.brightnessLastSlider : this.saturationLastSlider;
  5. try {
  6. let workerInstance = new worker.ThreadWorker(workerName);
  7. const bufferArray = new ArrayBuffer(this.pixelMap.getPixelBytesNumber());
  8. this.pixelMap.readPixelsToBuffer(bufferArray).then(() => {
  9. let message = new MessageItem(bufferArray, sliderValue, value);
  10. workerInstance.postMessage(message);
  11. if (this.postState) {
  12. this.deviceListDialogController.open();
  13. }
  14. this.postState = false;
  15. workerInstance.onmessage = (event: MessageEvents) => {
  16. this.updatePixelMap(event)
  17. };
  18. if (type === AdjustId.BRIGHTNESS) {
  19. this.brightnessLastSlider = Math.round(value);
  20. } else {
  21. this.saturationLastSlider = Math.round(value);
  22. }
  23. workerInstance.onexit = () => {
  24. if (workerInstance !== undefined) {
  25. workerInstance.terminate();
  26. }
  27. }
  28. });
  29. } catch (error) {
  30. Logger.error(`Create work instance fail, error message: ${JSON.stringify(error)}`);
  31. }
  32. }
  33. // AdjustSaturationWork.ts
  34. // worker线程处理部分
  35. workerPort.onmessage = function(event : MessageEvents) {
  36. let bufferArray = event.data.buf;
  37. let last = event.data.last;
  38. let cur = event.data.cur;
  39. let buffer = adjustSaturation(bufferArray, last, cur)
  40. workerPort.postMessage(buffer);
  41. workerPort.close();
  42. }
  43. // AdjustUtil.ets
  44. // 倍率计算部分
  45. export function adjustSaturation(bufferArray: ArrayBuffer, last: number, cur: number) {
  46. return execColorInfo(bufferArray, last, cur, HSVIndex.SATURATION);
  47. }

图片编码

图片位图经过处理之后,还是属于解码的状态,还需要进行打包编码成对应的格式,本章讲解编码的具体过程。

  1. 通过image组件创建打包工具packer。
  2. 使用PackingOption进行打包参数设定,比如格式、压缩质量等。
  3. 打包成图片信息数据imageData。
  4. 创建媒体库media,获取公共路径。
  5. 创建媒体文件asset,获取其fd。
  6. 使用fs将打包好的图片数据写入到媒体文件asset中。
  1. // ImageSelect.ets
  2. async encode(pixelMap: PixelMap | undefined) {
  3. if (pixelMap === undefined) {
  4. return;
  5. }
  6. const newPixelMap = pixelMap;
  7. // 打包图片
  8. const imagePackerApi = image.createImagePacker();
  9. const packOptions: image.PackingOption = {
  10. format: CommonConstants.ENCODE_FORMAT,
  11. quality: CommonConstants.ENCODE_QUALITY
  12. }
  13. const imageData = await imagePackerApi.packing(newPixelMap, packOptions);
  14. Logger.info(TAG, `imageData's length is ${imageData.byteLength}`);
  15. // 获取相册路径
  16. const context = getContext(this);
  17. const media = mediaLibrary.getMediaLibrary(context);
  18. const publicPath = await media.getPublicDirectory(mediaLibrary.DirectoryType.DIR_IMAGE);
  19. const currentTime = new Date().getTime();
  20. // 创建图片资源
  21. const imageAssetInfo = await media.createAsset(
  22. mediaLibrary.MediaType.IMAGE,
  23. `${CommonConstants.IMAGE_PREFIX}_${currentTime}${CommonConstants.IMAGE_FORMAT}`,
  24. publicPath
  25. );
  26. const imageFd = await imageAssetInfo.open(CommonConstants.ENCODE_FILE_PERMISSION);
  27. await fs.write(imageFd, imageData);
  28. // 释放资源
  29. await imageAssetInfo.close(imageFd);
  30. imagePackerApi.release();
  31. await media.release();
  32. }

最后

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。 

这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。

希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!

如果你是一名有经验的资深Android移动开发、Java开发、前端开发、对鸿蒙感兴趣以及转行人员,可以直接领取这份资料

 获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

鸿蒙(HarmonyOS NEXT)最新学习路线

  •  HarmonOS基础技能

  • HarmonOS就业必备技能 
  •  HarmonOS多媒体技术

  • 鸿蒙NaPi组件进阶

  • HarmonOS高级技能

  • 初识HarmonOS内核 
  • 实战就业级设备开发

 有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。

获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

《鸿蒙 (OpenHarmony)开发入门教学视频》

《鸿蒙生态应用开发V2.0白皮书》

图片

《鸿蒙 (OpenHarmony)开发基础到实战手册》

OpenHarmony北向、南向开发环境搭建

图片

 《鸿蒙开发基础》

  • ArkTS语言
  • 安装DevEco Studio
  • 运用你的第一个ArkTS应用
  • ArkUI声明式UI开发
  • .……

图片

 《鸿蒙开发进阶》

  • Stage模型入门
  • 网络管理
  • 数据管理
  • 电话服务
  • 分布式应用开发
  • 通知与窗口管理
  • 多媒体技术
  • 安全技能
  • 任务管理
  • WebGL
  • 国际化开发
  • 应用测试
  • DFX面向未来设计
  • 鸿蒙系统移植和裁剪定制
  • ……

图片

《鸿蒙进阶实战》

  • ArkTS实践
  • UIAbility应用
  • 网络案例
  • ……

图片

 获取以上完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料

总结

总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/423135
推荐阅读
  

闽ICP备14008679号