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
代码
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内部再嵌套可拖动组件时产生的冲突,通常是由于事件分发机制处理不当导致的。具体来说,当多个组件嵌套且都具备交互能力(如拖动),事件(如触摸事件)可能会在不同层级间发生竞争,导致行为异常。
解决这类问题,关键在于明确事件传递路径和处理逻辑。可以尝试以下方法:
-
事件拦截:在TabContent或外层Tabs中,通过重写事件处理方法(如onTouchEvent),根据需求拦截或传递事件,避免事件被多个组件同时处理。
-
组件属性调整:检查并调整可拖动组件的属性设置,如是否允许捕获触摸事件、是否启用拖动等,确保其行为符合预期。
-
布局优化:考虑调整嵌套结构,减少事件传递的层级,或采用其他布局方式实现相同功能,以减少事件冲突的可能性。
-
代码审查:仔细检查相关组件的事件处理代码,确保逻辑清晰、无遗漏。
如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html