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[] = ['相册', '拍照', '拍视频']
@Builder
bottomKeystrokeBuilder() {
// 底部拍照翻转相机
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 : 1
cameraShooting(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 })
}
@Builder
proportionBuilder() {
Row({ space: 20 }) {
Text('1:1')
.height(30)
.onClick(() => {
this.cameraMargin = 100
this.cameraHeight = 1440
this.isRatio = false
cameraShooting(cameraPosition, surfaceId, context, this.isRatio);
this.Initialize();
})
Text('9:16')
.height(30)
.onClick(() => {
this.cameraHeight = 2560
this.cameraMargin = 40
this.isRatio = true
cameraShooting(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 Picture
setPhotoOutputCb(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'
})
}