鸿蒙媒体开辟【拼图】照相和图片

十念  金牌会员 | 2024-10-2 13:28:40 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 828|帖子 828|积分 2484

拼图

介绍

该示例通过@ohos.multimedia.image和@ohos.file.photoAccessHelper接口实现获取图片,以及图片裁剪分割的功能。
效果预览


利用说明:

  • 利用预置相机照相后启动应用,应用首页会读取设备内的图片文件并展示获取到的第一个图片,没有图片时图片位置表现空白;
  • 点击开始按钮开始后,时间开始倒计时,在规定时间内未完成拼图则游戏结束。在游戏中,玩家点击重新开始举行游戏重置;
  • 点击开始游戏后,玩家可以根据上方的大图,点击黄格四周的图片移动,点击后图片和黄格互换位置,最终拼成完整的图片;
  • 不在游戏中时,玩家可以点击上方大图,选择自定义图片来举行拼图游戏。
详细实现



  • 游戏中图片裁剪分割的效果实如今ImageModel中,源码参考[ImageModel]:
  1. /*
  2. * Copyright (c) 2022 Huawei Device Co., Ltd.
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. *     http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. import { image } from '@kit.ImageKit';
  16. import { photoAccessHelper } from '@kit.MediaLibraryKit';
  17. import { dataSharePredicates } from '@kit.ArkData';
  18. import { Context } from '@kit.AbilityKit';
  19. import { fileIo } from '@kit.CoreFileKit';
  20. import Logger from './Logger';
  21. import PictureItem from './PictureItem';
  22. import { CommonConstants as Common } from '../common/CommonConstants';
  23. const TAG = '[ImageModel]';
  24. export default class ImageModel {
  25.   private phAccessHelper: photoAccessHelper.PhotoAccessHelper | null = null;
  26.   constructor(context: Context) {
  27.     this.phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
  28.   }
  29.   async getAllImg(): Promise<photoAccessHelper.PhotoAsset[]> {
  30.     Logger.info('getAllImg');
  31.     let photoList: Array<photoAccessHelper.PhotoAsset> = [];
  32.     if (this.phAccessHelper === null) {
  33.       Logger.info('phAccessHelper fail');
  34.       return photoList;
  35.     }
  36.     let fileKeyType = photoAccessHelper.PhotoKeys.PHOTO_TYPE;
  37.     let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
  38.     Logger.info(fileKeyType);
  39.     let fetchOptions: photoAccessHelper.FetchOptions = {
  40.       fetchColumns: [],
  41.       predicates: predicates
  42.     };
  43.     try {
  44.       let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> =
  45.         await this.phAccessHelper.getAssets(fetchOptions);
  46.       if (fetchResult != undefined) {
  47.         Logger.info('fetchResult success');
  48.         let photoAsset: Array<photoAccessHelper.PhotoAsset> = await fetchResult.getAllObjects();
  49.         if (photoAsset != undefined && photoAsset.length > 0) {
  50.           for (let i = 0; i < photoAsset.length; i++) {
  51.             if (photoAsset[i].photoType === 1) {
  52.               photoList.push(photoAsset[i]);
  53.             }
  54.           }
  55.         }
  56.       }
  57.     } catch (err) {
  58.       Logger.error('getAssets failed, message = ', err);
  59.     }
  60.     Logger.info('photoList success');
  61.     return photoList;
  62.   }
  63.   async splitPic(index: number): Promise<PictureItem[]> {
  64.     let imagePixelMap: PictureItem[] = [];
  65.     let imagesData: Array<photoAccessHelper.PhotoAsset> = await this.getAllImg();
  66.     let imagePackerApi = image.createImagePacker();
  67.     fileIo.open(imagesData[index].uri, fileIo.OpenMode.READ_ONLY).then(async (file: fileIo.File) => {
  68.       let fd: number = file.fd;
  69.       let imageSource = image.createImageSource(fd);
  70.       let imageInfo = await imageSource.getImageInfo();
  71.       Logger.info(TAG, `sizeImg createImageSource ${JSON.stringify(imageSource)}`);
  72.       let height = imageInfo.size.height / Common.SPLIT_COUNT;
  73.       for (let i = 0; i < Common.SPLIT_COUNT; i++) {
  74.         for (let j = 0; j < Common.SPLIT_COUNT; j++) {
  75.           let picItem: PictureItem;
  76.           if (i === Common.SPLIT_COUNT - 1 && j === Common.SPLIT_COUNT - 1) {
  77.             picItem = new PictureItem(Common.PICTURE_ITEM_NUMBER, {} as image.PixelMap);
  78.             imagePixelMap.push(picItem);
  79.           } else {
  80.             Logger.info(TAG, `sizeImg x = ${imageInfo.size.width / Common.SPLIT_COUNT} y = ${height}`);
  81.             let decodingOptions: image.DecodingOptions = {
  82.               desiredRegion: {
  83.                 size: {
  84.                   height: height,
  85.                   width: imageInfo.size.width / Common.SPLIT_COUNT
  86.                 }, x: j * imageInfo.size.width / Common.SPLIT_COUNT, y: i * height
  87.               }
  88.             }
  89.             imagePixelMap.push(
  90.               new PictureItem(i * Common.SPLIT_COUNT + j, await imageSource.createPixelMap(decodingOptions)));
  91.           }
  92.         }
  93.       }
  94.       imagePackerApi.release();
  95.       fileIo.closeSync(fd);
  96.     })
  97.     return imagePixelMap;
  98.   }
  99. }
复制代码


  • 获取本地图片:首先利用getPhotoAccessHelper获取相册管理模块实例,然后利用getAssets方法获取文件资源,最后利用getAllObjects获取检索效果中的所有文件资产方便展示;
  • 裁剪图片准备:裁剪图片必要利用@ohos.multimedia.image接口,裁剪前必要申请图片编辑权限,利用requestPermissionsFromUser申请,源码参考[Index.ets];
  1. /*
  2. * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. *     http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. import { mediaquery } from '@kit.ArkUI';
  16. import { Permissions, abilityAccessCtrl } from '@kit.AbilityKit';
  17. import { emitter } from '@kit.BasicServicesKit';
  18. import { photoAccessHelper } from '@kit.MediaLibraryKit';
  19. import GameRules from '../model/GameRules';
  20. import ImagePicker from '../common/ImagePicker';
  21. import Logger from '../model/Logger';
  22. import PictureItem from '../model/PictureItem';
  23. import ImageModel from '../model/ImageModel';
  24. import { CommonConstants as Common } from '../common/CommonConstants';
  25. const PERMISSIONS: Array<Permissions> = [
  26.   'ohos.permission.READ_MEDIA',
  27.   'ohos.permission.WRITE_MEDIA',
  28.   'ohos.permission.MEDIA_LOCATION',
  29.   'ohos.permission.MANAGE_MISSIONS'
  30. ];
  31. @Entry
  32. @Component
  33. struct Index {
  34.   @State numArray: PictureItem[] = [];
  35.   @State imgData: Array<photoAccessHelper.PhotoAsset> = [];
  36.   @State @Watch('onTimeOver') gameTime: number = Common.GAME_TIME;
  37.   @State @Watch('onImageChange') index: number = Common.ZERO;
  38.   @State isLand: boolean = false;
  39.   @StorageLink('isGameStart') isGameStart: boolean = false;
  40.   private listener = mediaquery.matchMediaSync('screen and (min-aspect-ratio: 1.5) or (orientation: landscape)');
  41.   private ImageModel: ImageModel = new ImageModel(getContext(this));
  42.   private game: GameRules = new GameRules();
  43.   private timer: number = -1;
  44.   private isRefresh: boolean = false;
  45.   onLand = (mediaQueryResult: mediaquery.MediaQueryResult) => {
  46.     Logger.info(`[eTSMediaQuery.Index]onLand: mediaQueryResult.matches=${mediaQueryResult.matches}`);
  47.     this.isLand = mediaQueryResult.matches;
  48.   };
  49.   over = () => {
  50.     Logger.info(Common.TAG, 'emitter on , eventID = 0');
  51.     for (let i = Common.ZERO; i < Common.LOOP; i++) {
  52.       this.numArray[i].index = i;
  53.     }
  54.     this.gameOver();
  55.   };
  56.   timeEnd = () => {
  57.     Logger.info(Common.TAG, 'emitter on , eventID = 1');
  58.     this.gameTime = Common.ZERO;
  59.   };
  60.   async aboutToAppear() {
  61.     this.listener.on('change', this.onLand);
  62.     await abilityAccessCtrl.createAtManager().requestPermissionsFromUser(getContext(this), PERMISSIONS);
  63.     this.imgData = await this.ImageModel.getAllImg();
  64.     Logger.info(Common.TAG, `images = ${this.imgData.length}`);
  65.     this.numArray = await this.ImageModel.splitPic(this.index);
  66.     // Test case. The game is simulated successfully.
  67.     emitter.on({ eventId: Common.ZERO, priority: Common.ZERO }, this.over);
  68.     // Test case. End of simulation time.
  69.     emitter.on({ eventId: Common.ONE, priority: Common.ZERO }, this.timeEnd);
  70.   }
  71.   onTimeOver() {
  72.     if (this.gameTime === Common.ZERO) {
  73.       this.isGameStart = false;
  74.       AlertDialog.show({ message: 'TimeOver' });
  75.       clearInterval(this.timer);
  76.     }
  77.   }
  78.   async onImageChange() {
  79.     this.isRefresh = true;
  80.     this.dialogController.close();
  81.     this.numArray = [];
  82.     this.numArray = await this.ImageModel.splitPic(this.index);
  83.     this.init();
  84.     this.isGameStart = false;
  85.     this.isRefresh = false;
  86.   }
  87.   init() {
  88.     this.gameTime = Common.GAME_TIME;
  89.     clearInterval(this.timer);
  90.   }
  91.   gameOver() {
  92.     let count = Common.ZERO;
  93.     for (let i = Common.ZERO; i < Common.LOOP; i++) {
  94.       if (this.numArray[i].index === i) {
  95.         count++;
  96.       } else {
  97.         count = Common.ZERO;
  98.         break;
  99.       }
  100.     }
  101.     if (count === Common.LOOP) {
  102.       this.isGameStart = false;
  103.       AlertDialog.show({ message: $r('app.string.congratulations') });
  104.       clearInterval(this.timer);
  105.       this.gameTime = Common.GAME_TIME;
  106.     }
  107.   }
  108.   start() {
  109.     this.init();
  110.     this.timer = setInterval(() => {
  111.       this.gameTime--;
  112.     }, Common.TIME_NUMBER)
  113.   }
  114.   dialogController: CustomDialogController = new CustomDialogController({
  115.     builder: ImagePicker({
  116.       imagesData: this.imgData,
  117.       index: $index
  118.     }),
  119.     autoCancel: true,
  120.     gridCount: Common.GRID_COUNT
  121.   })
  122.   @Builder
  123.   ImageShow() {
  124.     Image(this.imgData[this.index].uri)
  125.       .id(Common.IMAGE_SHOW)
  126.       .width(Common.IMAGE_WIDTH)
  127.       .height($r('app.float.imageShow_height'))
  128.       .objectFit(ImageFit.Fill)
  129.       .onClick(async () => {
  130.         if (this.isRefresh) {
  131.           return;
  132.         }
  133.         this.imgData = await this.ImageModel.getAllImg();
  134.         setTimeout(() => {
  135.           this.dialogController.open();
  136.         }, Common.TIME);
  137.       })
  138.   }
  139.   @Builder
  140.   ImageGrid(leftMargin: number, topMargin: number) {
  141.     Grid() {
  142.       ForEach(this.numArray, (item: PictureItem, index) => {
  143.         GridItem() {
  144.           Image(item.pixelMap)
  145.             .width(Common.GRID_IMAGE_WIDTH)
  146.             .objectFit(ImageFit.Fill)
  147.             .height($r('app.float.grid_image_height'))
  148.         }
  149.         .id(`image${index}`)
  150.         .backgroundColor(item.pixelMap === undefined ? $r('app.color.blank_picture_background') : $r('app.color.picture_background'))
  151.         .onClick(() => {
  152.           if (this.isRefresh) {
  153.             return;
  154.           }
  155.           if (this.isGameStart) {
  156.             this.isRefresh = true;
  157.             this.numArray = this.game.gameInit(index, this.numArray);
  158.             this.gameOver();
  159.             this.isRefresh = false;
  160.           }
  161.         })
  162.       }, (item: PictureItem) => JSON.stringify(item))
  163.     }
  164.     .id(Common.IMAGE_GRID)
  165.     .columnsTemplate(Common.COLUMN_TEMPLATE)
  166.     .columnsGap($r('app.float.gap'))
  167.     .rowsGap($r('app.float.gap'))
  168.     .width(Common.IMAGE_WIDTH)
  169.     .height($r('app.float.grid_height'))
  170.     .margin({ left: leftMargin, top: topMargin })
  171.   }
  172.   build() {
  173.     Column() {
  174.       Row() {
  175.         Text(`Time:0${Math.floor(this.gameTime / Common.SECOND)}:${this.gameTime % Common.SECOND < Common.TEN
  176.           ? Common.STRING_ZERO + this.gameTime % Common.SECOND : this.gameTime % Common.SECOND}`)
  177.           .id(Common.TIME_STRING)
  178.           .margin({ top: Common.MARGIN, bottom: Common.MARGIN })
  179.       }
  180.       if (this.imgData.length > Common.ZERO) {
  181.         if (this.isLand) {
  182.           Row() {
  183.             this.ImageShow()
  184.             this.ImageGrid(Common.TEN, Common.ZERO)
  185.           }
  186.           .margin({ top: Common.MARGIN })
  187.         } else {
  188.           Column() {
  189.             this.ImageShow()
  190.             this.ImageGrid(Common.ZERO, Common.FIVE)
  191.           }
  192.           .margin({ top: Common.MARGIN })
  193.         }
  194.       }
  195.       Button($r('app.string.start'), { type: ButtonType.Capsule, stateEffect: true })
  196.         .id(Common.START_BUTTON)
  197.         .height($r('app.float.button_height'))
  198.         .width(Common.FULL_WIDTH)
  199.         .fontSize($r('app.float.button_font_size'))
  200.         .margin({ top: Common.MARGIN })
  201.         .backgroundColor(this.isGameStart ? $r('app.color.forbid') : $r('app.color.allow'))
  202.         .enabled(!this.isGameStart)
  203.         .onClick(() => {
  204.           this.isGameStart = true;
  205.           this.start();
  206.           this.numArray = this.game.gameBegin(this.numArray);
  207.         })
  208.       Button($r('app.string.restart'), { type: ButtonType.Capsule, stateEffect: true })
  209.         .id(Common.RESTART_BUTTON)
  210.         .height($r('app.float.button_height'))
  211.         .width(Common.FULL_WIDTH)
  212.         .fontSize($r('app.float.button_font_size'))
  213.         .margin({ top: Common.MARGIN })
  214.         .backgroundColor(this.isGameStart ? $r('app.color.allow') : $r('app.color.forbid'))
  215.         .enabled(this.isGameStart)
  216.         .onClick(() => {
  217.           this.isGameStart = true;
  218.           this.start();
  219.           this.numArray = this.game.gameBegin(this.numArray);
  220.         })
  221.     }
  222.     .width(Common.FULL_WIDTH)
  223.     .height(Common.FULL_HEIGHT)
  224.     .padding({ left: Common.PERCENT, right: Common.PERCENT })
  225.   }
  226. }
复制代码


  • 图片编辑:首先利用createImagePacker创建ImagePacker实例,然后利用fs.open打开文件,调用createImageSource接口创建图片源实例方便操作图片,接下来利用getImageInfo方法获取图片巨细便于分割,最后利用createPixelMap方法传入每一份的尺寸参数完成图片裁剪。
以上就是本篇文章所带来的鸿蒙开辟中一小部分技术讲解;想要学习完整的鸿蒙全栈技术。可以在末端找我可全部拿到!
下面是鸿蒙的完整学习门路,展示如下:

除此之外,根据这个学习鸿蒙全栈学习门路,也附带一整套完整的学习【文档+视频】,内容包含如下
   内容包含了:(ArkTS、ArkUI、Stage模型、多端部署、分布式应用开辟、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、鸿蒙南向开辟、鸿蒙项目实战)等技术知识点。资助各人在学习鸿蒙路上快速成长!
  鸿蒙【北向应用开辟+南向系统层开辟】文档

鸿蒙【底子+实战项目】视频

鸿蒙面经


为了避免各人在学习过程中产生更多的时间成本,对比我把以上内容全部放在了↓↓↓想要的可以自拿喔!谢谢各人观看!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

十念

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表