鸿蒙自定义相机的拍照页面
1、权限申请
"requestPermissions": [{"name": "ohos.permission.CAMERA","reason": "$string:reason_camera","usedScene": {"abilities": ["EntryAbility"]}},{"name": "ohos.permission.MEDIA_LOCATION","reason": "$string:reason_media_location","usedScene": {"abilities": ["EntryAbility"]}}]
2、ui实现
import { dataSharePredicates } from '@kit.ArkData';
import { abilityAccessCtrl, Permissions } from '@kit.AbilityKit';
import { camera } from '@kit.CameraKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { filePreview } from '@kit.PreviewKit';
import { cameraShooting, capture, getUriAsync, previewPhoto, setPhotoFlashMode } from '../utils/CameraShooter';
import display from '@ohos.display';
import { AppStorageV2, curves } from '@kit.ArkUI';
import { sensor } from '@kit.SensorServiceKit';
import { DegreeConstants } from '../constants/DegreeConstants'let cameraPosition = 0;
let surfaceId = '';
let context = getContext(this);@Entry
@ComponentV2
struct XComponentPage {mXComponentController: XComponentController = new XComponentController;permissions: Array<Permissions> = ['ohos.permission.CAMERA','ohos.permission.MEDIA_LOCATION',];@Local cameraMargin: number = 40@Local cameraHeight: number = 2560@Local isRatio: boolean = true; // true: 16 / 9@Local isFront: boolean = false;@Local photoUri: string | Resource | PixelMap = ''@Local isStabilization: boolean = false;@Local isMovingPhoto: boolean = false;@Local flashPic: ResourceStr = ''textTimerController: TextTimerController = new TextTimerController();@Local rotation: number = 0;@Local moreTools: ResourceStr[] = ['相册', '拍照', '拍视频']@BuilderbottomKeystrokeBuilder() {// 底部拍照翻转相机Row() {Image(this.photoUri).borderWidth(this.photoUri === '' ? 0 : 1).borderColor(Color.White).height(70).width(70).borderRadius(35).rotate({ angle: this.rotation }).animation({ curve: curves.springMotion() }).onClick(() => {if (this.photoUri !== '') {previewPhoto(context);}})// 拍照Column() {Column() {}.width(60).height(60).backgroundColor('#0FD7B8').borderRadius(30)}.width(72).height(72).border({ width: 3, color: Color.White }).borderRadius(36).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center).onClick(() => {capture(this.isFront);getUriAsync().then(photoUri => {// photoUri 就是你要的图片uri// 可以赋值给 @State/@Local 变量,UI自动刷新this.photoUri = photoUri;console.log('hlasdlkas===' + this.photoUri)});})Column() {Text('翻转').width(28).onClick(async () => {cameraPosition = cameraPosition === 1 ? 0 : 1cameraShooting(cameraPosition, surfaceId, context, this.isRatio);this.Initialize();this.isFront = cameraPosition !== 0;})}.height(70).width(70).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)}.margin({ top: 20 }).width('100%').justifyContent(FlexAlign.SpaceAround).margin({ bottom: 39 })}@BuilderproportionBuilder() {Row({ space: 20 }) {Text('1:1').height(30).onClick(() => {this.cameraMargin = 100this.cameraHeight = 1440this.isRatio = falsecameraShooting(cameraPosition, surfaceId, context, this.isRatio);this.Initialize();})Text('9:16').height(30).onClick(() => {this.cameraHeight = 2560this.cameraMargin = 40this.isRatio = truecameraShooting(cameraPosition, surfaceId, context, this.isRatio);this.Initialize();})}.margin({ top: 500 })}onPageShow(): void {filePreview.closePreview(context);}async aboutToAppear() {sensor.on(sensor.SensorId.GRAVITY, (data: sensor.GravityResponse) => {let degree: number = -1;degree = this.getCalDegree(data.x, data.y, data.z);if (degree >= 0 && (degree <= DegreeConstants.DEGREE_ONE || degree >= DegreeConstants.DEGREE_FOUR)) {this.rotation = camera.ImageRotation.ROTATION_0;} else if (degree >= DegreeConstants.DEGREE_ONE && degree <= DegreeConstants.DEGREE_TWO) {this.rotation = camera.ImageRotation.ROTATION_270;} else if (degree >= DegreeConstants.DEGREE_TWO && degree <= DegreeConstants.DEGREE_THREE) {this.rotation = camera.ImageRotation.ROTATION_180;} else if (degree >= DegreeConstants.DEGREE_THREE && degree <= DegreeConstants.DEGREE_FOUR) {this.rotation = camera.ImageRotation.ROTATION_90;}})abilityAccessCtrl.createAtManager().requestPermissionsFromUser(context, this.permissions).then(() => {setTimeout(async () => {await cameraShooting(cameraPosition, surfaceId, context, this.isRatio);}, 200);});}aboutToDisappear(): void {sensor.off(sensor.SensorId.GRAVITY);}getCalDegree(x: number, y: number, z: number): number {let degree: number = -1;if ((x * x + y * y) * 3 < z * z) {return degree;}degree = 90 - (Number)(Math.round(Math.atan2(y, -x) / Math.PI * 180));return degree >= 0 ? degree % 360 : degree % 360 + 360;}build() {Stack({ alignContent: Alignment.Top }) {XComponent({ type: XComponentType.SURFACE, controller: this.mXComponentController }).onLoad(async () => {// todo:切换比例,暂时用不到if (this.isRatio === true) {this.mXComponentController.setXComponentSurfaceRect({surfaceWidth: display.getDefaultDisplaySync().width,surfaceHeight: display.getDefaultDisplaySync().width * 16 / 9,offsetY: 0});surfaceId = this.mXComponentController.getXComponentSurfaceId();} else {this.mXComponentController.setXComponentSurfaceRect({surfaceWidth: display.getDefaultDisplaySync().width,surfaceHeight: display.getDefaultDisplaySync().width,});surfaceId = this.mXComponentController.getXComponentSurfaceId();}}).width(px2vp(1440)).height(px2vp(this.cameraHeight)).align(Alignment.Top)Column() {Column() {Row() {Text('x').width(24)Text('拍照').fontWeight(600)Text().width(24)}.width('100%').justifyContent(FlexAlign.SpaceBetween)//比例this.proportionBuilder()// 底部按键this.bottomKeystrokeBuilder()}.width('100%').height(px2vp(display.getDefaultDisplaySync().width * 16 / 9) - 10).justifyContent(FlexAlign.SpaceBetween)// 更多Row() {ForEach(this.moreTools, (item: ResourceStr, index: number) => {Text(item)})}.width('70%').layoutWeight(1).justifyContent(FlexAlign.SpaceBetween).alignItems(VerticalAlign.Center)}.height('100%').align(Alignment.Top).padding({ left: 14, right: 14, top: 10 })}.height('100%').width('100%').backgroundColor(Color.White).padding({ top: 50 }) //顶部安全区}Initialize(): void {this.isStabilization = false;this.isMovingPhoto = false;if (this.isRatio === true) {this.mXComponentController.setXComponentSurfaceRect({surfaceWidth: display.getDefaultDisplaySync().width,surfaceHeight: display.getDefaultDisplaySync().width * 16 / 9, offsetY: 0})} else {this.mXComponentController.setXComponentSurfaceRect({surfaceWidth: display.getDefaultDisplaySync().width,surfaceHeight: display.getDefaultDisplaySync().width})}}switchFlash(flashMode: number): void {setPhotoFlashMode(flashMode);}async getThumbnail(): Promise<void> {let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();predicates.orderByDesc(photoAccessHelper.PhotoKeys.DATE_ADDED);let fetchOptions: photoAccessHelper.FetchOptions = {fetchColumns: [],predicates: predicates};let photoHelper = photoAccessHelper.getPhotoAccessHelper(context);let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> =await photoHelper.getAssets(fetchOptions);if (fetchResult !== undefined) {let photoAsset: photoAccessHelper.PhotoAsset = await fetchResult.getFirstObject();this.photoUri = await photoAsset.getThumbnail();}}
}
3、工具
import { camera } from '@kit.CameraKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { common } from '@kit.AbilityKit';
import { AppStorageV2, display } from '@kit.ArkUI';
import { colorSpaceManager } from '@kit.ArkGraphics2D';let previewOutput: camera.PreviewOutput;
let cameraInput: camera.CameraInput;
let photoSession: camera.PhotoSession;
let photoOutPut: camera.PhotoOutput;
let currentContext: Context;
let uri: string;
let uriWaiters: ((uri: string) => void)[] = [];export async function cameraShooting(cameraPosition: number, surfaceId: string, context: Context, ratio: boolean):Promise<number[]> {currentContext = context;releaseCamera();let cameraManager: camera.CameraManager = camera.getCameraManager(context);if (!cameraManager) {return [];}let cameraArray: camera.CameraDevice[] = cameraManager.getSupportedCameras();if (cameraArray.length <= 0) {return [];}cameraInput = cameraManager.createCameraInput(cameraArray[cameraPosition]);await cameraInput.open();let sceneModes: camera.SceneMode[] = cameraManager.getSupportedSceneModes(cameraArray[cameraPosition]);let cameraOutputCap: camera.CameraOutputCapability =cameraManager.getSupportedOutputCapability(cameraArray[cameraPosition], camera.SceneMode.NORMAL_PHOTO);let isSupportPhotoMode: boolean = sceneModes.indexOf(camera.SceneMode.NORMAL_PHOTO) >= 0;if (!isSupportPhotoMode) {return [];}if (!cameraOutputCap) {return [];}let previewProfilesArray: camera.Profile[] = cameraOutputCap.previewProfiles;let photoProfilesArray: camera.Profile[] = cameraOutputCap.photoProfiles;let previewProfile: undefined | camera.Profile = previewProfilesArray.find((profile: camera.Profile) => {let screen = display.getDefaultDisplaySync();if (screen.width <= 1080) {if (ratio === true) {return profile.size.height === 1080 && profile.size.width === 1920;} else {return profile.size.height === 1080 && profile.size.width === 1080;}} else {if (ratio === true) {return profile.size.height === 1440 && profile.size.width === 2560;} else {return profile.size.height === 1440 && profile.size.width === 1440;}}});let photoProfile: undefined | camera.Profile = photoProfilesArray.find((profile: camera.Profile) => {if (previewProfile) {return profile.size.width <= 4096 && profile.size.width >= 2448;}return undefined;});previewOutput = cameraManager.createPreviewOutput(previewProfile, surfaceId);if (previewOutput === undefined) {return [];}photoOutPut = cameraManager.createPhotoOutput(photoProfile);if (photoOutPut === undefined) {return [];}// Save PicturesetPhotoOutputCb(photoOutPut);photoSession = cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession;if (photoSession === undefined) {return [];}photoSession.beginConfig();photoSession.addInput(cameraInput);photoSession.addOutput(previewOutput);photoSession.addOutput(photoOutPut);photoSession.setColorSpace(colorSpaceManager.ColorSpace.DISPLAY_P3);await photoSession.commitConfig();await photoSession.start();let flashStatus: boolean = photoSession.hasFlash();if (flashStatus) {photoSession.setFlashMode(camera.FlashMode.FLASH_MODE_CLOSE);}let focusModeStatus: boolean = photoSession.isFocusModeSupported(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO);if (focusModeStatus) {photoSession.setFocusMode(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO);}let zoomRatioRange = photoSession.getZoomRatioRange();return zoomRatioRange;
}export function capture(isFront: boolean): void {let settings: camera.PhotoCaptureSetting = {quality: camera.QualityLevel.QUALITY_LEVEL_HIGH,rotation: camera.ImageRotation.ROTATION_0,mirror: isFront};photoOutPut.capture(settings);
}export async function setPhotoFlashMode(flashMode: number): Promise<void> {photoSession.setFlashMode(flashMode);
}export async function releaseCamera(): Promise<void> {if (photoSession) {photoSession.stop();}if (cameraInput) {cameraInput.close();}if (previewOutput) {previewOutput.release();}if (photoSession) {photoSession.release();}if (photoOutPut) {photoOutPut.release();}
}function setPhotoOutputCb(photoOutput: camera.PhotoOutput): void {photoOutput.on('photoAssetAvailable',async (_err: BusinessError, photoAsset: photoAccessHelper.PhotoAsset): Promise<void> => {let accessHelper: photoAccessHelper.PhotoAccessHelper =photoAccessHelper.getPhotoAccessHelper(currentContext);let assetChangeRequest: photoAccessHelper.MediaAssetChangeRequest =new photoAccessHelper.MediaAssetChangeRequest(photoAsset);assetChangeRequest.saveCameraPhoto();await accessHelper.applyChanges(assetChangeRequest);uri = photoAsset.uri;// AppStorage.setOrCreate('photoUri', await photoAsset.getThumbnail());uriWaiters.forEach(fn => fn(uri));uriWaiters = [];});
}export function getUriAsync(): Promise<string> {return new Promise(resolve => {uriWaiters.push(resolve);});
}export function previewPhoto(context: Context): void {let photoContext = context as common.UIAbilityContext;photoContext.startAbility({parameters: { uri: uri },action: 'ohos.want.action.viewData',bundleName: 'com.huawei.hmos.photos',abilityName: 'com.huawei.hmos.photos.MainAbility'})
}