簡介
Component是Qt封裝好的、只暴露必要接口的QML類型,可以重復利用。一個QML組件就像一個黑盒子,它通過屬性、信號、函數和外部世界交互。
一個Component既可以定義在獨立的QML文件(.qml為后綴的文件)中,也可以嵌入到其他的QML文件中來定義。那么這兩種方式分別適用於什么場景呢?這里有一個原則可以幫助我們去選擇Component的定義方式:如果一個Component比較小且只在某個QML文件中使用,或者說一個Component從邏輯上來看屬於某個QML文檔,那么就可以采用嵌入的方式來定義該Component;如果這個Component有很多地方可以用到,也就是說復用率比較高,那么就可以采用定義在獨立的QML文件中的方式。下面說明一下這兩種實現Component方式的差別:
- 嵌入式定義Component:
要在一個QML文件中嵌入Component的定義,需要使用Component對象。
定義一個Component與定義一個QML文件類似,Component只能包含一個頂層Item,而且在這個Item之外不能定義任何數據,除了id。 在頂層Item之內,則可以包含更多的子元素來協同工作,最終形成一個具有特定功能的組件。
Component通常用來給一個View提供圖形化組件,比如ListVIew::delegate屬性就需要一個Component來指定如何顯示列表的每一個項,又比如ButtonStyle::background屬性也需要一個Component來指定如何繪制Button的背景。
Component不是Item的派生類,而是從QQmlComponent繼承而來的,雖然它通過自己的頂層Item為其他的view提供可視化組件,但它本身是不可見元素。你可以這么理解:你定義的組件是一個新的類型,他必須被實例化以后才能顯示。而要實例化一個嵌入在QML文件中定義的Component,則可以通過Loader。
- 在單獨文件中定義Component:
很多時候我們把一個Component單獨定義在一個QML文件中,比如Qt Qucik提供的BusyIndicator空間,其實就是在BusyIndicator中定義一個組件(BusyIndicator.qml):
Control { id: indicator /*! \qmlproperty bool BusyIndicator::running This property holds whether the busy indicator is currently indicating activity. \note The indicator is only visible when this property is set to \c true. The default value is \c true. */ property bool running: true Accessible.role: Accessible.Indicator Accessible.name: "busy" style: Settings.styleComponent(Settings.style, "BusyIndicatorStyle.qml", indicator) }
可以看到BusyIndicator的代碼非常簡單,只是給Control元素(Qt Quick定義的私有元素,用作其他控件的基類,如ComboBox、BusyIndicator等)增加了一個屬性,設置了幾個值而已。
BusyIndicator.qml文件中的頂層Item是Control,而我們使用時卻是以BusyIndicator為組件名(類名)。這是定義Component的一個約定:組件名必須和QML文件名一致,且組件名的首字母必須是大寫的。Qt Quick提供的多數基本元素和特性,都可以在定義組件時使用 。
例子:在一個單獨的QMl文件中定義顏色選擇組件ColorPicker,對應QML文件為ColorPicker.qml,可以在其他的QMl文件中使用Cololr{...}來創建ColorPicker的實例。
import QtQuick 2.6 Rectangle { id: colorPicker width: 50 height: 30 signal colorPicked(color clr); function configureBorder() { colorPicker.border.width = colorPicker.focus ? 2 : 0; colorPicker.border.color = colorPicker.focus ? "#90D750" : "#808080"; } MouseArea { anchors.fill: parent onClicked: { colorPicker.colorPicked(colorPicker.color); mouse.accepted = true; colorPicker.focus = true; } } Keys.onReturnPressed: { // 對應Enter鍵 console.log("ColorPicker:onReturnPressed"); colorPicker.colorPicked(colorPicker.color); event.accepted = true; } Keys.onSpacePressed: { // 對應Space鍵 console.log("ColorPicker:onSpacePressed"); colorPicker.colorPicked(colorPicker.color); event.accepted = true; } onFocusChanged: { console.log("ColorPicker:onFocusChanged"); configureBorder(); } Component.onCompleted: { console.log("ColorPicker:onCompleted"); configureBorder(); } }
在單獨文件中定義Component,與嵌入式定義有明顯的不同:Component對象不見了,是因為在單獨文件中定義組件,不需要Component對象,只有在其他QML文件中嵌入式定義組件時才需要Component對象。
main.qml內容:
import QtQuick 2.6 import QtQuick.Window 2.2 import QtQuick.Controls 1.4 Window { visible: true width: 320 height: 240 title: qsTr("Component") Rectangle { width: parent.width height: parent.height color: "#EEEEEE" Text { id: coloredText anchors.centerIn: parent anchors.top: parent.top anchors.topMargin: 4 text: "ColorPicker" font.pixelSize: 32 } function setTextColor(clr) { coloredText.color = clr; } ColorPicker { id: redColor color: "red" focus: true width: parent.width / 3 - 4 anchors.left: parent.left anchors.leftMargin: 4 anchors.bottom: parent.bottom anchors.bottomMargin: 4 KeyNavigation.right: blueColor KeyNavigation.tab: blueColor onColorPicked: { coloredText.color = clr; } } ColorPicker { id: blueColor color: "blue" width: parent.width / 3 - 4 anchors.left: redColor.right anchors.leftMargin: 4 anchors.bottom: parent.bottom anchors.bottomMargin: 4 KeyNavigation.left: redColor KeyNavigation.right: pinkColor KeyNavigation.tab: pinkColor } ColorPicker { id: pinkColor color: "pink" width: parent.width / 3 - 8 anchors.left: blueColor.right anchors.leftMargin: 4 anchors.bottom: parent.bottom anchors.bottomMargin: 4 KeyNavigation.left: blueColor KeyNavigation.tab: redColor } Component.onCompleted: { blueColor.colorPicked.connect(setTextColor); pinkColor.colorPicked.connect(setTextColor); } } }
《Qt Quick核心編程》