HarmonyOS鸿蒙Next中bindContentCover和bindSheet可以在同一个组件使用吗
HarmonyOS鸿蒙Next中bindContentCover和bindSheet可以在同一个组件使用吗
为什么这里一个组件同时存在bindContentCover和bindSheet时,bindContentCover中转场失效了,只有bindSheet有效果,状态没有冲突。注释bindSheet后,转场是成功的
更多关于HarmonyOS鸿蒙Next中bindContentCover和bindSheet可以在同一个组件使用吗的实战教程也可以访问 https://www.itying.com/category-93-b0.html
可以在想绑定的组件上再套上一层组件,然后在内外组件绑定不同的模态转场:
Row() {
Row()
.bindSheet(this.isShowSheet, this.mySheet(), {
height: 300,
dragBar: false,
onDisappear: () => {
this.isShowSheet = false;
}
})
}
.bindContentCover(this.isPresent, this.MyBuilder(), {
modalTransition: ModalTransition.DEFAULT,
onDisappear: () => {
this.isPresent = false;
}
})
完整demo:
import { curves } from '@kit.ArkUI';
interface PersonList {
name: string,
cardnum: string
}
@Entry
@Component
struct BindContentCoverDemo {
private personList: Array<PersonList> = [
{ name: '王**', cardnum: '1234***********789' },
{ name: '宋*', cardnum: '2345***********789' },
{ name: '许**', cardnum: '3456***********789' },
{ name: '唐*', cardnum: '4567***********789' }
];
@State isShowSheet: boolean = false;
private menuList: string[] = ['广州南', '虎门站', '深圳北站', '佛山站', '东莞南站', '贵阳北站', '北京西站'];
// 第一步:定义全屏模态转场效果bindContentCover
// 模态转场控制变量
@State isPresent: boolean = false;
// 第二步:定义模态展示界面
// 通过@Builder构建模态展示界面
@Builder
MyBuilder() {
Column() {
Row() {
Text('选择乘车人')
.fontSize(20)
.fontColor(Color.White)
.width('100%')
.textAlign(TextAlign.Center)
.padding({ top: 30, bottom: 15 })
}
.backgroundColor(0x007dfe)
Row() {
Text('+ 添加乘车人')
.fontSize(16)
.fontColor(0x333333)
.margin({ top: 10 })
.padding({ top: 20, bottom: 20 })
.width('92%')
.borderRadius(10)
.textAlign(TextAlign.Center)
.backgroundColor(Color.White)
}
Column() {
ForEach(this.personList, (item: PersonList, index: number) => {
Row() {
Column() {
if (index % 2 == 0) {
Column()
.width(20)
.height(20)
.border({ width: 1, color: 0x007dfe })
.backgroundColor('#007dfe')
} else {
Column()
.width(20)
.height(20)
.border({ width: 1, color: 0x007dfe })
}
}
.width('20%')
Column() {
Text(item.name)
.fontColor(0x333333)
.fontSize(18)
Text(item.cardnum)
.fontColor(0x666666)
.fontSize(14)
}
.width('60%')
.alignItems(HorizontalAlign.Start)
Column() {
Text('编辑')
.fontColor(0x007dfe)
.fontSize(16)
}
.width('20%')
}
.padding({ top: 10, bottom: 10 })
.border({ width: { bottom: 1 }, color: 0xf1f1f1 })
.width('92%')
.backgroundColor(Color.White)
})
}
.padding({ top: 20, bottom: 20 })
Text('确认')
.width('90%')
.height(40)
.textAlign(TextAlign.Center)
.borderRadius(10)
.fontColor(Color.White)
.backgroundColor(0x007dfe)
.onClick(() => {
this.isPresent = false;
})
}
.size({ width: '100%', height: '100%' })
.backgroundColor(0xf5f5f5)
// 通过转场动画实现出现消失转场动画效果
.transition(TransitionEffect.translate({ y: 100 }).animation({ curve: curves.springMotion(0.6, 0.8)))
}
@Builder
mySheet() {
Column() {
Flex({ direction: FlexDirection.Row, wrap: FlexWrap.Wrap }) {
ForEach(this.menuList, (item: string) => {
Text(item)
.fontSize(16)
.fontColor(0x333333)
.backgroundColor(0xf1f1f1)
.borderRadius(8)
.margin(10)
.padding(10)
})
}
.padding({ top: 18 })
}
.width('100%')
.height('100%')
.backgroundColor(Color.White)
.transition(TransitionEffect.translate({ y: 100 }).animation({ curve: curves.springMotion(0.6, 0.8)))
}
build() {
Column() {
Row() {
Text('确认订单')
.fontSize(20)
.fontColor(Color.White)
.width('100%')
.textAlign(TextAlign.Center)
.padding({ top: 30, bottom: 60 })
}
.backgroundColor(0x007dfe)
Column() {
Row() {
Column() {
Text('00:25')
Text('始发站')
}
.width('30%')
Column() {
Text('G1234')
Text('8时1分')
}
.width('30%')
Column() {
Text('08:26')
Text('终点站')
}
.width('30%')
}
}
.width('92%')
.padding(15)
.margin({ top: -30 })
.backgroundColor(Color.White)
.shadow({ radius: 30, color: '#aaaaaa' })
.borderRadius(10)
Column() {
Text('+ 选择出发站')
.fontSize(18)
.fontColor(Color.Orange)
.fontWeight(FontWeight.Bold)
.padding({ top: 10, bottom: 10 })
.width('60%')
.textAlign(TextAlign.Center)
.borderRadius(15)
.onClick(() => {
this.isShowSheet = true;
})
.margin({ bottom: '10%' })
Text('+ 选择乘车人')
.fontSize(18)
.fontColor(Color.Orange)
.fontWeight(FontWeight.Bold)
.padding({ top: 10, bottom: 10 })
.width('60%')
.textAlign(TextAlign.Center)
.borderRadius(15)
// 通过选定的模态接口,绑定模态展示界面,ModalTransition是内置的ContentCover转场动画类型,这里选择DEFAULT代表设置上下切换动画效果,通过onDisappear控制状态变量变换。
.onClick(() => {
// 第三步:通过模态接口调起模态展示界面,通过转场动画或者共享元素动画去实现对应的动画效果
// 改变状态变量,显示模态界面
this.isPresent = true;
})
Row() {
Row()
.bindSheet(this.isShowSheet, this.mySheet(), {
height: 300,
dragBar: false,
onDisappear: () => {
this.isShowSheet = false;
}
})
}
.bindContentCover(this.isPresent, this.MyBuilder(), {
modalTransition: ModalTransition.DEFAULT,
onDisappear: () => {
this.isPresent = false;
}
})
}
.padding({ top: 60 })
}
}
}
更多关于HarmonyOS鸿蒙Next中bindContentCover和bindSheet可以在同一个组件使用吗的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
你给不同组件挂载试一试,用Row或者Column嵌套一层,bindContentCover和bindSheet都是属于模态框类型,可能机制不让一个组件挂载同类型的模态框吧
import { displaySync } from "@kit.ArkGraphics2D";
import { MusicControlComponent } from "./MusicControlComponent";
import { curves, window } from "@kit.ArkUI";
import { Logger } from "../utils/Logger";
import { BusinessError } from "@kit.BasicServicesKit";
import { SongItem } from "../viewmodel/SongItem";
import { MediaService } from "../service/MediaService";
@Component
export struct PlayerComponent{
@StorageLink('isPlay') @Watch('animationFun') isPlay: boolean = false;
private panOption: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.Vertical });
private backDisplaySyncSlow: displaySync.DisplaySync | undefined = undefined;
@StorageLink('isShowPlay') isShowPlay: boolean = false;
@StorageLink('topArea') topArea: number = 0;
@StorageLink('bottomArea') bottomArea: number = 0;
@State componentHeight: number = 0;
@StorageLink('deviceHeight') deviceHeight: number = 0;
@StorageLink('songList') songList: SongItem[] = [];
@StorageProp('selectIndex') selectIndex: number = 0;
@State imageRotate: number = 0;
@State isPlaylistVisible: boolean = false; // 新增状态变量,用于控制播放列表的显示与隐
private drawFrame: (value: displaySync.IntervalInfo) => void = (value: displaySync.IntervalInfo) => {
if (this.imageRotate >= 360 ) {
this.imageRotate = 0;
}
this.imageRotate += 1;
};
animationFun() {
if (this.isPlay) {
this.backDisplaySyncSlow?.start();
} else {
this.backDisplaySyncSlow?.stop();
}
}
aboutToAppear() {
let range : ExpectedFrameRateRange = {
expected: 30,
min: 0,
max: 30
};
this.backDisplaySyncSlow = displaySync.create();
this.backDisplaySyncSlow.setExpectedFrameRateRange(range);
this.backDisplaySyncSlow.on('frame', this.drawFrame);
console.log('Current isShowPlay state:', this.isShowPlay);
}
aboutToDisappear(): void {
this.backDisplaySyncSlow?.off('frame', this.drawFrame);
}
build() {
Row(){
Row(){
Image(this.songList[this.selectIndex]?.label || $r('app.media.ic_default_cover'))
.height('32vp')
.width('32vp')
.borderRadius('16vp')
.margin({right:'12vp'})
.rotate({angle:0})
.onAppear(()=>{
if (this.songList.length > 0) { // (2) 增加数组长度校验
this.animationFun();
}
})
Column() {
Text(this.songList[this.selectIndex]?.title ?? '未知标题')
.fontColor($r('app.color.song_name'))
.fontSize($r('app.float.song_title_sm'))
Text(this.songList[this.selectIndex]?.singer ?? '未知歌手')
.fontColor($r('app.color.singer'))
.fontSize($r('app.float.singer_title_sm'))
.opacity($r('app.float.singer_opacity'))
}
.alignItems(HorizontalAlign.Start)
}
.layoutWeight(1)
Blank()
Row(){
Image($r('app.media.ic_previous'))
.height($r('app.float.control_icon_height'))
.width($r('app.float.control_icon_width'))
.margin({ right: $r('app.float.control_icon_margin') })
.displayPriority(2)
.onClick(()=> MediaService.getInstance().playPrevious())
Image(this.isPlay?$r('app.media.ic_play'):$r('app.media.ic_pause') )
.height('24vp')
.width('24vp')
.displayPriority(3)
.onClick(()=>{
if (MediaService.getInstance().getFirst()) {
MediaService.getInstance().loadAssent(0);
} else {
this.isPlay ? MediaService.getInstance().pause() : MediaService.getInstance().play();
}
})
Image($r('app.media.ic_next'))
.height($r('app.float.control_icon_height'))
.width($r('app.float.control_icon_width'))
.margin({
right: $r('app.float.control_icon_margin'),
left: $r('app.float.control_icon_margin')
})
.displayPriority(2)
.onClick(()=> MediaService.getInstance().playNextAuto(true))
Image($r('app.media.ic_music_list'))
.height($r('app.float.control_icon_height'))
.width($r('app.float.control_icon_width'))
.displayPriority(1)
.onClick(()=> {
this.isPlaylistVisible = true; // 点击图标时显示播放列表
console.log('isPlaylistVisible:', this.isPlaylistVisible);
console.log("isShowPlay",`${this.isShowPlay}`)
})
}
.width($r('app.float.play_width_sm'))
.justifyContent(FlexAlign.End)
.layoutWeight(1)
}
.height('48vp')
.width('100%')
.onClick(()=>{
this.isShowPlay = true;
console.log("isShowPlay",`${this.isShowPlay}`)
console.log("isPlaylistVisible", this.isPlaylistVisible)
})
.backgroundColor($r('app.color.player_background'))
.bindContentCover($$this.isShowPlay, this.musicPlayBuilder(), ModalTransition.DEFAULT)
.bindSheet($$this.isPlaylistVisible, this.playlistBuilder(),{
height: '80%',
dragBar: true,
onDisappear: ()=>{
console.log('bindSheet onDisappear, isShowPlay:', this.isShowPlay);
this.isPlaylistVisible = false; // 隐藏播放列表
},
})
.padding({
left: $r('app.float.player_padding'),
right: $r('app.float.player_padding')
})
.position({
x: 0,
y: '100%'
})
.translate({
x: 0,
y: '-48vp'
})
.gesture(
PanGesture(this.panOption)
.onActionEnd((event?: GestureEvent) =>{
if (event && event.offsetY < -10) {
this.isShowPlay = true;
}
})
)
}
@Builder
musicPlayBuilder(){
Column(){
Column() {
MusicControlComponent({ isShowPlay: this.isShowPlay })
}
.height((100 - this.componentHeight) + '%')
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.End)
.transition(TransitionEffect.translate({ y: 1000 }).animation({ curve: curves.springMotion(0.6, 0.8) }))
.backgroundColor($r('app.color.player_background'))
.onAppear(()=>{
window.getLastWindow(getContext(this)).then((windowStage: window.Window) =>{
let area = windowStage.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
this.topArea = px2vp(area.topRect.height);
let bottomArea = windowStage.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR);
this.bottomArea = px2vp(bottomArea.bottomRect.height);
Logger.info('bottomArea '+ this.bottomArea)
if (this.topArea > 0) {
windowStage.setWindowLayoutFullScreen(true);
}
let sysBarProps: window.SystemBarProperties = {
statusBarContentColor: '#FFFFFF'
};
windowStage.setWindowSystemBarProperties(sysBarProps);
}).catch((error: BusinessError) =>{
Logger.error(`${error.code} + ${error.message}`)
});
console.log('musicPlayBuilder onAppear');
})
.onDisAppear(()=>{
this.componentHeight = 0;
this.isShowPlay = false;
window.getLastWindow(getContext(this)).then((windowStage: window.Window) =>{
let area = windowStage.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
let topHeight = px2vp(area.topRect.height);
if (topHeight > 0) {
windowStage.setWindowLayoutFullScreen(false);
}
let sysBarProps: window.SystemBarProperties = {
statusBarContentColor: '#000000'
};
windowStage.setWindowSystemBarProperties(sysBarProps);
}).catch((error: BusinessError) =>{
Logger.error(`${error.code} + ${error.message}`)
});
console.log('musicPlayBuilder onDisAppear');
})
.gesture(
PanGesture(this.panOption)
.onActionUpdate((event?: GestureEvent) =>{
if (event) {
let height = event.offsetY / this.deviceHeight * 100;
this.componentHeight = height;
if (this.componentHeight < 0) {
this.componentHeight = 0;
}
}
})
.onActionEnd(()=>{
if (this.componentHeight > 40) {
this.isShowPlay = false;
} else {
this.componentHeight = 0;
}
})
)
}
@Builder
playlistBuilder() {
Column() {
Text('播放列表')
.fontSize(22)
.fontColor($r('app.color.black'))
.margin({
left: $r('app.float.player_padding'),
right: $r('app.float.player_padding'),
top: $r('app.float.player_padding')
})
List({space: 8}){
ForEach(this.songList,(song:SongItem,index:number) =>{
ListItem(){
Column(){
Row(){
Row(){
Text(song.title)
.fontSize(18)
.fontColor(this.selectIndex === index ? $r('app.color.playing_color') : $r('app.color.black'))
.margin({
left: $r('app.float.player_padding'),
right: $r('app.float.player_padding'),
})
Text(song.singer)
.fontSize(12)
.fontColor(this.selectIndex === index ? $r('app.color.playing_color') : $r('app.color.black'))
.opacity(this.selectIndex == index ? 1: $r('app.float.singer_opacity'))
}
Row(){
if (this.selectIndex === index){
Image($r('app.media.ic_playing'))
.height($r('app.float.control_icon_height'))
.width($r('app.float.control_icon_width'))
.margin({
right: $r('app.float.control_icon_margin'),
left: $r('app.float.control_icon_margin')
})
}
Image($r('app.media.ic_delete'))
.height($r('app.float.control_icon_height'))
.width($r('app.float.control_icon_width'))
.margin({
right: $r('app.float.control_icon_margin'),
left: $r('app.float.control_icon_margin')
})
.displayPriority(1)
.onClick(()=>{
this.songList.splice(index, 1);
})
}
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
Divider()
.height(1)
.backgroundColor($r('app.color.divider'))
.margin({
left: $r('app.float.player_padding'),
right: $r('app.float.player_padding'),
})
.opacity($r('app.float.divider_opacity'))
}
.width('100%')
.onClick(()=>{
this.selectIndex = index;
MediaService.getInstance().loadAssent(this.selectIndex)
})
}
})
}
.scrollBar(BarState.Off)
.backgroundColor(Color.White)
.width('100%')
.height('100%')
.margin({
top: 20
})
}
}
}
在HarmonyOS鸿蒙Next中,bindContentCover
和bindSheet
可以在同一个组件中使用。bindContentCover
用于绑定内容覆盖层,通常用于显示模态对话框或覆盖内容;bindSheet
用于绑定底部弹出层,通常用于显示操作选项或表单。两者功能不同,可以同时使用,但需注意布局和交互逻辑,避免冲突或重叠。