[Daily HarmonyOS Next Knowledge] Tab Area Occlusion, System Cropping Function, JSON Conversion Issues, Click Events, Animation Continuous Execution Problem
1. HarmonyOS Tabbar area occlusion problem?
When using Tabbar as the bottom navigation with a List inside, occlusion occurs.
Solution: Set the barOverlap
attribute to false
to eliminate occlusion.
2. How to invoke the system cropping function for images in HarmonyOS?
Image cropping requires operations on PixelMap
.
Reference documentation: https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/image-arkts-dev-V5
Button('Image Crop').fancy()
.onClick(() => {
let region: image.Region = { x: 300, y: 0, size: { height: 500, width: 700 } };
if (this.imagePixelMap !== undefined) {
this.imagePixelMap.crop(region).then(async () => {
if (this.imagePixelMap !== undefined) {
let pixel = await copyPixelMap(this.imagePixelMap);
this.imagePixelMap.release();
this.imagePixelMap = pixel;
console.info('Sucessed in setting crop.');
}
}).catch((err: BusinessError) => {
console.error('Failed to crop pixelmap.');
})
}
})
async function copyPixelMap(imagePixel: PixelMap): Promise<image.PixelMap> {
let imageInfo: image.ImageInfo = await imagePixel.getImageInfo();
console.info(`copyPixelMapSize: width:${imageInfo?.size.width} height:${imageInfo?.size.height}`);
let newRegion: image.Region = {
size: { height: imageInfo.size.height, width: imageInfo.size.width },
x: 0,
y: 0
}
let newArea: image.PositionArea = {
pixels: new ArrayBuffer(imageInfo.size.height * imageInfo.size.width * 4),
offset: 0,
stride: imageInfo.stride,
region: newRegion
}
await imagePixel.readPixels(newArea);
let opts: image.InitializationOptions = { editable: true, pixelFormat: 4, size: imageInfo.size };
let imagePixelCache = await image.createPixelMap(newArea.pixels, opts);
return imagePixelCache;
}
Simple example reference: For third-party library image cropping, refer to ImageKnife, which provides a cropping interface request.crop()
.
Documentation: https://gitee.com/openharmony-tpc/ImageKnife
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { fileIo, picker } from '@kit.CoreFileKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { image } from '@kit.ImageKit';
@Entry
@Component
struct Index {
@State origin: PixelMap | undefined = undefined
@State origin2: PixelMap | undefined = undefined
private uri:string = ''
private async decodeImage(uri: string) {
try {
let file = fileIo.openSync(uri, fileIo.OpenMode.READ_ONLY)
const imageSourceApi = image.createImageSource(file.fd)
imageSourceApi.getImageInfo(0, (error: BusinessError, imageInfo) => {
if (imageInfo === undefined) {
// log.e(error)
}
console.log(`imageInfo: ${JSON.stringify(imageInfo)}`)
})
return await imageSourceApi.createPixelMap()
} catch (error) {
// log.e(error)
return undefined
}
}
build() {
Column() {
Button('Add Photo')
.onClick(async () => {
let uris = await selectPhoto({
MIMEType: picker.PhotoViewMIMETypes.IMAGE_TYPE,
maxSelectNumber: 1,
isPhotoTakingSupported: false,
recommendationOptions: {
recommendationType: photoAccessHelper.RecommendationType.PROFILE_PICTURE,
}
})
if (uris.length === 0) {
return
}
this.uri = uris[0];
this.origin = await this.decodeImage(uris[0])
})
Image(this.origin)
.width('100%')
.height('100%')
.layoutWeight(1)
.objectFit(ImageFit.Contain)
Button('Crop Photo')
.onClick(async () => {
this.origin2 = await this.decodeImage(this.uri)
if (this.origin2) {
let info = await this.origin2.getImageInfo()
this.origin2.crop({
x: 0, y: 0,
size: {
width: info.size.width,
height: info.size.height / 2
}
})
let pixel = await copyPixelMap(this.origin2);
this.origin2.release();
this.origin2 = pixel;
}
})
Image(this.origin2)
.width('100%')
.height('100%')
.layoutWeight(1)
.objectFit(ImageFit.Contain)
}
.width(`100%`)
}
}
const PHOTO_DEFAULT_SELECT_NUMBER: number = 9; // Quantity
async function copyPixelMap(imagePixel: PixelMap): Promise<image.PixelMap> {
let imageInfo: image.ImageInfo = await imagePixel.getImageInfo();
console.info(`copyPixelMapSize: width:${imageInfo?.size.width} height:${imageInfo?.size.height}`);
let newRegion: image.Region = {
size: { height: imageInfo.size.height, width: imageInfo.size.width },
x: 0,
y: 0
}
let newArea: image.PositionArea = {
pixels: new ArrayBuffer(imageInfo.size.height * imageInfo.size.width * 4),
offset: 0,
stride: imageInfo.stride,
region: newRegion
}
await imagePixel.readPixels(newArea);
let opts: image.InitializationOptions = { editable: true, pixelFormat: 4, size: imageInfo.size };
let imagePixelCache = await image.createPixelMap(newArea.pixels, opts);
return imagePixelCache;
}
/**
Launches the photoPicker interface in selection mode, allowing users to select one or more images/videos.
@param options
@returns
*/
async function selectPhoto(options?: PhotoSelectOptions): Promise<Array<string>> {
try {
if (!options) {
options = new PhotoSelectOptions();
}
if (!options.MIMEType) { // Allowed media file types; defaults to images and videos if not specified.
options.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
}
if (!options.maxSelectNumber) { // Maximum number of media files to select, default is 9
options.maxSelectNumber = PHOTO_DEFAULT_SELECT_NUMBER;
}
if (options.isPhotoTakingSupported === undefined) {
options.isPhotoTakingSupported = true; // Enables photo taking.
}
if (options.isEditSupported === undefined) {
options.isEditSupported = true; // Enables photo editing.
}
if (options.isSearchSupported === undefined) {
options.isSearchSupported = true; // Enables search.
}
let photoSelectOptions: photoAccessHelper.PhotoSelectOptions = {
MIMEType: options.MIMEType,
maxSelectNumber: options.maxSelectNumber,
isPhotoTakingSupported: options.isPhotoTakingSupported,
isEditSupported: options.isEditSupported,
isSearchSupported: options.isSearchSupported,
recommendationOptions: options.recommendationOptions,
preselectedUris: options.preselectedUris
}
let photoPicker = new photoAccessHelper.PhotoViewPicker();
let photoSelectResult: photoAccessHelper.PhotoSelectResult = await photoPicker.select(photoSelectOptions)
if (photoSelectResult && photoSelectResult.photoUris && photoSelectResult.photoUris.length > 0) {
return photoSelectResult.photoUris
} else {
return [];
}
} catch (err) {
console.error(err)
return [];
}
}
class PhotoSelectOptions {
MIMEType?: photoAccessHelper.PhotoViewMIMETypes = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE; // Allowed media file types; defaults to images and videos if not specified.
maxSelectNumber?: number = PHOTO_DEFAULT_SELECT_NUMBER; // Maximum number of media files to select (default is 50, maximum is 500).
isPhotoTakingSupported?: boolean = true; // Enables photo taking.
isEditSupported?: boolean = true; // Enables photo editing.
isSearchSupported?: boolean = true; // Enables search.
recommendationOptions?: photoAccessHelper.RecommendationOptions; // Enables photo recommendations.
preselectedUris?: Array<string>; // URIs of preselected images.
}
- Use
gesture
to implement image zooming: Refer to the demo https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/tutorials_NEXT-ElectronicAlbum. - Implement image cropping with ImageKnife: https://gitee.com/openharmony-tpc/ImageKnife.
3. HarmonyOS JSON conversion issues in release mode (parameter names are modified)?
When converting data to a JSON string, it works in test
build mode but fails in release
mode, as parameter names are altered.
Reason: DevEco Studio enables code obfuscation by default for Stage models (API 10 and above) in release mode.
Solution: Disable code obfuscation for debugging in release mode.
Reference: https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5
4. The global click event listener in HarmonyOS has x
and y
properties in GestureEvent
, but they are not exposed as external properties or methods?
let callback = (event: GestureEvent, frameNode: FrameNode) => {
console.error("PageEvent didClick, event: {}", JSON.stringify(frameNode))
}
Solution: Use ClickEvent
to access x
and y
properties.
Reference documentation: https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-arkui-uicontext-V5#ZH-CN_TOPIC_0000001884757690__onwillclick12-1
5. The Lottie animation on the HarmonyOS home page continues running even after page navigation?
The home page's cube card (e.g., live broadcast card) has a Lottie animation that persists after switching pages, causing device overheating.
Solution: Manually control animations during page navigation using:
-
lottie.pause('name')
to pause the animation. -
lottie.play('name')
to resume the animation.
Top comments (0)