qml编程中,经常会遇到组件重叠的情况,结果响应异常
尤其有多个mousearea重叠的时候,希望点击时都响应,结果有一个死活不响应。作为qml的初学者,可能会很疑惑,调试半天,搞不明白哪里错了。。
我查了很多资料,都只是谈mousearea用法,很少有人清楚谈到这个问题。于是,我决定自己来讲,避免更多新人像我一样踩坑,无从解决。
基本原理
首先,需要理解qml组件渲染的基本规则。
渲染层级
先画两个大小矩形,如下:
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Rectangle{
id:rectangle1
color: "lightblue"
anchors.fill: parent
}
Rectangle{
id:rectangle2
color: "pink"
width: 100
height: 100
}
}
运行程序,查看效果。
然后交换代码顺序,先写粉色矩形的代码,如下
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Rectangle{
id:rectangle2
color: "pink"
width: 100
height: 100
}
Rectangle{
id:rectangle1
color: "lightblue"
anchors.fill: parent
}
}
结果如图,这时,粉色矩形被面积更大的蓝色矩形所覆盖。
为什么呢?只是交换了一下代码顺序,显示就不一样了呢?
qml组件显示到屏幕,就像把一层一层衣服摞起来。定义顺序会影响这个摞起来的顺序。
小的粉色矩形先定义,大的蓝色矩形后定义,导致大的蓝色矩形直接覆盖了小的粉色矩形。
这两个矩形换算成mousearea组件,大的mousearea覆盖掉了小的mousearea。这时点击小的mousearea区域,就只会触发大的mousearea.
因为渲染顺序错了,所以,重叠的时候才会响应异常。小的mousearea被大的覆盖,无法点击触发。
了解了组件层级的概念
怎么更改qml组件的层级顺序?
1.修改定义顺序
2.在可视组件中,还可修改z属性的值
可视组件继承于Item都会有xyz属性
z属性则是用来表示垂直于屏幕的一轴,决定各个组件所处的先后位置
其值取值范围可以是正数负数,z值相同代表组件处同一个层级,所有的组件z的值默认都是0
具体规则如下:
1.z值不同:z值更小者会处于更底层位置,z值更大者会处于渲染结果的表面,也就是用户看到的这一侧。
2.z值相同:由组件定义顺序决定组件的渲染顺序。先定义的先被渲染,后定义的后被渲染,最后定义的将处于结果的表层
多个mousearea重叠响应异常解决方法
到此,解决方法依然明了:
要么设定z值,保证要触发的组件在最高层级,或者调整定义顺序,来让它处于表面。
特别注意
这个方法主要用来避免大组件覆盖小组件
多个组件叠加,事件传递肯定是需要分先后的,不可能让所有组件同时处理事件,就是说在区域内点击,只有最表面的那个会响应点击事件
如果有特殊需求,让区域内重叠的多个mousearea 或 button都能响应点击事件
就需要一层一层地传递鼠标事件
设置方法如下
(别的博文更详细,我这里简单复述一下):
设置属性propagateComposedEvents=true
并在槽函数内接收鼠标事件对象mouse,设置mouse.accepted=false,表示当前鼠标事件没有被接收,可以继续向内传递到下一层,交给内层继续处理该类型鼠标事件
示例代码如下:
Rectangle {
width: 100
height: 100
MouseArea {
anchors.fill: parent
onClicked: console.log("clicked yellow")
}
Rectangle {
color: "blue"
width: 50
height: 50
MouseArea {
anchors.fill: parent
propagateComposedEvents: true
onClicked: {
console.log("clicked blue");
mouse.accepted = false;
}
}
}
}
更方便的鼠标事件处理方法 --- Handler
为了适应触屏设备,qt qml推出了一种更为简洁方便的鼠标键盘事件处理方法
最大的好处就是定义简洁方便,功能明确。彼此事件独立,能够避免像mousearea重叠时,事件传递异常,事件处理繁琐的问题。
这些组件作为子组件添加到父组件中,就可以让父组件具备对应的功能,示例如下:
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Test handler conponent")
//pink rectangle
Rectangle{
id:rectangle1
width: 100
height: 200
color: "pink"
TapHandler{
onTapped: {
console.log("rectangle1")
}
}
}
//lightblue rectangle
Rectangle{
id:rectangle2
anchors.fill: parent
color:"lightblue"
TapHandler{
onTapped: {
console.log("rectangle2")
}
}
}
}
仍然以开头的例子举例,这里的蓝色矩形覆盖掉了粉色矩形,按照之前的介绍,
如果使用mousearea/button方法,不做任何处理,这里只能是rectangle2打印出log信息
但是使用handler组件,点击重叠区域,两个log信息都被打印,轻松解决问题
handler组件默认作用于父组件,也可以设置target来设定要作用的目标组件,结果只需编写一个响应事件的槽函数即可完成任务。
操作上更便捷
handler类型很多,当前qt6.7版本中,常见的handler组件如下:
1.DragHandler{} //处理拖拽事件,对所有基于item的组件有效
2.TapHandler{} //处理点击事件
3.WheelHandler{} //用于处理鼠标滚轮事件的组件。
4.KeyEvent{} //用于处理键盘事件的组件
5.HoverHandler{} // 用于处理鼠标悬停事件的组件