首页
GitHub
在QML中拖动无边框窗口时“抖动”的问题

# 一般实现

通常拖动的实现方法如下,会导致在某些系统上窗口“抖动”:

    ApplicationWindow {
    id: mainWindow
    visible: true
    width: 640
    height: 480
    title: qsTr("Window")
    flags: Qt.FramelessWindowHint
    header: ToolBar {
        MouseArea{
            anchors.fill: parent
            property variant pressedPos: "0,0"

            onPressed: {
                pressedPos  = Qt.point(mouse.x, mouse.y)
            }

            onPositionChanged: {
                    var delta = Qt.point(mouse.x - pressedPos.x, mouse.y - pressedPos.y);
                    mainWindow.x += delta.x;
                    mainWindow.y += delta.y;
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 解决方法1:

使用c++实现一个鼠标位置的帮助类,并将其对象暴露给QML上下文属性,来解决这个问题。这个方法在原先“抖动”的系统上,使得复杂的QML UI也能表现良好:

  1. 一个c++帮助类的实现:
class CursorPosProvider : public QObject
{
    Q_OBJECT
public:
    explicit CursorPosProvider(QObject *parent = nullptr) : QObject(parent)
    {
    }
    virtual ~CursorPosProvider() = default;

    Q_INVOKABLE QPointF cursorPos()
    {
        return QCursor::pos();
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14

非常简单的一个类,只从c++端提供一个鼠标位置。这有些奇怪,在QML中使用相同的操作时,在一些系统上会遇到“抖动”问题。

  1. 将这个类注册为context属性暴露给QML:
// ...
#include "CursorPosProvider.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    CursorPosProvider mousePosProvider; 
    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("mousePosition", &mousePosProvider);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  1. 给无边框窗口实现一个Qt Quick标题栏组件TitleBar.qml
Rectangle {
    id: control
    width: parent.width
    color: "#2196F3" // 随便给的一个颜色

    property QtObject container
    // TODO: 一些扩展属性或信号
    // ...

    MouseArea {
        id: titleBarMouseRegion
        property var pressedPos
        anchors.fill: parent
        onPressed: {
            pressedPos = { x: mouse.x, y: mouse.y }
        }
        onPositionChanged: {
            container.x = mousePosition.cursorPos().x - pressedPos.x
            container.y = mousePosition.cursorPos().y - pressedPos.y
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

4.现在可以在Window组件下使用这个TitleBar组件了:

Window {
    id: root
    visible: true
    flags: Qt.FramelessWindowHint

    TitleBar {
        height: 20
        container: root
    }

    Text {
        text: qsTr("Hello World")
        anchors.centerIn: parent
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

补充:当使用无边框窗口拖动时,UI中的动画会停止刷新(在Mac OS,其他系统未测试),直到拖动被释放,此时拖动过程中的动画侦被跳过了。而拖动原生窗口的标题栏不会导致这种问题。所以,有必要提出解决方法2

# 解决方法2:

使用系统API(待续...)