HarmonyOS 鸿蒙Next 自动截屏

发布于 1周前 作者 zlyuanteng 来自 鸿蒙OS

HarmonyOS 鸿蒙Next 自动截屏

如何在原生里面自动截屏保存到相册,如何在flutter中点击一个按钮的时候调用这个截屏

12 回复

方案:使用methodChannel

1.创建methodChannel,

// plugin.ets  
onMethodCall(call: MethodCall, result: MethodResult): void {
    if (call.method == "saveImage") {
      // 实现截屏和保存相册
      result.success("OpenHarmony ^ ^ ")
    } else {
      result.notImplemented();
    }
  }

2.用原生的API实现截屏和保存图片

可以参考这个链接:https://developer.huawei.com/consumer/cn/blog/topic/03166979994620019

3.flutter端通过methodChannel去调用通信方法即可。

// main.dart

final methodChannel = const MethodChannel('save_image');

  [@override](/user/override)
  Future<void?> saveImage() async {
     await methodChannel.invokeMethod<String>('saveImage');
  }

参考:

methodChannel: https://gitee.com/openharmony-sig/flutter_samples/blob/master/ohos/docs/04_development/如何使用Flutter与鸿蒙通信 FlutterChannel.md

更多关于HarmonyOS 鸿蒙Next 自动截屏的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


截图保存demo如下,

保存相册用到的api (phAccessHelper.showAssetsCreationDialog)需要授权允许保存,没有用到ACL权限,该权限一般也不让申请。:

// main.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Channel Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const Scaffold(
        body: PluginPage(),
      ),
    );
  }
}

class PluginPage extends StatefulWidget {
  const PluginPage({super.key});

  @override
  State<PluginPage> createState() => _PluginPageState();
}

class _PluginPageState extends State<PluginPage> {
  final _platform = const MethodChannel('samples.flutter.dev/screenSave');
  int count = 0;

  @override
  void initState() {
    super.initState();
    _initListener();
  }

  _initListener() {
  }

  Future<void> _screenSave() async {
    
    try {
      // 调用需要在平台中实现的方法
     await _platform.invokeMethod<int>('screenSave');
     
    } on PlatformException catch (e) {
     
    }

    setState(() {
      
    });
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text('test'),
          ElevatedButton(
            onPressed: () => _screenSave(),
            child: const Text("screenSave"),
          ),
        ],
      ),
    );
  }
}

// index.ets

import common from '@ohos.app.ability.common';
import { FlutterPage } from '@ohos/flutter_ohos'

let storage = LocalStorage.getShared()
const EVENT_BACK_PRESS = 'EVENT_BACK_PRESS'

@Entry(storage)
@Component
struct Index {
  private context = getContext(this) as common.UIAbilityContext
  @LocalStorageLink('viewId') viewId: string = "";

  build() {
    Column() {
      FlutterPage({ viewId: this.viewId })
    }
  }

  onBackPress(): boolean {
    this.context.eventHub.emit(EVENT_BACK_PRESS)
    return true
  }
}
// EntryAbility.ets
import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos';
import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';
import ScreenSavePlugin from '../plugins/ScreenSavePlugin';

export default class EntryAbility extends FlutterAbility {
  configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    GeneratedPluginRegistrant.registerWith(flutterEngine)
    this.addPlugin(new ScreenSavePlugin());
  }
}

// ets/plugins/ScreenSavePlugin.ets

import {
  FlutterPlugin,
  Log,
  MethodCall,
  MethodChannel,
} from '@ohos/flutter_ohos';
import { FlutterPluginBinding } from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin';
import { BusinessError } from '[@kit](/user/kit).BasicServicesKit';
import { MethodResult } from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodChannel';
import { window } from '[@kit](/user/kit).ArkUI';
import { image } from '[@kit](/user/kit).ImageKit';
import { PixelMapUtil } from '../util/PixelMapUtil';

const TAG = "ScreenSavePluginTag";

export default class ScreenSavePlugin implements FlutterPlugin {
  private channel?: MethodChannel;
  private api = new ScreenSaveApi();

  onAttachedToEngine(binding: FlutterPluginBinding): void {
    this.channel = new MethodChannel(binding.getBinaryMessenger(), "samples.flutter.dev/screenSave");
    let that = this;
    this.channel.setMethodCallHandler({
      onMethodCall(call: MethodCall, result: MethodResult) {
        switch (call.method) {
          case "screenSave":
            that.api.screenSave(result)
            break;
          default:
            result.notImplemented();
            break;
        }
      }
    })
  }

  onDetachedFromEngine(binding: FlutterPluginBinding): void {
    Log.i(TAG, "onDetachedFromEngine");
    this.channel?.setMethodCallHandler(null);
  }
  getUniqueClassName(): string {
    return "ScreenSavePlugin";
  }
}
class ScreenSaveApi {
  screenSave(result: MethodResult) {
    let fileName: string = new Date().getMilliseconds().toString()
    let windowClass: window.Window | undefined = undefined

    //获取当前应用内最上层的子窗口,若无应用子窗口,则返回应用主窗口
    let mainWindow = window.getLastWindow(getContext(this))
    mainWindow.then((data) => {
      windowClass = data

      //屏幕截图
      let snapshot = windowClass.snapshot();
      snapshot.then(async (pickInfo: image.PixelMap) => {
        //把PixelMap保存到相册
        const imagePackerApi = image.createImagePacker()
        const IMAGE_QUALITY = 100
        const packOpts: image.PackingOption = { format: "image/jpeg", quality: IMAGE_QUALITY }
        let compressedImageData: ArrayBuffer = await imagePackerApi.packing(pickInfo, packOpts)

        // 调用保存图片工具类方法
        PixelMapUtil.savePicToAlbum(compressedImageData, fileName)
        // PixelMap使用完后及时释放内存
        pickInfo.release()

      }).catch((err: BusinessError) => {
      })
    }).catch((err: BusinessError) => {
      console.error(TAG, `Failed to obtain the top window. Cause code: ${err.code}, message: ${err.message}`);
    })

    result.success(null);
  }
}

// ets/util/PixelMapUtil.ets

import { common } from "[@kit](/user/kit).AbilityKit"
import { fileIo as fs, fileUri, ReadOptions, WriteOptions } from '[@kit](/user/kit).CoreFileKit';
import { photoAccessHelper } from "[@kit](/user/kit).MediaLibraryKit";
import { promptAction } from "[@kit](/user/kit).ArkUI";

const TAG = 'PixelMapUtil'

export class PixelMapUtil {
  //图片保存到相册
  static async savePicToAlbum(base64: ArrayBuffer, fileName: string) {
    PixelMapUtil.writeFileDataToSandBox(base64, fileName + '.jpg').then((path) => {
      PixelMapUtil.savePic(path, fileName)
    })
  }

  public static writeFileDataToSandBox(fileData: ArrayBuffer, fileName: string): Promise<string> {
    return new Promise((resolve, reject) => {
      let context = getContext(PixelMapUtil) as common.UIAbilityContext
      let cacheDir = context.filesDir
      let filePath = cacheDir + "/textDir";
      if (!fs.accessSync(filePath)) {
        fs.mkdirSync(filePath)
      }

      let cachePath = filePath + '/' + fileName
      let file = fs.openSync(cachePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
      console.info(TAG, "file fd: " + file.fd)
      let writeLen = fs.writeSync(file.fd, fileData)
      console.info(TAG, "write data to file succeed and size is:" + writeLen)
      fs.closeSync(file)
      resolve(cachePath)
    })
  }


  static async savePic(filePath: string, fileName: string) {
    console.info(TAG, 'ShowAssetsCreationDialogDemo.');
    let context = getContext(PixelMapUtil) as common.UIAbilityContext
    let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
    try {
      let srcFileUri: Array<string> = [fileUri.getUriFromPath(filePath)]
      console.debug(TAG, `图片 uri is : ${JSON.stringify(srcFileUri)}`)

      let photoCreationConfigs: Array<photoAccessHelper.PhotoCreationConfig> = [
        {
          title: fileName, // 可选
          fileNameExtension: 'jpg',
          photoType: photoAccessHelper.PhotoType.IMAGE,
          subtype: photoAccessHelper.PhotoSubtype.DEFAULT, // 可选
        }
      ];
      console.log(TAG, "syncToSysAlarm fileUri:" + JSON.stringify(srcFileUri) + ",config:" +
      JSON.stringify(photoCreationConfigs))
      let desFileUris: Array<string> = await phAccessHelper.showAssetsCreationDialog(srcFileUri, photoCreationConfigs);
      console.debug(TAG, `目标图片 uri is : ${JSON.stringify(desFileUris)}`)


      if (desFileUris.length > 0) {
        for (let index = 0; index < desFileUris.length; index++) {
          PixelMapUtil.copyFileContentTo(srcFileUri[index], desFileUris[index])
        }
      }

    } catch (err) {
      console.error(TAG, 'showAssetsCreationDialog failed, errCode is ' + err.code + ', errMsg is ' + err.message);
    }
  }


  /**
   * 复制文件内容到目标文件
   * [@param](/user/param) srcFilePath 源文件路径
   * [@param](/user/param) destFilePath 目标文件路径
   */
  static copyFileContentTo(srcFilePath: string, destFilePath: string) {
    let srcFile = fs.openSync(srcFilePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
    let destFile = fs.openSync(destFilePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)

    // 读取源文件内容并写入至目的文件
    let bufSize = 4096;
    let readSize = 0;
    let buf = new ArrayBuffer(bufSize);
    let readOptions: ReadOptions = {
      offset: readSize,
      length: bufSize
    };
    let readLen = fs.readSync(srcFile.fd, buf, readOptions);
    while (readLen > 0) {
      readSize += readLen;
      let writeOptions: WriteOptions = {
        length: readLen
      };
      fs.writeSync(destFile.fd, buf, writeOptions);
      readOptions.offset = readSize;
      readLen = fs.readSync(srcFile.fd, buf, readOptions);
    }
    // 关闭文件
    promptAction.showToast({
      message: '保存成功',
      duration: 1000
    });
    fs.closeSync(srcFile);
    fs.closeSync(destFile);
  }
}
// oh-package.json5
  "dependencies": {
    "@ohos/flutter_ohos": "file:./har/flutter.har"
  },

哥 今天我两和了代码 合完之后我的截屏保存不到相册了 但是他的震动没有问题 然后我这样 Future<void> _screenSave() async { final completer = Completer<void>(); try { // 调用平台方法 print(“截屏开始…”); await _platform.invokeMethod<int>(‘screenSave’); print(“截屏完成…”); completer.complete(); } on PlatformException catch (e) { print(“保存开始…”); completer.completeError(e); print(“保存完成…”); } print(“整体完成…”); return completer.future; } 输出了 print(“截屏开始…”); print(“截屏完成…”); print(“整体完成…”); 但是就是没有保存到相册

找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17

刚开始 我以为是合丢了啥 然后我就重新写了一遍 也是没有保存到相册,关键是用的方法都是一样的啊

哥我是上个要振动和铃声那个同事啊

铃声那个搞定了吗?看他没回复了

你看下这个有用吗?还是需要我整个demo贴出来呢。

找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17

整个demo贴出来 谢谢

HarmonyOS 鸿蒙Next 自动截屏功能通常内置于系统级应用中,通过系统服务实现定时或触发式屏幕截图。以下是一些基本的操作说明:

  1. 系统设置:首先,检查系统设置中的辅助功能或智能辅助选项,查找是否有自动截屏的相关设置。部分版本可能直接提供定时截屏或基于特定事件(如摇晃、特定手势)的截屏功能。

  2. 快捷手势:部分HarmonyOS设备支持通过快捷手势触发截屏,如同时按下电源键和音量减小键。若需自动执行,可考虑使用第三方自动化工具模拟该手势,但需注意兼容性和权限问题。

  3. 第三方应用:安装支持HarmonyOS的第三方应用,这些应用可能提供更为灵活的自动截屏功能,如定时截图、屏幕变化检测截图等。使用前请确保应用来源可靠,避免隐私泄露。

  4. 任务自动化:利用HarmonyOS的任务自动化功能(如智慧生活APP中的场景设置),设置触发条件和执行动作,实现自动截屏。具体步骤需根据系统版本和应用支持情况调整。

请注意,自动截屏功能可能受系统权限、应用兼容性及安全策略限制。若上述方法无法满足需求,可能是当前系统版本或应用限制所致。如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部