九宮格切圖-創(chuàng)意分享新風(fēng)尚

作者:狼哥
團(tuán)隊:堅果派
團(tuán)隊介紹:堅果派由堅果等人創(chuàng)建,團(tuán)隊擁有12個華為HDE帶領(lǐng)熱愛HarmonyOS/OpenHarmony的開發(fā)者,以及若干其他領(lǐng)域的三十余位萬粉博主運營。專注于分享HarmonyOS/OpenHarmony、ArkUI-X、元服務(wù)、倉頡。團(tuán)隊成員聚集在北京,上海,南京,深圳,廣州,寧夏等地,目前已開發(fā)鴻蒙原生應(yīng)用,三方庫60+,歡迎交流。

介紹

    在社交媒體日益繁榮的今天,九宮格切圖以其獨特的視覺呈現(xiàn)方式,成為了朋友圈中的一股清新之風(fēng)。通過將一張完整圖片精心切割為九個小方塊,再依次排列發(fā)布,不僅讓圖片內(nèi)容更加層次分明,還能激發(fā)觀者的探索欲,引導(dǎo)他們逐格瀏覽,享受發(fā)現(xiàn)新細(xì)節(jié)的樂趣。

   九宮格圖片的用處廣泛而巧妙。它適用于旅行美景的展示,每一格都是一處風(fēng)景的縮影,串聯(lián)起一段完整的旅程記憶;也是美食分享的絕佳選擇,從食材準(zhǔn)備到成品呈現(xiàn),步步精彩,讓人垂涎欲滴;更可用于生活日常的創(chuàng)意記錄,無論是溫馨的家庭瞬間,還是個人的小確幸,都能在九宮格的框架下,被賦予更多故事性和觀賞性。這種創(chuàng)意切圖方式,讓每一次分享都變得更加有趣和生動,是連接你我,傳遞美好情感的新橋梁。

效果預(yù)覽

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

工程目錄

├──entry/src/main/ets                         // 代碼區(qū)
│  ├──dialog
│  │  └──ImagePicker.ets                      // 圖片選擇
│  ├──entryability
│  │  └──EntryAbility.ets 
│  ├──model
│  │  ├──ImageModel.ets                       // 圖片操作
│  │  └──PictureItem.ets                      // 圖片對象
│  └──pages
│     └──Index.ets                            // 首頁
└──entry/src/main/resources                   // 應(yīng)用資源目錄

具體實現(xiàn)

1. 權(quán)限添加

配置文件module.json5里添加讀取圖片及視頻權(quán)限和修改圖片或視頻權(quán)限。

"requestPermissions": [
      {
        "name": "ohos.permission.WRITE_MEDIA",
        "usedScene": {
          "abilities": [
            "EntryAbility"
          ],
          "when": "inuse"
        },
        "reason": "$string:WRITE_MEDIA"
      },
      {
        "name": "ohos.permission.MEDIA_LOCATION",
        "usedScene": {
          "abilities": [
            "EntryAbility"
          ],
          "when": "inuse"
        },
        "reason": "$string:MEDIA_LOCATION"
      },
      {
        "name": "ohos.permission.READ_IMAGEVIDEO",
        "usedScene": {
          "abilities": [
            "EntryAbility"
          ],
          "when": "inuse"
        },
        "reason": "$string:READ_IMAGEVIDEO"
      },
      {
        "name": "ohos.permission.WRITE_IMAGEVIDEO",
        "usedScene": {
          "abilities": [
            "EntryAbility"
          ],
          "when": "inuse"
        },
        "reason": "$string:WRITE_IMAGEVIDEO"
      }
    ]
2. 圖片選擇對話

獲取本地圖片:首先使用getPhotoAccessHelper獲取相冊管理模塊實例,然后使用getAssets方法獲取文件資源,最后使用getAllObjects獲取檢索結(jié)果中的所有文件資產(chǎn)方便展示;

    let photoList: Array<photoAccessHelper.PhotoAsset> = [];
  
    let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
    let fetchOptions: photoAccessHelper.FetchOptions = {
      fetchColumns: [],
      predicates: predicates
    }

    let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> = await this.phAccessHelper.getAssets(fetchOptions);
    if (fetchResult != undefined) {
      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]);
          }
        }
      }
    }

自定義對話框顯示獲取到的本地圖片

import { photoAccessHelper } from '@kit.MediaLibraryKit';

@CustomDialog
export struct ImagePicker {
  @Link index: number;
  private imagesData: Array<photoAccessHelper.PhotoAsset> = [];
  public controller: CustomDialogController;
  @State selected: number = 0;

  build() {
    Column() {
      List({ space: 5 }) {
        ForEach(this.imagesData, (item: photoAccessHelper.PhotoAsset, index) => {
          ListItem() {
            Stack({ alignContent: Alignment.TopEnd }) {...}
        }, (item: photoAccessHelper.PhotoAsset) => JSON.stringify(item))
      }
      .width('95%')
      .height(160)
      .listDirection(Axis.Horizontal)

      Row() {...}
      .margin({ bottom: 10, top: 10 })
    }
    .width('100%')
    .padding({ top: 16, left: 16, right: 16 })
  }
}
3. 切圖九宮格

使用createImagePacker創(chuàng)建ImagePacker實例,然后使用fs.open打開文件,調(diào)用createImageSource接口創(chuàng)建圖片源實例方便操作圖片,接下來使用getImageInfo方法獲取圖片大小便于分割,最后使用createPixelMap方法傳入每一份的尺寸參數(shù)完成圖片裁剪。具體就是根據(jù)圖片選擇對話框,選擇的下標(biāo),到圖庫里獲取到選擇的圖片,然后以只讀方式打開圖片,獲取打開圖片信息,計算出切圖后的寬度和高度,根據(jù)參數(shù)生成新切圖,并緩存到數(shù)組里,方便顯示切圖后的九宮格,最后調(diào)用存儲函數(shù)把切圖存儲到圖庫里,方便之后使用,比如發(fā)朋友圖。

async splitPic(index: number): Promise<Array<PictureItem>> {
    // 調(diào)用上面函數(shù)獲取全部圖片
    let imagesData: Array<photoAccessHelper.PhotoAsset> = await this.getAllImg();
    console.info(`xx testTag splitPic 圖庫圖片數(shù)量為: ${imagesData.length}`)
    let imagePixelMap: Array<PictureItem> = [];
    // 創(chuàng)建圖像編碼ImagePacker對象
    let imagePickerApi = image.createImagePacker();

    // 以只讀方式打開指定下標(biāo)圖片
    await 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();
      // 圖片高度除以3,就是把圖片切為3份
      let height = imageInfo.size.height / this.splitCount;
      let width = imageInfo.size.width / this.splitCount;
      // 切換為 3x3 張圖片
      for (let i = 0; i < this.splitCount; i++) {
        for (let j = 0; j < this.splitCount; j++) {
          // 設(shè)置解碼參數(shù)DecodingOptions,解碼獲取pixelMap圖片對象
          let decodingOptions: image.DecodingOptions = {
            desiredRegion: {
              size: {
                height: height, // 切開圖片高度
                width: width  // 切開圖片寬度
              },
              x: j * width,   // 切開x起始位置
              y: i * height     // 切開y起始位置
            }
          }
          // 根據(jù)參數(shù)重新九宮格圖片
          let img: image.PixelMap = await imageSource.createPixelMap(decodingOptions);
          // 把生成新圖片放到內(nèi)存里
          imagePixelMap.push(new PictureItem(i * this.splitCount + j, img));
          // 保存到相冊
          await this.savePixelMap(img)
        }
      }

      imagePickerApi.release();
      fileIo.closeSync(fd);
    })

    return imagePixelMap;
  }
4. 保存圖庫

把上面切出來的PixelMap先轉(zhuǎn)為ArrayBuffer,然后通過PhotoAccessHelper模塊提供相冊管理模塊能力,包括創(chuàng)建相冊以及訪問、修改相冊中的媒體數(shù)據(jù)信息等。把ArrayBuffer保存到圖庫里。

async savePixelMap(pm: PixelMap) {
    if (this.phAccessHelper === null) {
      return;
    }
    const imagePackerApi: image.ImagePacker = image.createImagePacker();
    const packOpts: image.PackingOption = { format: 'image/jpeg', quality: 30 };
    try {
      const buffer: ArrayBuffer = await imagePackerApi.packing(pm, packOpts);
      let options: photoAccessHelper.CreateOptions = {
        title: new Date().getTime().toString()
      };

      let photoUri: string = await this.phAccessHelper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg', options);
      let file: fileIo.File = fileIo.openSync(photoUri, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
      await fileIo.write(file.fd, buffer);
      fileIo.closeSync(file);
    } catch (err) {
      console.error(err)
    }
  }
5. 界面布局

頂部和底部顯示紅色說明文字,上面默認(rèn)顯示圖庫第一張圖上,點擊圖片彈出對話框選擇圖庫里的其它圖片,下來是個切割九宮格按鈕,點擊可以把選擇的圖片切割為九張圖片,并自動保存到圖庫里。

Column() {
      Text(`默認(rèn)顯示圖庫第一張圖片、點擊圖片彈出圖庫對話框、選擇一張希望切九宮格圖片!`)
        .fontSize(10)
        .fontColor(Color.Red)
      Image(this.imgData[this.index]?.uri)
        .objectFit(ImageFit.Contain)
        .width('100%')
        .aspectRatio(1)
        .margin(20)
        .onClick(async () => {
          this.imagePixelMap = [];
          this.imgData = await this.imageModel.getAllImg();
          setTimeout(() => {
            this.dialogController.open();
          }, 200);
        })

      Stack() {
        Divider()
          .width('100%')
          .color(Color.Orange)
        Button('切割九宮格')
          .onClick(async () => {
            this.imagePixelMap = [];
            this.imagePixelMap = await this.imageModel.splitPic(this.index);
          })
      }
      .width('100%')
      .height(30)

      Grid() {
        ForEach(this.imagePixelMap, (item: PictureItem, index:number) => {
          GridItem() {
            Image(item.pixelMap)
              .width('99%')
              .objectFit(ImageFit.Fill)
              .height(90)
          }
          .backgroundColor(item.pixelMap === undefined ? '#f5f5f5' : '#ffdead')
        }, (item: PictureItem) => JSON.stringify(item))
      }
      .columnsTemplate('1fr 1fr 1fr')
      .columnsGap(2)
      .rowsGap(2)
      .backgroundColor('#fff')
      .width('100%')
      .aspectRatio(1)
      .margin(20)
      .layoutWeight(1)

      Text(`上面九宮格圖片已保存到圖庫、請移步到圖庫發(fā)一個不一樣的朋友圈吧!`)
        .fontSize(10)
        .fontColor(Color.Red)
    }
    .height('100%')
    .width('100%')
    .padding(20)
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Start)
6. 權(quán)限申請

在頁面生命周期aboutToAppear函數(shù)時,調(diào)用權(quán)限申請,并獲取圖庫數(shù)據(jù)。

const PERMISSIONS: Array<Permissions> = [
  'ohos.permission.READ_MEDIA',
  'ohos.permission.WRITE_MEDIA',
  'ohos.permission.MEDIA_LOCATION',
  'ohos.permission.MANAGE_MISSIONS'
];
async aboutToAppear() {
    await abilityAccessCtrl.createAtManager().requestPermissionsFromUser(getContext(this), PERMISSIONS);
    this.imgData = await this.imageModel.getAllImg();
  }

相關(guān)權(quán)限

讀取圖片及視頻權(quán)限:ohos.permission.READ_IMAGEVIDEO

修改圖片或視頻權(quán)限:ohos.permission.WRITE_IMAGEVIDEO

約束與限制

1.本示例僅支持標(biāo)準(zhǔn)系統(tǒng)上運行,支持設(shè)備:華為手機(jī)。

2.HarmonyOS系統(tǒng):HarmonyOS NEXT Developer Beta1及以上。

3.DevEco Studio版本:DevEco Studio NEXT Developer Beta1及以上。

4.HarmonyOS SDK版本:HarmonyOS NEXT Developer Beta1 SDK及以上。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容