当前位置:   article > 正文

Qt6 QML Book/模型视图/委托_qml访问委托

qml访问委托

Delegate

委托

When it comes to using models and views in a custom user interface, the delegate plays a huge role in creating a look and behaviour. As each item in a model is visualized through a delegate, what is actually visible to the user are the delegates.

在自定义用户界面中,使用模型和视图时,委托在创建外观和行为方面扮演着重要角色。由于模型中的每个项目都是通过委托可视化的,因此用户实际上看到的是委托。

Each delegate gets access to a number of attached properties, some from the data model, others from the view. From the model, the properties convey the data for each item to the delegate. From the view, the properties convey state information related to the delegate within the view. Let's dive into the properties from the view.

每个委托都可以访问许多附加的属性,有些来自数据模型,有些来自视图。从模型看,属性将每个项的数据传递给委托项。从视图看,属性传递与视图中的委托相关的状态信息。让我们从视图中深入了解属性。

The most commonly used properties attached from the view are ListView.isCurrentItem and ListView.view. The first is a boolean indicating if the item is the current item, while the latter is a read-only reference to the actual view. Through access to the view, it is possible to create general, reusable delegates that adapt to the size and nature of the view in which they are contained. In the example below, the width of each delegate is bound to the width of the view, while the background color of each delegate depends on the attached ListView.isCurrentItem property.

视图提供的最常用附加属性是ListView.isCurrentItemListView.view。前者是一个布尔值,指示该项是否为当前项,而后者是对实际视图的只读引用。通过对视图的访问,可以创建通用的、可重用的委托,以适应包含委托的视图的大小和性质。在下面的示例中,每个委托项的宽度width都绑定到视图的宽度width,而每个委托项的背景色color取决于附加属性ListView.isCurrentItem

  1. import QtQuick
  2. Rectangle {
  3. width: 120
  4. height: 300
  5. gradient: Gradient {
  6. GradientStop { position: 0.0; color: "#f6f6f6" }
  7. GradientStop { position: 1.0; color: "#d7d7d7" }
  8. }
  9. ListView {
  10. anchors.fill: parent
  11. anchors.margins: 20
  12. focus: true
  13. model: 100
  14. delegate: numberDelegate
  15. spacing: 5
  16. clip: true
  17. }
  18. Component {
  19. id: numberDelegate
  20. Rectangle {
  21. id: wrapper
  22. required property int index
  23. width: ListView.view.width
  24. height: 40
  25. color: ListView.isCurrentItem ? "#157efb" : "#53d769"
  26. border.color: Qt.lighter(color, 1.1)
  27. Text {
  28. anchors.centerIn: parent
  29. font.pixelSize: 10
  30. text: wrapper.index
  31. }
  32. }
  33. }
  34. }

If each item in the model is associated with an action, for instance, clicking an item acts upon it, that functionality is a part of each delegate. This divides the event management between the view, which handles the navigation between items in the view, and the delegate which handles actions on a specific item.

如果模型中的每个项都与某个行为关联,例如,单击某个项对其进行行为操作,则该功能是每个委托的一部分。这将事件管理分为视图(处理视图中项目之间的导航)和委托(处理特定项目上的行为操作)。

The most basic way to do this is to create a MouseArea within each delegate and act on the onClicked signal. This is demonstrated in the example in the next section of this chapter.

最基本的方法是在每个委托中创建一个MouseArea,并对onClicked信号进行响应操作。这将在本章下一节的示例中演示。

Animating Added and Removed Items
为添加和删除的项目设置动画

In some cases, the contents shown in a view changes over time. Items are added and removed as the underlying data model is altered. In these cases, it is often a good idea to employ visual cues to give the user a sense of direction and to help the user understand what data is added or removed.

在某些情况下,视图中显示的内容会随时间而变化。在更改基础数据模型时添加和删除项。在这些情况下,使用视觉提示给用户操作感,并帮助用户理解添加或删除的数据,通常是一个不错的主意。

Conveniently enough, QML views attach two signals, onAdd and onRemove, to each item delegate. By triggering animations from these, it is easy to create the movement necessary to aid the user in identifying what is taking place.

很方便,QML视图将两个信号onAdd和onRemove附加到每个委托项。通过触发这些,可以轻松创建必要的动画,以帮助用户识别正在发生的事情。

The example below demonstrates this through the use of a dynamically populated ListModel. At the bottom of the screen, a button for adding new items is shown. When it is clicked, a new item is added to the model using the append method. This triggers the creation of a new delegate in the view, and the emission of the GridView.onAdd signal. The SequentialAnimation called addAnimation is started from the signal causes the item to zoom into view by animating the scale property of the delegate.

下面的示例通过使用动态填充的ListModel演示了这一点。屏幕底部显示一个用于添加新项目的按钮。单击时,将使用append方法向模型中添加一个新项。这将触发在视图中创建新委托,并发出GridView.onAdd信号。id为addAnimation的串行动画(SequentialAnimation)从信号开始,通过设置委托的缩放属性的动画,使项目缩放到视图中。

  1. GridView.onAdd: addAnimation.start()
  2. SequentialAnimation {
  3. id: addAnimation
  4. NumberAnimation {
  5. target: wrapper
  6. property: "scale"
  7. from: 0
  8. to: 1
  9. duration: 250
  10. easing.type: Easing.InOutQuad
  11. }
  12. }

When a delegate in the view is clicked, the item is removed from the model through a call to the remove method. This causes the GridView.onRemove signal to be emitted, starting the removeAnimation SequentialAnimation. This time, however, the destruction of the delegate must be delayed until the animation has completed. To do this, PropertyAction element is used to set the GridView.delayRemove property to true before the animation, and false after. This ensures that the animation is allowed to complete before the delegate item is removed.

单击视图中的委托时,通过调用remove方法将该项从模型中移除。这将导致GridView.onRemove发出信号,启动removeAnimation串行动画(SequentialAnimation)。但是,这一次,代理的销毁必须延迟到动画完成。为此,PropertyAction元素在动画完成前,将GridView.delayRemove属性设置为true,在动画结束后设置为false。这样可以确保在删除委托项前,保证动画完成。

  1. GridView.onRemove: removeAnimation.start()
  2. SequentialAnimation {
  3. id: removeAnimation
  4. PropertyAction { target: wrapper; property: "GridView.delayRemove"; value: true }
  5. NumberAnimation { target: wrapper; property: "scale"; to: 0; duration: 250; easing.type: Easing.InOutQuad }
  6. PropertyAction { target: wrapper; property: "GridView.delayRemove"; value: false }
  7. }

Here is the complete code:

以下是完整的代码:

  1. import QtQuick
  2. Rectangle {
  3. width: 480
  4. height: 300
  5. gradient: Gradient {
  6. GradientStop { position: 0.0; color: "#dbddde" }
  7. GradientStop { position: 1.0; color: "#5fc9f8" }
  8. }
  9. ListModel {
  10. id: theModel
  11. ListElement { number: 0 }
  12. ListElement { number: 1 }
  13. ListElement { number: 2 }
  14. ListElement { number: 3 }
  15. ListElement { number: 4 }
  16. ListElement { number: 5 }
  17. ListElement { number: 6 }
  18. ListElement { number: 7 }
  19. ListElement { number: 8 }
  20. ListElement { number: 9 }
  21. }
  22. Rectangle {
  23. property int count: 9
  24. anchors.left: parent.left
  25. anchors.right: parent.right
  26. anchors.bottom: parent.bottom
  27. anchors.margins: 20
  28. height: 40
  29. color: "#53d769"
  30. border.color: Qt.lighter(color, 1.1)
  31. Text {
  32. anchors.centerIn: parent
  33. text: "Add item!"
  34. }
  35. MouseArea {
  36. anchors.fill: parent
  37. onClicked: {
  38. theModel.append({"number": ++parent.count})
  39. }
  40. }
  41. }
  42. GridView {
  43. anchors.fill: parent
  44. anchors.margins: 20
  45. anchors.bottomMargin: 80
  46. clip: true
  47. model: theModel
  48. cellWidth: 45
  49. cellHeight: 45
  50. delegate: numberDelegate
  51. }
  52. Component {
  53. id: numberDelegate
  54. Rectangle {
  55. id: wrapper
  56. required property int index
  57. required property int number
  58. width: 40
  59. height: 40
  60. gradient: Gradient {
  61. GradientStop { position: 0.0; color: "#f8306a" }
  62. GradientStop { position: 1.0; color: "#fb5b40" }
  63. }
  64. Text {
  65. anchors.centerIn: parent
  66. font.pixelSize: 10
  67. text: wrapper.number
  68. }
  69. MouseArea {
  70. anchors.fill: parent
  71. onClicked: {
  72. if (wrapper.index == -1) {
  73. return
  74. }
  75. theModel.remove(wrapper.index)
  76. }
  77. }
  78. GridView.onRemove: removeAnimation.start()
  79. SequentialAnimation {
  80. id: removeAnimation
  81. PropertyAction { target: wrapper; property: "GridView.delayRemove"; value: true }
  82. NumberAnimation { target: wrapper; property: "scale"; to: 0; duration: 250; easing.type: Easing.InOutQuad }
  83. PropertyAction { target: wrapper; property: "GridView.delayRemove"; value: false }
  84. }
  85. GridView.onAdd: addAnimation.start()
  86. SequentialAnimation {
  87. id: addAnimation
  88. NumberAnimation {
  89. target: wrapper
  90. property: "scale"
  91. from: 0
  92. to: 1
  93. duration: 250
  94. easing.type: Easing.InOutQuad
  95. }
  96. }
  97. }
  98. }
  99. }

Shape-Shifting Delegates

可形变的委托

A commonly used mechanism in lists is that the current item is expanded when activated. This can be used to dynamically let the item expand to fill the screen to enter a new part of the user interface, or it can be used to provide slightly more information for the current item in a given list.

列表中常用的一种机制是,当前项在激活时展开。这可以用于动态地让项目展开以填充屏幕,以进入用户界面的新部分,也可以用于为给定列表中的当前项目提供较多的信息显示。

In the example below, each item is expanded to the full extent of the ListView containing it when clicked. The extra space is then used to add more information. The mechanism used to control this is a state expanded that each item delegate can enter, where the item is expanded. In that state, a number of properties are altered.

在下面的示例中,单击时,每个项目都会扩展到包含它的ListView的全部范围。然后,额外的空间用于添加更多信息。用于控制种机制是每个委托项都可以进入的expanded状态,expanded状态时,其中项被展开。在这种状态下,许多属性都会发生变化。

First of all, the height of the wrapper is set to the height of the ListView. The thumbnail image is then enlarged and moved down to make it move from its small position into its larger position. In addition to this, the two hidden items, the factsView and closeButton are shown by altering the opacity of the elements. Finally, the ListView is setup.

首先,wrapper的高度height设置为ListView的高度。然后放大并向下移动缩略图图像,使其从小位置移动到大位置。除此之外,两个隐藏项factsView和closeButton通过改变元素的不透明度opacity来显示。最后,设置ListView。

Setting up the ListView involves setting the contentsY, that is the top of the visible part of the view, to the y value of the delegate. The other change is to set interactive of the view to false. This prevents the view from moving. The user can no longer scroll through the list or change the current item.

设置ListView涉及将contentsY(视图可见部分的顶部)设置为委托项的y值。另一个更改是将视图的交互interactive设置为false。这会阻止视图移动。用户无法再滚动列表或更改当前项目。

As the item first is clicked, it enters the expanded state, causing the item delegate to fill the ListView and the contents to rearrange. When the close button is clicked, the state is cleared, causing the delegate to return to its previous state and re-enabling the ListView.

第一次单击项目时,它进入expanded状态,导致项目委托填充ListView并重新排列内容。单击“关闭”按钮时,状态将被清除,从而使委托项返回到其以前的状态并重新启用ListView。

  1. import QtQuick
  2. Item {
  3. width: 300
  4. height: 480
  5. Rectangle {
  6. anchors.fill: parent
  7. gradient: Gradient {
  8. GradientStop { position: 0.0; color: "#4a4a4a" }
  9. GradientStop { position: 1.0; color: "#2b2b2b" }
  10. }
  11. }
  12. ListView {
  13. id: listView
  14. anchors.fill: parent
  15. delegate: detailsDelegate
  16. model: planets
  17. }
  18. ListModel {
  19. id: planets
  20. ListElement { name: "Mercury"; imageSource: "images/mercury.jpeg"; facts: "Mercury is the smallest planet in the Solar System. It is the closest planet to the sun. It makes one trip around the Sun once every 87.969 days." }
  21. ListElement { name: "Venus"; imageSource: "images/venus.jpeg"; facts: "Venus is the second planet from the Sun. It is a terrestrial planet because it has a solid, rocky surface. The other terrestrial planets are Mercury, Earth and Mars. Astronomers have known Venus for thousands of years." }
  22. ListElement { name: "Earth"; imageSource: "images/earth.jpeg"; facts: "The Earth is the third planet from the Sun. It is one of the four terrestrial planets in our Solar System. This means most of its mass is solid. The other three are Mercury, Venus and Mars. The Earth is also called the Blue Planet, 'Planet Earth', and 'Terra'." }
  23. ListElement { name: "Mars"; imageSource: "images/mars.jpeg"; facts: "Mars is the fourth planet from the Sun in the Solar System. Mars is dry, rocky and cold. It is home to the largest volcano in the Solar System. Mars is named after the mythological Roman god of war because it is a red planet, which signifies the colour of blood." }
  24. }
  25. Component {
  26. id: detailsDelegate
  27. Item {
  28. id: wrapper
  29. required property string name
  30. required property string imageSource
  31. required property string facts
  32. width: listView.width
  33. height: 30
  34. Rectangle {
  35. anchors.left: parent.left
  36. anchors.right: parent.right
  37. anchors.top: parent.top
  38. height: 30
  39. color: "#333"
  40. border.color: Qt.lighter(color, 1.2)
  41. Text {
  42. anchors.left: parent.left
  43. anchors.verticalCenter: parent.verticalCenter
  44. anchors.leftMargin: 4
  45. font.pixelSize: parent.height-4
  46. color: '#fff'
  47. text: wrapper.name
  48. }
  49. }
  50. Rectangle {
  51. id: image
  52. width: 26
  53. height: 26
  54. anchors.right: parent.right
  55. anchors.top: parent.top
  56. anchors.rightMargin: 2
  57. anchors.topMargin: 2
  58. color: "black"
  59. Image {
  60. anchors.fill: parent
  61. fillMode: Image.PreserveAspectFit
  62. source: wrapper.imageSource
  63. }
  64. }
  65. MouseArea {
  66. anchors.fill: parent
  67. onClicked: parent.state = "expanded"
  68. }
  69. Item {
  70. id: factsView
  71. anchors.top: image.bottom
  72. anchors.left: parent.left
  73. anchors.right: parent.right
  74. anchors.bottom: parent.bottom
  75. opacity: 0
  76. Rectangle {
  77. anchors.fill: parent
  78. gradient: Gradient {
  79. GradientStop { position: 0.0; color: "#fed958" }
  80. GradientStop { position: 1.0; color: "#fecc2f" }
  81. }
  82. border.color: '#000000'
  83. border.width: 2
  84. Text {
  85. anchors.fill: parent
  86. anchors.margins: 5
  87. clip: true
  88. wrapMode: Text.WordWrap
  89. color: '#1f1f21'
  90. font.pixelSize: 12
  91. text: wrapper.facts
  92. }
  93. }
  94. }
  95. Rectangle {
  96. id: closeButton
  97. anchors.right: parent.right
  98. anchors.top: parent.top
  99. anchors.rightMargin: 2
  100. anchors.topMargin: 2
  101. width: 26
  102. height: 26
  103. color: "#157efb"
  104. border.color: Qt.lighter(color, 1.1)
  105. opacity: 0
  106. MouseArea {
  107. anchors.fill: parent
  108. onClicked: wrapper.state = ""
  109. }
  110. }
  111. states: [
  112. State {
  113. name: "expanded"
  114. PropertyChanges { target: wrapper; height: listView.height }
  115. PropertyChanges { target: image; width: listView.width; height: listView.width; anchors.rightMargin: 0; anchors.topMargin: 30 }
  116. PropertyChanges { target: factsView; opacity: 1 }
  117. PropertyChanges { target: closeButton; opacity: 1 }
  118. PropertyChanges { target: wrapper.ListView.view; contentY: wrapper.y; interactive: false }
  119. }
  120. ]
  121. transitions: [
  122. Transition {
  123. NumberAnimation {
  124. duration: 200;
  125. properties: "height,width,anchors.rightMargin,anchors.topMargin,opacity,contentY"
  126. }
  127. }
  128. ]
  129. }
  130. }
  131. }

image

image

The techniques demonstrated here to expand the delegate to fill the entire view can be employed to make an item delegate shift shape in a much smaller way. For instance, when browsing through a list of songs, the current item could be made slightly larger, accommodating more information about that particular item.

此处演示是扩展委托项,以填充整个视图的技术,可用于使项目委托,以较小的形状移动。例如,当浏览歌曲列表时,当前项目可能会稍微变大,以容纳有关该特定项目的更多信息。

 示例源码下载

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/170704
推荐阅读
相关标签
  

闽ICP备14008679号