HarmonyOS 鸿蒙Next Tabs嵌套TabContent内部嵌套可拖动组件导致冲突

HarmonyOS 鸿蒙Next Tabs嵌套TabContent内部嵌套可拖动组件导致冲突

,这边使用Tabs组件嵌套两个TabContent页面,第一个TabContent页面内嵌套了一个可随意拖动的组件。拖动FloatWindowView这个组件时,会触发TabContent左右滑动,导致了滑动冲突。在onTouchEvent事件内调用了event.stopPropagation(),好像并没有拦截成功。或者有没有其他办法可以解决此冲突?

import { FloatWindowView } from './FloatWindowView';

@Entry
@Component
struct Index {
  // 父组件宽度
  @State containerWidth: number = 0;
  // 父组件高度
  @State containerHeight: number = 0;

  array?: Array<string> = new Array

  aboutToAppear(): void {
    this.array?.push('page1')
    this.array?.push('page2')
  }

  build() {
    Stack() {
      Tabs() {
        ForEach(this.array, (item: string, index) => {
          // 模拟两个页面
          TabContent() {
            // 第一个
            if (index == 0) {
              Stack() {
                List().width('100%')
                  .height('100%')
                  .backgroundColor(Color.Gray)

                FloatWindowView({
                  containerWidth: this.containerWidth, containerHeight: this.containerHeight,
                  initPositionX: 0, initPositionY: 300,
                })
              }
            } else {
              // 第二个
              List().width('100%')
                .height('100%')
                .backgroundColor(Color.Black)
            }
          }
          .tabBar(item)
          .onAreaChange((oldValue: Area, newValue: Area) => {
            console.log('test55 父级onAreaChange ')
          })
        }, (item: string) => JSON.stringify(item))

      }
    }
    .height('100%')
    .width('100%')
    .onAreaChange((oldValue: Area, newValue: Area) => {
      // TODO:onAreaChange是高频回调,仅在父组件尺寸改变时获取新的父组件宽高,避免性能损耗
      if (oldValue.width !== newValue.width) {
        this.containerWidth = newValue.width as number;
      }
      if (oldValue.height !== newValue.height) {
        this.containerHeight = newValue.height as number;
      }
    })
  }
}

更多关于HarmonyOS 鸿蒙Next Tabs嵌套TabContent内部嵌套可拖动组件导致冲突的实战教程也可以访问 https://www.itying.com/category-93-b0.html

2 回复

代码

import { FloatWindowView } from './FloatWindowView';

[@Entry](/user/Entry)
[@Component](/user/Component)
struct Index {
 // 父组件宽度
 [@State](/user/State) containerWidth: number = 0;
 // 父组件高度
 [@State](/user/State) containerHeight: number = 0;
 [@State](/user/State) canScroll: boolean = true
 array?: Array<string> = new Array

 aboutToAppear(): void {
   this.array?.push('page1')
   this.array?.push('page2')
 }
 controlScroll(){
   this.canScroll = false
 }
 outtouch(){
   this.canScroll = true
 }

 build() {
   Stack() {
     Tabs() {
       ForEach(this.array, (item: string, index) => {
         // 模拟两个页面
         TabContent() {
           // 第一个
           if (index == 0) {
             Stack() {
               List().width('100%')
                 .height('100%')
                 .backgroundColor(Color.Gray)

               FloatWindowView({
                 ontouch:()=>{this.controlScroll()},
                 outtouch:()=>{this.outtouch()},
                 containerWidth: this.containerWidth,
                 containerHeight: this.containerHeight,
                 initPositionX: 0,
                 initPositionY: 300,
               })
             }
           } else {
             // 第二个
             List().width('100%')
               .height('100%')
               .backgroundColor(Color.Black)
           }
         }
         // .scale()
         .tabBar(item)
         .onAreaChange((oldValue: Area, newValue: Area) => {
           console.log('test55 父级onAreaChange ')
         })
       }, (item: string) => JSON.stringify(item))

     }
     .scrollable(this.canScroll)
   }
   .height('100%')
   .width('100%')
   .onAreaChange((oldValue: Area, newValue: Area) => {
     // TODO:onAreaChange是高频回调,仅在父组件尺寸改变时获取新的父组件宽高,避免性能损耗
     if (oldValue.width !== newValue.width) {
       this.containerWidth = newValue.width as number;
     }
     if (oldValue.height !== newValue.height) {
       this.containerHeight = newValue.height as number;
     }
   })
 }
}



import { curves, display } from '[@kit](/user/kit).ArkUI';

[@Component](/user/Component)
export struct FloatWindowView {
 // 悬浮窗相对于父组件四条边的距离,top和bottom同时设置时top生效,right和left同时设置时left生效
 [@State](/user/State) edge: Edges = null!!;
 [@Link](/user/Link) containerWidth: number;
 [@Link](/user/Link) containerHeight: number;
 // 拖拽移动开始时悬浮窗在窗口中的坐标,每次移动回调触发时更新
 private windowStartX: number = 0;
 private windowStartY: number = 0;
 openAdsorb: boolean = true
 [@Prop](/user/Prop) PAGE_PADDING: number = 0; // 页面内容内边距,用于悬浮窗位置计算
 [@Prop](/user/Prop) FLOAT_WINDOW_WIDTH: number = 100; // 悬浮窗宽度
 [@Prop](/user/Prop) FLOAT_WINDOW_HEIGHT: number = 30; // 悬浮窗高度
 [@Prop](/user/Prop) initPositionY: number = 300; // 悬浮窗相对父容器左上角的Y坐标初始值
 [@Prop](/user/Prop) initPositionX: number = 0; // 悬浮窗相对父容器左上角的X坐标初始值

 aboutToAppear(): void {
   this.edge = { top: this.initPositionY, right: this.initPositionX }
 }

 /**
  * 触摸回调,悬浮窗跟手和贴边动画
  */
 onTouchEvent(event: TouchEvent): void {
   switch (event.type) {
     case TouchType.Down: {
       this.ontouch()
       // 获取拖拽开始时悬浮窗在窗口中的坐标
       this.windowStartX = event.touches[0].windowX;
       this.windowStartY = event.touches[0].windowY;
       AppStorage.set('childScrolling', true)
       console.log('test551: 子的touch')
       break;
     }
     case TouchType.Move: {
       const windowX: number = event.touches[0].windowX;
       const windowY: number = event.touches[0].windowY;
       // TODO:知识点:跟手动画,推荐使用默认参数的弹性跟手动画曲线curves.responsiveSpringMotion。
       animateTo({ curve: curves.responsiveSpringMotion() }, () => {
         // 判断当前edge中属性left和right哪个不为undefined,用于控制悬浮窗水平方向的位置
         if (this.edge.left !== undefined) {
           this.edge.left = this.edge.left as number + (windowX - this.windowStartX);
         } else {
           this.edge.right = this.edge.right as number - (windowX - this.windowStartX);
         }

         this.edge.top = this.edge.top as number + (windowY - this.windowStartY);
         this.windowStartX = windowX;
         this.windowStartY = windowY;
       })
       break;
     }
     case TouchType.Up: {
       this.outtouch()
       console.log('test551: 子的touch up')
       // 计算悬浮窗中心点在父组件中水平方向的坐标
       let centerX: number;
       if (this.edge.left !== undefined) {
         centerX = this.edge.left as number + this.FLOAT_WINDOW_WIDTH / 2;
       } else {
         centerX = this.containerWidth - (this.edge.right as number) - this.FLOAT_WINDOW_WIDTH / 2;
       }
       // TODO:通过判断悬浮窗在父组件中的位置,设置悬浮窗贴边,使用curves.springMotion()弹性动画曲线,可以实现阻尼动画效果
       animateTo({ curve: curves.springMotion() }, () => {
         // 判断悬浮窗中心在水平方向是否超过父组件宽度的一半,根据结果设置靠左或靠右
         if (this.edge.top as number < this.PAGE_PADDING) {
           this.edge.top = this.PAGE_PADDING;
         } else if (this.edge.top as number >
           this.containerHeight - this.FLOAT_WINDOW_HEIGHT - this.PAGE_PADDING) {
           this.edge.top = this.containerHeight - this.FLOAT_WINDOW_HEIGHT - this.PAGE_PADDING;
         }

         let width = px2vp(display.getDefaultDisplaySync().width)

         if (this.openAdsorb) {
           if (centerX > (this.containerWidth / 2)) {
             this.edge.right = this.PAGE_PADDING;
             this.edge.left = undefined;
             this.currentAdsorbLeft = false
           } else {
             this.edge.right = undefined;
             this.edge.left = this.PAGE_PADDING;
             this.currentAdsorbLeft = true
           }
         } else {
           // 是否超出了左侧屏幕
           let overflowLeft = this.edge.right! > width - this.FLOAT_WINDOW_WIDTH
           // 是否超出了右侧屏幕
           let overflowRight = this.edge.right! < 0

           if (overflowLeft) { // 超出左边屏幕了
             this.edge.left = undefined;
             this.edge.right = width - this.FLOAT_WINDOW_WIDTH;
           } else if (overflowRight) { // 超出右边屏幕了
             this.edge.right = this.PAGE_PADDING;
             this.edge.left = undefined;
           }
         }

         AppStorage.set('childScrolling', false)
       })
       break;
     }
     default: {
       break;
     }
   }

   // 停止向父级传递事件
   event.stopPropagation()
 }

 ontouch: () => void = () => {

 }
 outtouch: () => void = () => {

 }
 [@State](/user/State) currentAdsorbLeft?: boolean = false

 build() {
   Row() {

   }
   .backgroundColor(Color.Pink)
   .width(this.FLOAT_WINDOW_WIDTH)
   .height(this.FLOAT_WINDOW_HEIGHT)
   .position(this.edge)
   .onTouch((event: TouchEvent) => {

     this.onTouchEvent(event);
   })

   .onClick(() => {

   })
 }
}

更多关于HarmonyOS 鸿蒙Next Tabs嵌套TabContent内部嵌套可拖动组件导致冲突的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙系统中,Next Tabs嵌套TabContent内部再嵌套可拖动组件时产生的冲突,通常是由于事件分发机制处理不当导致的。具体来说,当多个组件嵌套且都具备交互能力(如拖动),事件(如触摸事件)可能会在不同层级间发生竞争,导致行为异常。

解决这类问题,关键在于明确事件传递路径和处理逻辑。可以尝试以下方法:

  1. 事件拦截:在TabContent或外层Tabs中,通过重写事件处理方法(如onTouchEvent),根据需求拦截或传递事件,避免事件被多个组件同时处理。

  2. 组件属性调整:检查并调整可拖动组件的属性设置,如是否允许捕获触摸事件、是否启用拖动等,确保其行为符合预期。

  3. 布局优化:考虑调整嵌套结构,减少事件传递的层级,或采用其他布局方式实现相同功能,以减少事件冲突的可能性。

  4. 代码审查:仔细检查相关组件的事件处理代码,确保逻辑清晰、无遗漏。

如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部