鸿蒙媒体开发【拼图】拍照和图片

news2024/9/23 21:18:25

拼图

介绍

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

效果预览

1

使用说明:

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

具体实现

  • 游戏中图片裁剪分割的效果实现在ImageModel中,源码参考[ImageModel]:
/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { image } from '@kit.ImageKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { dataSharePredicates } from '@kit.ArkData';
import { Context } from '@kit.AbilityKit';
import { fileIo } from '@kit.CoreFileKit';
import Logger from './Logger';
import PictureItem from './PictureItem';
import { CommonConstants as Common } from '../common/CommonConstants';

const TAG = '[ImageModel]';

export default class ImageModel {
  private phAccessHelper: photoAccessHelper.PhotoAccessHelper | null = null;

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

  async getAllImg(): Promise<photoAccessHelper.PhotoAsset[]> {
    Logger.info('getAllImg');
    let photoList: Array<photoAccessHelper.PhotoAsset> = [];
    if (this.phAccessHelper === null) {
      Logger.info('phAccessHelper fail');
      return photoList;
    }
    let fileKeyType = photoAccessHelper.PhotoKeys.PHOTO_TYPE;
    let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
    Logger.info(fileKeyType);
    let fetchOptions: photoAccessHelper.FetchOptions = {
      fetchColumns: [],
      predicates: predicates
    };

    try {
      let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> =
        await this.phAccessHelper.getAssets(fetchOptions);
      if (fetchResult != undefined) {
        Logger.info('fetchResult success');
        let photoAsset: Array<photoAccessHelper.PhotoAsset> = await fetchResult.getAllObjects();
        if (photoAsset != undefined && photoAsset.length > 0) {
          for (let i = 0; i < photoAsset.length; i++) {
            if (photoAsset[i].photoType === 1) {
              photoList.push(photoAsset[i]);
            }
          }
        }
      }
    } catch (err) {
      Logger.error('getAssets failed, message = ', err);
    }
    Logger.info('photoList success');
    return photoList;
  }

  async splitPic(index: number): Promise<PictureItem[]> {
    let imagePixelMap: PictureItem[] = [];
    let imagesData: Array<photoAccessHelper.PhotoAsset> = await this.getAllImg();
    let imagePackerApi = image.createImagePacker();
    fileIo.open(imagesData[index].uri, fileIo.OpenMode.READ_ONLY).then(async (file: fileIo.File) => {
      let fd: number = file.fd;
      let imageSource = image.createImageSource(fd);
      let imageInfo = await imageSource.getImageInfo();
      Logger.info(TAG, `sizeImg createImageSource ${JSON.stringify(imageSource)}`);
      let height = imageInfo.size.height / Common.SPLIT_COUNT;
      for (let i = 0; i < Common.SPLIT_COUNT; i++) {
        for (let j = 0; j < Common.SPLIT_COUNT; j++) {
          let picItem: PictureItem;
          if (i === Common.SPLIT_COUNT - 1 && j === Common.SPLIT_COUNT - 1) {
            picItem = new PictureItem(Common.PICTURE_ITEM_NUMBER, {} as image.PixelMap);
            imagePixelMap.push(picItem);
          } else {
            Logger.info(TAG, `sizeImg x = ${imageInfo.size.width / Common.SPLIT_COUNT} y = ${height}`);
            let decodingOptions: image.DecodingOptions = {
              desiredRegion: {
                size: {
                  height: height,
                  width: imageInfo.size.width / Common.SPLIT_COUNT
                }, x: j * imageInfo.size.width / Common.SPLIT_COUNT, y: i * height
              }
            }
            imagePixelMap.push(
              new PictureItem(i * Common.SPLIT_COUNT + j, await imageSource.createPixelMap(decodingOptions)));
          }
        }
      }
      imagePackerApi.release();
      fileIo.closeSync(fd);
    })
    return imagePixelMap;
  }
}
  • 获取本地图片:首先使用getPhotoAccessHelper获取相册管理模块实例,然后使用getAssets方法获取文件资源,最后使用getAllObjects获取检索结果中的所有文件资产方便展示;
  • 裁剪图片准备:裁剪图片需要使用@ohos.multimedia.image接口,裁剪前需要申请图片编辑权限,使用requestPermissionsFromUser申请,源码参考[Index.ets];
/*
 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { mediaquery } from '@kit.ArkUI';
import { Permissions, abilityAccessCtrl } from '@kit.AbilityKit';
import { emitter } from '@kit.BasicServicesKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import GameRules from '../model/GameRules';
import ImagePicker from '../common/ImagePicker';
import Logger from '../model/Logger';
import PictureItem from '../model/PictureItem';
import ImageModel from '../model/ImageModel';
import { CommonConstants as Common } from '../common/CommonConstants';

const PERMISSIONS: Array<Permissions> = [
  'ohos.permission.READ_MEDIA',
  'ohos.permission.WRITE_MEDIA',
  'ohos.permission.MEDIA_LOCATION',
  'ohos.permission.MANAGE_MISSIONS'
];

@Entry
@Component
struct Index {
  @State numArray: PictureItem[] = [];
  @State imgData: Array<photoAccessHelper.PhotoAsset> = [];
  @State @Watch('onTimeOver') gameTime: number = Common.GAME_TIME;
  @State @Watch('onImageChange') index: number = Common.ZERO;
  @State isLand: boolean = false;
  @StorageLink('isGameStart') isGameStart: boolean = false;
  private listener = mediaquery.matchMediaSync('screen and (min-aspect-ratio: 1.5) or (orientation: landscape)');
  private ImageModel: ImageModel = new ImageModel(getContext(this));
  private game: GameRules = new GameRules();
  private timer: number = -1;
  private isRefresh: boolean = false;

  onLand = (mediaQueryResult: mediaquery.MediaQueryResult) => {
    Logger.info(`[eTSMediaQuery.Index]onLand: mediaQueryResult.matches=${mediaQueryResult.matches}`);
    this.isLand = mediaQueryResult.matches;
  };
  over = () => {
    Logger.info(Common.TAG, 'emitter on , eventID = 0');
    for (let i = Common.ZERO; i < Common.LOOP; i++) {
      this.numArray[i].index = i;
    }
    this.gameOver();
  };
  timeEnd = () => {
    Logger.info(Common.TAG, 'emitter on , eventID = 1');
    this.gameTime = Common.ZERO;
  };

  async aboutToAppear() {
    this.listener.on('change', this.onLand);
    await abilityAccessCtrl.createAtManager().requestPermissionsFromUser(getContext(this), PERMISSIONS);
    this.imgData = await this.ImageModel.getAllImg();
    Logger.info(Common.TAG, `images = ${this.imgData.length}`);
    this.numArray = await this.ImageModel.splitPic(this.index);
    // Test case. The game is simulated successfully.
    emitter.on({ eventId: Common.ZERO, priority: Common.ZERO }, this.over);
    // Test case. End of simulation time.
    emitter.on({ eventId: Common.ONE, priority: Common.ZERO }, this.timeEnd);
  }

  onTimeOver() {
    if (this.gameTime === Common.ZERO) {
      this.isGameStart = false;
      AlertDialog.show({ message: 'TimeOver' });
      clearInterval(this.timer);
    }
  }

  async onImageChange() {
    this.isRefresh = true;
    this.dialogController.close();
    this.numArray = [];
    this.numArray = await this.ImageModel.splitPic(this.index);
    this.init();
    this.isGameStart = false;
    this.isRefresh = false;
  }

  init() {
    this.gameTime = Common.GAME_TIME;
    clearInterval(this.timer);
  }

  gameOver() {
    let count = Common.ZERO;
    for (let i = Common.ZERO; i < Common.LOOP; i++) {
      if (this.numArray[i].index === i) {
        count++;
      } else {
        count = Common.ZERO;
        break;
      }
    }
    if (count === Common.LOOP) {
      this.isGameStart = false;
      AlertDialog.show({ message: $r('app.string.congratulations') });
      clearInterval(this.timer);
      this.gameTime = Common.GAME_TIME;
    }
  }

  start() {
    this.init();
    this.timer = setInterval(() => {
      this.gameTime--;
    }, Common.TIME_NUMBER)
  }

  dialogController: CustomDialogController = new CustomDialogController({
    builder: ImagePicker({
      imagesData: this.imgData,
      index: $index
    }),
    autoCancel: true,
    gridCount: Common.GRID_COUNT
  })

  @Builder
  ImageShow() {
    Image(this.imgData[this.index].uri)
      .id(Common.IMAGE_SHOW)
      .width(Common.IMAGE_WIDTH)
      .height($r('app.float.imageShow_height'))
      .objectFit(ImageFit.Fill)
      .onClick(async () => {
        if (this.isRefresh) {
          return;
        }
        this.imgData = await this.ImageModel.getAllImg();
        setTimeout(() => {
          this.dialogController.open();
        }, Common.TIME);
      })
  }

  @Builder
  ImageGrid(leftMargin: number, topMargin: number) {
    Grid() {
      ForEach(this.numArray, (item: PictureItem, index) => {
        GridItem() {
          Image(item.pixelMap)
            .width(Common.GRID_IMAGE_WIDTH)
            .objectFit(ImageFit.Fill)
            .height($r('app.float.grid_image_height'))
        }
        .id(`image${index}`)
        .backgroundColor(item.pixelMap === undefined ? $r('app.color.blank_picture_background') : $r('app.color.picture_background'))
        .onClick(() => {
          if (this.isRefresh) {
            return;
          }
          if (this.isGameStart) {
            this.isRefresh = true;
            this.numArray = this.game.gameInit(index, this.numArray);
            this.gameOver();
            this.isRefresh = false;
          }
        })
      }, (item: PictureItem) => JSON.stringify(item))
    }
    .id(Common.IMAGE_GRID)
    .columnsTemplate(Common.COLUMN_TEMPLATE)
    .columnsGap($r('app.float.gap'))
    .rowsGap($r('app.float.gap'))
    .width(Common.IMAGE_WIDTH)
    .height($r('app.float.grid_height'))
    .margin({ left: leftMargin, top: topMargin })
  }

  build() {
    Column() {
      Row() {
        Text(`Time:0${Math.floor(this.gameTime / Common.SECOND)}:${this.gameTime % Common.SECOND < Common.TEN
          ? Common.STRING_ZERO + this.gameTime % Common.SECOND : this.gameTime % Common.SECOND}`)
          .id(Common.TIME_STRING)
          .margin({ top: Common.MARGIN, bottom: Common.MARGIN })
      }

      if (this.imgData.length > Common.ZERO) {
        if (this.isLand) {
          Row() {
            this.ImageShow()
            this.ImageGrid(Common.TEN, Common.ZERO)
          }
          .margin({ top: Common.MARGIN })
        } else {
          Column() {
            this.ImageShow()
            this.ImageGrid(Common.ZERO, Common.FIVE)
          }
          .margin({ top: Common.MARGIN })
        }
      }
      Button($r('app.string.start'), { type: ButtonType.Capsule, stateEffect: true })
        .id(Common.START_BUTTON)
        .height($r('app.float.button_height'))
        .width(Common.FULL_WIDTH)
        .fontSize($r('app.float.button_font_size'))
        .margin({ top: Common.MARGIN })
        .backgroundColor(this.isGameStart ? $r('app.color.forbid') : $r('app.color.allow'))
        .enabled(!this.isGameStart)
        .onClick(() => {
          this.isGameStart = true;
          this.start();
          this.numArray = this.game.gameBegin(this.numArray);
        })

      Button($r('app.string.restart'), { type: ButtonType.Capsule, stateEffect: true })
        .id(Common.RESTART_BUTTON)
        .height($r('app.float.button_height'))
        .width(Common.FULL_WIDTH)
        .fontSize($r('app.float.button_font_size'))
        .margin({ top: Common.MARGIN })
        .backgroundColor(this.isGameStart ? $r('app.color.allow') : $r('app.color.forbid'))
        .enabled(this.isGameStart)
        .onClick(() => {
          this.isGameStart = true;
          this.start();
          this.numArray = this.game.gameBegin(this.numArray);
        })
    }
    .width(Common.FULL_WIDTH)
    .height(Common.FULL_HEIGHT)
    .padding({ left: Common.PERCENT, right: Common.PERCENT })
  }
}
  • 图片编辑:首先使用createImagePacker创建ImagePacker实例,然后使用fs.open打开文件,调用createImageSource接口创建图片源实例方便操作图片,接下来使用getImageInfo方法获取图片大小便于分割,最后使用createPixelMap方法传入每一份的尺寸参数完成图片裁剪。

以上就是本篇文章所带来的鸿蒙开发中一小部分技术讲解;想要学习完整的鸿蒙全栈技术。可以在结尾找我可全部拿到!
下面是鸿蒙的完整学习路线,展示如下:
1

除此之外,根据这个学习鸿蒙全栈学习路线,也附带一整套完整的学习【文档+视频】,内容包含如下

内容包含了:(ArkTS、ArkUI、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、鸿蒙南向开发、鸿蒙项目实战)等技术知识点。帮助大家在学习鸿蒙路上快速成长!

鸿蒙【北向应用开发+南向系统层开发】文档

鸿蒙【基础+实战项目】视频

鸿蒙面经

在这里插入图片描述

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1976462.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Animate软件基础:关于补间动画中的图层

Animate 文档中的每一个场景都可以包含任意数量的时间轴图层。使用图层和图层文件夹可组织动画序列的内容和分隔动画对象。在图层和文件夹中组织它们可防止它们在重叠时相互擦除、连接或分段。若要创建一次包含多个元件或文本字段的补间移动的动画&#xff0c;请将每个对象放置…

go 中 string 并发写导致的 panic

类型的一点变化 在Go语言的演化过程中&#xff0c;引入了unsafe.String来取代之前的StringHeader结构体&#xff0c;这是为了提供更安全和简洁的字符串操作方式。 旧设计 (StringHeader 结构体) StringHeader注释发生了一点变动&#xff0c;被标注了 Deprecated&#xff0c;…

谷粒商城实战笔记-103~104-全文检索-ElasticSearch-Docker安装ES和Kibana

文章目录 一&#xff0c;103-全文检索-ElasticSearch-Docker安装ES1&#xff0c;下载镜像文件2&#xff0c;Elasticsearch安装3&#xff0c;验证 二&#xff0c;104-全文检索-ElasticSearch-Docker安装Kibana1&#xff0c;下载镜像文件2&#xff0c;kibana的安装3&#xff0c;验…

【数据结构算法经典题目刨析(c语言)】环形链表的约瑟夫问题(图文详解)

&#x1f493; 博客主页&#xff1a;C-SDN花园GGbond ⏩ 文章专栏&#xff1a;数据结构经典题目刨析(c语言) 一.前言&#xff1a; 前言——著名的Josephus问题 据说著名犹太 Josephus有过以下的故事&#xff1a;在罗⻢⼈占领乔塔帕特后&#xff0c;39个犹太⼈与Josephus及他…

ansible 配置yum源

ansible配置yum源 有两种方式&#xff0c;一种是可以写好sh脚本&#xff0c;然后ansible去执行sh文件 另外一种就是使用yum_repository库 本文讲使用库的方式 本文使用的环境是centos7 &#xff0c;配置也是按照7去配置的&#xff0c;没有写动态配置 直接上代码 [rootvm-2 ~…

《技术人求职之道》之求职机遇篇:多渠道并进,如何高效获取面试机会

摘要 本文探讨了求职过程中获取面试机会的多种途径,强调简历优化的重要性,并指出了不同求职方式的优劣及其适用情况。文章首先介绍通过企业挖掘、内部推荐、猎头服务、社会招聘和校园招聘等途径获得面试机会的方法,并根据成功率和适用性为这些方法排序。然后,详细讨论了每…

门控循环单元GRU

目录 一、GRU提出的背景&#xff1a;1.RNN存在的问题&#xff1a;2.GRU的思想&#xff1a; 二、更新门和重置门&#xff1a;三、GRU网络架构&#xff1a;1.更新门和重置门如何发挥作用&#xff1a;1.1候选隐藏状态H~t&#xff1a;1.2隐藏状态Ht&#xff1a; 2.GRU: 四、训练过程…

当自回归遇到Diffusion

文章目录 Autoregressive Image Generation without Vector Quantization一. 简介1.1 摘要1.1 引言二.相关工作2.1 Sequence Models for Image Generation2.2 Diffusion for Representation Learning2.3 Diffusion for Policy Learning三.方法3.1 重新思考离散值的tokens3.2 Di…

Kotlin OpenCV 图像图像50 Haar 级联分类器模型

Kotlin OpenCV 图像图像50 Haar 级联分类器模型 1 OpenCV Haar 级联分类器模型2 Kotlin OpenCV Haar 测试代码 1 OpenCV Haar 级联分类器模型 Haar级联分类器是一种用于对象检测&#xff08;如人脸检测&#xff09;的机器学习算法。它由Paul Viola和Michael Jones在2001年提出…

conda环境pip 安装Tensorflow-gpu 2.10.2提示nbconvert 的包依赖冲突

问题如下&#xff1a; ERROR: pip’s dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts. nbconvert 7.16.4 requires beautifulsoup4, which is not inst…

DETR论文详解

文章目录 前言一、DETR理论二、模型架构1. CNN2. Transformer3. FFN 三、损失函数四、代码实现总结 前言 DETR是Facebook团队在2020年提出的一篇论文&#xff0c;名字叫做《End-to-End Object Detection with Transformers》端到端的基于Transformers的目标检测&#xff0c;DET…

数仓入门:数据分析模型、数仓建模、离线实时数仓、Lambda、Kappa、湖仓一体

往期推荐 大数据HBase图文简介-CSDN博客 数仓分层ODS、DWD、DWM、DWS、DIM、DM、ADS-CSDN博客 数仓常见名词解析和名词之间的关系-CSDN博客 目录 0. 前言 0.1 浅谈维度建模 0.2 数据分析模型 1. 何为数据仓库 1.1 为什么不直接用业务平台的数据而要建设数仓&#xff1f; …

ChatGPT能代替网络作家吗?

最强AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频百万播放量https://aitools.jurilu.com/ 当然可以&#xff01;只要你玩写作AI玩得6&#xff0c;甚至可以达到某些大神的水平&#xff01; 看看大神、小白、AI输出内容的区…

重塑企业知识库:AI搜索的深度应用与变革

在数字化浪潮的推动下&#xff0c;企业知识库已成为企业智慧的核心载体。而AI搜索技术的融入&#xff0c;让海量信息瞬间变得井然有序&#xff0c;触手可及。它不仅革新了传统的搜索方式&#xff0c;更开启了企业知识管理的新纪元&#xff0c;引领着企业向更加智能化、高效化的…

【人工智能】FPGA实现人工智能算法硬件加速学习笔记

一. FPGA的优势 FPGA拥有高度的重配置性和并行处理能力,能够同时处理多个运算单元和多个数据并行操作。FPGA与卷积神经网络(CNN)的结合,有助于提升CNN的部署效率和性能。由于FPGA功耗很低的特性进一步增强了其吸引力。此外,FPGA可以根据具体算法需求量身打造硬件加速器。针对动…

[CR]厚云填补_SEGDNet

Structure-transferring edge-enhanced grid dehazing network Abstract 在过去的二十年里&#xff0c;图像去雾问题在计算机视觉界受到了极大的关注。在雾霾条件下&#xff0c;由于空气中水汽和粉尘颗粒的散射&#xff0c;图像的清晰度严重降低&#xff0c;使得许多计算机视觉…

鸿蒙媒体开发【基于AVCodec能力的视频编解码】音频和视频

基于AVCodec能力的视频编解码 介绍 本实例基于AVCodec能力&#xff0c;提供基于视频编解码的视频播放和录制的功能。 视频播放的主要流程是将视频文件通过解封装->解码->送显/播放。视频录制的主要流程是相机采集->编码->封装成mp4文件。 播放支持的原子能力规…

【从0到1进阶Redis】Jedis 操作 Redis

笔记内容来自B站博主《遇见狂神说》&#xff1a;Redis视频链接 Jedis 是一个用于 Java 的 Redis 客户端库&#xff0c;它提供了一组 API 用于与 Redis 数据库进行交互。Redis 是一个高性能的键值存储数据库&#xff0c;广泛用于缓存、消息队列等场景。Jedis 使得 Java 开发者能…

图欧科技-IMYAI智能助手24年5月~7月更新日志大汇总

上一篇推文盘点了我们图欧科技团队近一年来的更新日志&#xff0c;可以说是跟随着人工智能时代的发展&#xff0c;我们的IMYAI也丝毫不落后于这场时代的浪潮&#xff01;近三个月以来&#xff0c;我们的更新频率直线上升&#xff0c;现在我们AI网站已经成为一个集GPT、Claude、…

《学会 SpringMVC 系列 · 消息转换器 MessageConverters》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…