一、效果展示
如本文的標題所示,這篇文章分析的demo是一個異形窗口,主要展示鼠標在和異形區域交互的使用,效果如圖1所示,當鼠標移動到白雲或者月亮上時,相應的物體會高亮,當鼠標按下時,物體會有一個放大的動畫效果,鼠標離開時恢復原樣。
圖1 月亮和雲朵
二、源碼分析
正式算起來,這是我分析的第五篇qml示例程序了,在這里他么有一個共同點,qml控件直接展示不了的東西都是使用C++類或者js函數來完成,比如這篇文章要講的異形區域判斷;qml demo分析(customgeometry-貝塞爾曲線)文章中的貝塞爾曲線繪制;qml demo分析(maroon-小游戲)小游戲中的代碼復雜邏輯使用js控制;qml demo分析(abstractitemmodel-數據分離)示例中的model結構等。那么從這幾篇文章中我們也能體會到qml不是一個人在戰斗,他更多的是在於ui展示,而具體的邏輯或者更為復雜的操作需要交給C++程序或者js代碼來完成,關於C++和qml混合編程、js和qml混合編程之前的代碼都有涉及,不了解的同學可以直接點擊相關鏈接進入。
本篇示例代碼相對來說比較簡單,主要就是兩部分:qml和C++
1、qml代碼
qml代碼中總共有3張圖片,先添加的圖片在下層顯示,如果你想要讓某一張圖片在上次顯示那么就需要在最后添加該組件。這3張圖片的行為是一模一樣,因此我只分析月亮這一個組件,代碼如下
1 //后添加的元素在上 2 Image { 3 id: moon 4 anchors.centerIn: parent 5 scale: moonArea.pressed ? 1.1 : 1.0//按下時 放大 6 opacity: moonArea.containsMouse ? 1.0 : 0.7//hover時 無透明度 7 source: Qt.resolvedUrl("images/moon.png") 8 9 MaskedMouseArea {//自定義組件 新增鼠標是否按下判斷、鼠標是否在區域內 10 id: moonArea 11 anchors.fill: parent 12 alphaThreshold: 0 13 maskSource: moon.source 14 } 15 16 Behavior on opacity {//透明度使用漸變 17 NumberAnimation { duration: 200 } 18 } 19 Behavior on scale {//放大使用漸變 20 NumberAnimation { duration: 100 } 21 } 22 }
上述代碼就是qml中關於月亮的展示,MaskedMouseArea組件是使用qmlRegisterType宏注冊到qml系統中的。本篇文章我就不在講解main函數了,如果忘記的同學可以到qml demo分析(customgeometry-貝塞爾曲線)文章中回顧。
2、C++代碼
本篇文章有一個自定義的C++類,主要是給qml程序提供鼠標按下、鼠標hover等狀態,頭文件如下
1 #include <QImage> 2 #include <QQuickItem> 3 4 class MaskedMouseArea : public QQuickItem 5 { 6 Q_OBJECT 7 Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged) 8 Q_PROPERTY(bool containsMouse READ containsMouse NOTIFY containsMouseChanged) 9 Q_PROPERTY(QUrl maskSource READ maskSource WRITE setMaskSource NOTIFY maskSourceChanged) 10 Q_PROPERTY(qreal alphaThreshold READ alphaThreshold WRITE setAlphaThreshold NOTIFY alphaThresholdChanged) 11 12 public: 13 MaskedMouseArea(QQuickItem *parent = 0); 14 15 bool contains(const QPointF &point) const;//重寫contains接口 判斷鼠標是否在在異形窗口內 默認實現判斷參數點是否在bounding rect內 16 17 //鼠標是否按下 配合Q_PROPERTY宏 可以被qml系統調用 例如:scale: rightCloudArea.pressed ? 1.1 : 1.0 18 bool isPressed() const { return m_pressed; } 19 bool containsMouse() const { return m_containsMouse; } 20 21 QUrl maskSource() const { return m_maskSource; } 22 void setMaskSource(const QUrl &source); 23 24 qreal alphaThreshold() const { return m_alphaThreshold; } 25 void setAlphaThreshold(qreal threshold); 26 27 signals: 28 void pressed();//自定義信號 在qml系統中均有OnPressed槽 29 void released(); 30 void clicked(); 31 void canceled(); 32 void pressedChanged(); 33 void maskSourceChanged(); 34 void containsMouseChanged(); 35 void alphaThresholdChanged(); 36 37 protected: 38 void setPressed(bool pressed); 39 void setContainsMouse(bool containsMouse); 40 41 void mousePressEvent(QMouseEvent *event); 42 void mouseReleaseEvent(QMouseEvent *event); 43 44 void hoverEnterEvent(QHoverEvent *event); 45 void hoverLeaveEvent(QHoverEvent *event); 46 47 void mouseUngrabEvent(); 48 49 private: 50 bool m_pressed; 51 QUrl m_maskSource; 52 QImage m_maskImage; 53 QPointF m_pressPoint; 54 qreal m_alphaThreshold; 55 bool m_containsMouse; 56 };
頭文件中的Q_PROPERTY如果忘記其含義,可以到qml demo分析(customgeometry-貝塞爾曲線)文章中了解,其中每一個Q_PROPERTY宏第一個參數為屬性,READ指定讀取屬性的接口,WRITE指定設置屬性的接口,NOTIFY指定當屬性改變時所觸發的信號,當然了這個屬性還有更多的其他功能,感興趣的同學可以自行上幫助文檔查閱。
1 bool MaskedMouseArea::contains(const QPointF &point) const 2 { 3 if (!QQuickItem::contains(point) || m_maskImage.isNull()) 4 return false; 5 6 QPoint p = point.toPoint(); 7 8 if (p.x() < 0 || p.x() >= m_maskImage.width() || 9 p.y() < 0 || p.y() >= m_maskImage.height()) 10 return false; 11 12 qreal r = qBound<int>(0, m_alphaThreshold * 255, 255); 13 return qAlpha(m_maskImage.pixel(p)) > r;//根據alpha值判斷 異形區域 14 }
自定義QQuickItem類,最重要的是判斷鼠標是否在區域內,也就是contains函數,該函數默認是判斷鼠標是否在組件所在矩形區域,為了讓交互行為更好,我們需要讓鼠標在月亮的圓上或者雲朵內才高亮物體,因此我們重寫了次接口,該接口是用圖片的alpha值來判斷,當該值達到一定透明度時,認為其不在區域內,具體代碼如上所示。
這篇示例代碼在Qt5.7.0_vs2013\Examples\Qt-5.7\quick\customitems\maskedmousearea目錄下。我使用的qt5.6.1-1版本,該版本為自行編譯版本,編譯參考:msvc2013編譯qt5.6源碼
三、相關文章
qml demo分析(abstractitemmodel-數據分離)
qml demo分析(customgeometry-貝塞爾曲線)