HarmonyOS鸿蒙Next中bindContentCover和bindSheet可以在同一个组件使用吗

HarmonyOS鸿蒙Next中bindContentCover和bindSheet可以在同一个组件使用吗

cke_9124.png

为什么这里一个组件同时存在bindContentCover和bindSheet时,bindContentCover中转场失效了,只有bindSheet有效果,状态没有冲突。注释bindSheet后,转场是成功的


更多关于HarmonyOS鸿蒙Next中bindContentCover和bindSheet可以在同一个组件使用吗的实战教程也可以访问 https://www.itying.com/category-93-b0.html

4 回复

可以在想绑定的组件上再套上一层组件,然后在内外组件绑定不同的模态转场:

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中,bindContentCoverbindSheet可以在同一个组件中使用。bindContentCover用于绑定内容覆盖层,通常用于显示模态对话框或覆盖内容;bindSheet用于绑定底部弹出层,通常用于显示操作选项或表单。两者功能不同,可以同时使用,但需注意布局和交互逻辑,避免冲突或重叠。

回到顶部