HarmonyOS 鸿蒙Next 自动截屏
HarmonyOS 鸿蒙Next 自动截屏
方案:使用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 自动截屏功能通常内置于系统级应用中,通过系统服务实现定时或触发式屏幕截图。以下是一些基本的操作说明:
-
系统设置:首先,检查系统设置中的辅助功能或智能辅助选项,查找是否有自动截屏的相关设置。部分版本可能直接提供定时截屏或基于特定事件(如摇晃、特定手势)的截屏功能。
-
快捷手势:部分HarmonyOS设备支持通过快捷手势触发截屏,如同时按下电源键和音量减小键。若需自动执行,可考虑使用第三方自动化工具模拟该手势,但需注意兼容性和权限问题。
-
第三方应用:安装支持HarmonyOS的第三方应用,这些应用可能提供更为灵活的自动截屏功能,如定时截图、屏幕变化检测截图等。使用前请确保应用来源可靠,避免隐私泄露。
-
任务自动化:利用HarmonyOS的任务自动化功能(如智慧生活APP中的场景设置),设置触发条件和执行动作,实现自动截屏。具体步骤需根据系统版本和应用支持情况调整。
请注意,自动截屏功能可能受系统权限、应用兼容性及安全策略限制。若上述方法无法满足需求,可能是当前系统版本或应用限制所致。如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html