Qt6 QML Book/模型视图/技术进阶

Advanced Techniques

技术进阶

The PathView

The PathView element is the most flexible view provided in Qt Quick, but it is also the most complex. It makes it possible to create a view where the items are laid out along an arbitrary path. Along the same path, attributes such as scale, opacity and more can be controlled in detail.

PathView元素类型是Qt Quick中提供的最灵活的视图,但也是最复杂的。它可以创建一个视图,其中项目沿任意路径进行布局。沿着同一路径,可以详细控制缩放比例、不透明度等属性。

When using the PathView, you have to define a delegate and a path. In addition to this, the PathView itself can be customized through a range of properties. The most common being pathItemCount, controlling the number of visible items at once, and the highlight range control properties preferredHighlightBeginpreferredHighlightEnd and highlightRangeMode, controlling where along the path the current item is to be shown.

使用PathView时,必须定义委托和路径。除此之外,PathView还可以通过一系列属性自定义本身。最常见的是pathItemCount,一次控制的可见项数量,以及高亮显示范围控制属性preferredHighlightBegin、PreferredHighlighted和highlightRangeMode,控制当前项目沿路径显示的位置。

Before looking at the highlight range control properties in depth, we must look at the path property. The path property expects a Path element defining the path that the delegates follow as the PathView is being scrolled. The path is defined using the startX and startY properties in combinations with path elements such as PathLinePathQuad and PathCubic. These elements are joined together to form a two-dimensional path.

在深入查看高光范围控件属性之前,我们必须先查看路径属性path。path属性需要一个path元素类型来定义,在滚动PathView时委托项所遵循的路径。使用startX和startY属性以及路径元素类型(如PathLine、PathQuad和PathCubic)组合定义路径。这些元素连接在一起形成二维路径。

When the path has been defined, it is possible to further tune it using PathPercent and PathAttribute elements. These are placed in between path elements and provide more fine-grained control over the path and the delegates on it. The PathPercent controls how large a portion of the path that has been covered between each element. This, in turn, controls the distribution of delegates along the path, as they are distributed proportionally to the percentage progressed.

定义路径后,可以使用PathPercent和PathAttribute元素类型对其进行进一步细化。它们被放置在路径元素类型之间,并对路径及其委托提供更详细的控制。PathPercent控制每个元素之间覆盖的路径的大小。这反过来控制委托项沿路径的分布,因为它们是按进度百分比分布的。

This is where the preferredHighlightBegin and preferredHighlightEnd properties of the PathView enters the picture. They both expect real values in the range between zero and one. The end is also expected to be more or equal to the beginning. Setting both these properties too, for instance, 0.5, the current item will be displayed at the location fifty percent along the path.

这是PathView的preferredHighlightBegin和PreferredHighlighted属性进入图片的地方。它们实际值都在0到1之间。终点应大于或等于起点。同时设置这两个属性(例如,0.5),当前项目将显示在路径百分之五十的位置。

In the Path, the PathAttribute elements are placed between elements, just as PathPercent elements. They let you specify property values that are interpolated along the path. These properties are attached to the delegates and can be used to control any conceivable property.

在路径Path中,PathAttribute元素放置在各元素项之间,就像PathPercent元素一样。通过它们,可以沿路径插入指定属性值。这些属性附加到委托,可用于控制任何可能的属性。

Qt6 QML Book/模型视图/技术进阶_第1张图片

The example below demonstrates how the PathView element is used to create a view of cards that the user can flip through. It employs a number of tricks to do this. The path consists of three PathLine elements. Using PathPercent elements, the central element is properly centered and provided enough space not to be cluttered by other elements. Using PathAttribute elements, the rotation, size and z-value is controlled.

下面的示例演示如何使用PathView元素创建用户可以翻阅的卡片视图。它使用了许多技巧来实现。路径由三个路径线元素对象组成。使用PathPercent元素类型,中心元素将正确居中,并提供足够的空间,以免与其他元素重叠。使用PathAttribute元素,可以控制旋转、大小和z值。

In addition to the path, the pathItemCount property of the PathView has been set. This controls how densely populated the path will be. The preferredHighlightBegin and preferredHighlightEnd the PathView.onPath is used to control the visibility of the delegates.

除了路径path之外,还设置了PathView的pathItemCount属性。这将控制路径的元素密度。preferredHighlightBegin和preferredHighlightEnd用于控制委托项的可见性。

PathView {
    anchors.fill: parent

    model: 100
    delegate: flipCardDelegate

    path: Path {
        startX: root.width / 2
        startY: 0

        PathAttribute { name: "itemZ"; value: 0 }
        PathAttribute { name: "itemAngle"; value: -90.0; }
        PathAttribute { name: "itemScale"; value: 0.5; }
        PathLine { x: root.width / 2; y: root.height * 0.4; }
        PathPercent { value: 0.48; }
        PathLine { x: root.width / 2; y: root.height * 0.5; }
        PathAttribute { name: "itemAngle"; value: 0.0; }
        PathAttribute { name: "itemScale"; value: 1.0; }
        PathAttribute { name: "itemZ"; value: 100 }
        PathLine { x: root.width / 2; y: root.height * 0.6; }
        PathPercent { value: 0.52; }
        PathLine { x: root.width / 2; y: root.height; }
        PathAttribute { name: "itemAngle"; value: 90.0; }
        PathAttribute { name: "itemScale"; value: 0.5; }
        PathAttribute { name: "itemZ"; value: 0 }
    }

    pathItemCount: 16

    preferredHighlightBegin: 0.5
    preferredHighlightEnd: 0.5
}

The delegate, shown below, utilizes the attached properties itemZitemAngle and itemScale from the PathAttribute elements. It is worth noticing that the attached properties of the delegate only are available from the wrapper. Thus, the rotX property is defined to be able to access the value from within the Rotation element.

如下图所示,委托使用PathAttribute元素类型中的附加属性itemZ、itemAngle和itemScale。值得注意的是,委托的附加属性只能从wrapper中获得。因此,属性rotX被定义为能够从旋转元素对象中访问的值。

Another detail specific to PathView worth noticing is the usage of the attached PathView.onPath property. It is common practice to bind the visibility to this, as this allows the PathView to keep invisible elements for caching purposes. This can usually not be handled through clipping, as the item delegates of a PathView are placed more freely than the item delegates of ListView or GridView views.

值得注意的另一个特定细节是,附加属性PathView.onPath的使用。通常的做法是将可见性绑定到它,因为这允许PathView保留不可见的元素以进行缓存。这通常不能通过剪裁来处理,因为PathView的委托项,比ListView或GridView委托项有更高的自由度。

Component {
    id: flipCardDelegate

    BlueBox {
        id: wrapper

        required property int index
        property real rotX: PathView.itemAngle

        visible: PathView.onPath

        width: 64
        height: 64
        scale: PathView.itemScale
        z: PathView.itemZ

        antialiasing: true

        gradient: Gradient {
            GradientStop { position: 0.0; color: "#2ed5fa" }
            GradientStop { position: 1.0; color: "#2467ec" }
        }

        transform: Rotation {
            axis { x: 1; y: 0; z: 0 }
            angle: wrapper.rotX
            origin { x: 32; y: 32; }
        }

        text: wrapper.index
    }
}

When transforming images or other complex elements on in PathView, a performance optimization trick that is common to use is to bind the smooth property of the Image element to the attached property PathView.view.moving. This means that the images are less pretty while moving but smoothly transformed when stationary. There is no point spending processing power on smooth scaling when the view is in motion, as the user will not be able to see this anyway.

在PathView中转换图像或其他复杂元素时,常用的性能优化技巧是将图像Image元素类型的平滑属性smooth绑定到附加属性PathView.view.moving。这意味着图像在移动时不太漂亮,但在静止时平滑处理。当视图处于运动状态时,将处理能力花费在平滑缩放上是没有意义的,因为这时用户无论如何都无法看到。

TIP

Given the dynamic nature of PathAttribute, the qml tooling (in this case: qmllint) is not aware of itemZitemAngle nor itemScale.

鉴于PathAttribute的动态特性,qml工具(在本例中为QMLLIT)不知道itemZ、itemAngle和itemScale。

When using the PathView and changing the currentIndex programatically you might want to control the direction that the path moves in. You can do this using the movementDirection property. It can be set to PathView.Shortest, which is the default value. This means that the movement can be either direction, depending on which way is the closest way to move to the target value. The direction can instead be restricted by setting movementDirection to PathView.Negative or PathView.Positive.

使用PathView并以编程方式更改currentIndex时,您可能希望控制路径的移动方向。可以使用movementDirection属性执行此操作。可以将其设置为PathView.Shortest,这是默认值。这意味着移动可以是任意方向,具体取决于最接近目标值的移动方式。相反,可以通过将movementDirection设置为PathView.Negative或PathView.Positive来限制方向。

Table Models

表格模型

All views discussed until now present an array of items one way or another. Even the GridView expects the model to provide a one dimensional list of items. For two dimensional tables of data you need to use the TableView element.

到目前为止讨论的所有视图都以各种的方式一一呈现。GridView元素类型需要模型提供一维的项目列表。对于二维数据表,需要使用TableView元素类型。

The TableView is similar to other views in that it combines a model with a delegate to form a grid. If given a list oriented model, it displays a single column, making it very similar to the ListView element. However, it can also display two-dimensional models that explicitly define both columns and rows.

TableView与其他视图类似,它将模型model与委托delegate组合在一起以形成网格。如果给定一个面向列表的模型,它将显示一个列,这使得它与ListView元素非常相似。但是,它也可以是显式定义列和行的二维模型。

In the example below, we set up a simple TableView with a custom model exposed from C++. At the moment, it is not possible to create table oriented models directly from QML, but in the ‘Qt and C++’ chapter the concept is explained. The running example is shown in the image below.

在下面的示例中,我们设置了一个简单的TabLVIEW视图,其中包含了从C++暴露的自定义模型。目前,无法直接从QML创建面向表的模型,但在Qt和C++章节中对概念进行了解释。下图显示了正在运行中的示例。

Qt6 QML Book/模型视图/技术进阶_第2张图片

In the example below, we create a TableView and set the rowSpacing and columnSpacing to control the horizontal and vertical gaps between delegates. The rest of the properties are set up as for any other type of view.

在下面的示例中,我们创建一个TableView并设置rowSpacing和ColumnSpacking以控制委托项间的水平和垂直间隙。其余属性的设置与其他类型的视图相同。

TableView {
    id: view
    anchors.fill: parent
    anchors.margins: 20

    rowSpacing: 5
    columnSpacing: 5

    clip: true

    model: tableModel
    delegate: cellDelegate
}

The delegate itself can carry an implicit size through the implicitWidth and implicitHeight. This is what we do in the example below. The actual data contents, i.e. the data returned from the model’s display role.

委托项本身可以通过implicitWidth和implicitHeight传递隐式大小。这就是我们在下面的例子中所做的。实际数据内容,是从模型的display角色返回的数据。

Component {
    id: cellDelegate

    GreenBox {
        id: wrapper

        required property string display

        implicitHeight: 40
        implicitWidth: 40

        Text {
            anchors.centerIn: parent
            text: wrapper.display
        }
    }
}

It is possible to provide delegates with different sizes depending on the model contents, e.g.:

根据模型内容,也可以为委托项提供不同尺寸,例如:

GreenBox {
    implicitHeight: (1 + row) * 10
    // ...
}

Notice that both the width and the height must be greater than zero.

请注意,宽度和高度都必须大于零。

When providing an implicit size from the delegate, the tallest delegate of each row and the widest delegate of each column controls the size. This can create interesting behaviour if the width of items depend on the row, or if the height depends on the column. This is because not all delegates are instantiated at all times, so the width of a column might change as the user scrolls through the table.

当委托项提供隐式大小时,每行最高的委托项和每列最宽的委托项控制大小。如果项的宽度取决于行,或者高度取决于列,那么这会产生有趣的行为。这是因为并非所有委托项都是同时实例化的,因此当用户在表中滚动时,列的宽度可能会改变。

To avoid the issues with specifying column widths and row heights using implicit delegate sizes, you can provide functions that calculate these sizes. This is done using the columnWidthProvider and rowHeightProvider . These functions return the size of the width and row respectively as shown below:

为了避免使用隐式委托大小指定列宽和行高的问题,可以提供计算这些大小的函数。这里使用columnWidthProvider和rowHeightProvider实现的。这些函数分别返回宽度和高度的大小,如下所示:

TableView {
    columnWidthProvider: function (column) { return 10 * (column + 1) }
    // ...
}

If you need to dynamically change the column widths or row heights you must notify the view of this by calling the forceLayout method. This will make the view re-calculate the size and position of all cells.

如果需要动态更改列宽或行高,则必须通过调用forceLayout方法通知视图。这将使视图重新计算所有单元的大小和位置。

A Model from XML

基于XML的模型

As XML is a ubiquitous data format, QML provides the XmlListModel element that exposes XML data as a model. The element can fetch XML data locally or remotely and then processes the data using XPath expressions.

由于XML是一种普遍存在的数据格式,QML提供了XmlListModel元素,该元素将XML数据作为模型公开。元素可以本地或远程获取XML数据,然后使用XPath表达式处理数据。

The example below demonstrates fetching images from an RSS flow. The source property refers to a remote location over HTTP, and the data is automatically downloaded.

下面的示例演示如何从RSS流获取图像。source属性引用HTTP上的远程位置,数据将自动下载。

Qt6 QML Book/模型视图/技术进阶_第3张图片

When the data has been downloaded, it is processed into model items and roles. The query property of the XmlListModel is an XPath representing the base query for creating model items. In this example, the path is /rss/channel/item, so for every item tag, inside a channel tag, inside an RSS tag, a model item is created.

下载数据后,将其处理为模型项和角色。XmlListModel的查询属性query是一个XPath,表示用于创建模型项的基本查询。在本例中,路径是/rss/channel/item,因此对于每个项标签,在通道标签内、在rss标签内,都会创建一个模型项目。

For every model item, a number of roles are extracted. These are represented by XmlListModelRole elements. Each role is given a name, which the delegate can access through an attached property. The actual value of each such property is determined through the elementName and (optional) attributeName properties for each role. For instance, the title property corresponds to the title XML element, returning the contents between the </code> and <code> tags.

对于每个模型项,都会提取一些角色。这些由XmlListModelRole元素表示。每个角色都有一个名称,委托可以通过附加的属性访问该名称。此类属性的实际值是通过每个角色的elementName和attributeName(可选)属性确定的。例如,title属性对应于title XML元素,返回标签之间的内容。

The imageSource property extracts the value of an attribute of a tag instead of the contents of the tag. In this case, the url attribute of the enclosure tag is extracted as a string. The imageSource property can then be used directly as the source for an Image element, which loads the image from the given URL.

imageSource属性提取标签的属性值,而不是标签的内容。在这种情况下,enclosure标签的url特性被提取为字符串。然后,imageSource属性可以直接用作图像元素类型Image的源source,该元素类型从给定URL加载图像。

import QtQuick
import QtQml.XmlListModel
import "../common"

Background {
    width: 300
    height: 480

    Component {
        id: imageDelegate

        Box {
            id: wrapper

            required property string title 
            required property string imageSource

            width: listView.width
            height: 220
            color: '#333'

            Column {
                Text {
                    text: wrapper.title
                    color: '#e0e0e0'
                }
                Image {
                    width: listView.width
                    height: 200
                    fillMode: Image.PreserveAspectCrop
                    source: wrapper.imageSource
                }
            }
        }
    }

    XmlListModel {
        id: imageModel

        source: "https://www.nasa.gov/rss/dyn/image_of_the_day.rss"
        query: "/rss/channel/item"

        XmlListModelRole { name: "title"; elementName: "title" }
        XmlListModelRole { name: "imageSource"; elementName: "enclosure"; attributeName: "url"; }
    }

    ListView {
        id: listView
        anchors.fill: parent
        model: imageModel
        delegate: imageDelegate
    }
}

Lists with Sections

带小节的列表

Sometimes, the data in a list can be divided into sections. It can be as simple as dividing a list of contacts into sections under each letter of the alphabet or music tracks under albums. Using a ListView it is possible to divide a flat list into categories, providing more depth to the experience.

有时,列表中的数据可以分为多个部分。它可以像将联系人列表划分为字母表中每个字母下的部分或相册中的音乐曲目一样简单。使用ListView,可以将平面列表划分为多个类别,从而提供深度体验。

Qt6 QML Book/模型视图/技术进阶_第4张图片

In order to use sections, the section.property and section.criteria must be set up. The section.property defines which property to use to divide the contents into sections. Here, it is important to know that the model must be sorted so that each section consists of continuous elements, otherwise, the same property name might appear in multiple locations.

要使用小节,必须要设置section.property和section.criteriasection.property定义使用哪个属性将内容划分为节。在这里,要知道,必须对模型进行排序很重要,以便每个部分由连续的元素组成,否则,相同的属性名称可能会出现在多个位置。

The section.criteria can be set to either ViewSection.FullString or ViewSection.FirstCharacter. The first is the default value and can be used for models that have clear sections, for example, tracks of music albums. The latter takes the first character of a property and means that any property can be used for this. The most common example being the last name of contacts in a phone book.

section.criteria设置为ViewSection.FullStringViewSection.FirstCharacter。前者是默认值,可用于具有明确区分的模型,例如,音乐专辑的曲目。后者采用属性的第一个字符,意味着任何属性都可以用于此目的。最常见的例子是电话簿中联系人的姓氏。

When the sections have been defined, they can be accessed from each item using the attached properties ListView.sectionListView.previousSection and ListView.nextSection. Using these properties, it is possible to detect the first and last item of a section and act accordingly.

定义小节后,可以使用附加属性ListView.section、ListView.previousSection、ListView.nextSection让每个项相互访问。使用这些属性,可以检测小节的第一项或最后一项,并执行相应操作。

It is also possible to assign a section delegate component to the section.delegate property of a ListView. This creates a section header delegate which is inserted before any items of a section. The delegate component can access the name of the current section using the attached property section.

也可以将小节委托组件指定给ListView的section.delegate属性。这将创建一个小节头的委托,该委托插入到节的各项之前。委托组件可以使用附加属性section访问当前节的名称。

The example below demonstrates the section concept by showing a list of spacemen sectioned after their nationality. The nation is used as the section.property. The section.delegate component, sectionDelegate, shows a heading for each nation, displaying the name of the nation. In each section, the names of the spacemen are shown using the spaceManDelegate component.

下面的示例,通过按国籍划分显示宇航员列表,来演示小节的概念。以nation作section.property值section.delegate组件sectionDelegate显示每个国家的名称。在每个小节中,使用spaceManDelegate组件显示宇航员的姓名。

import QtQuick
import "../common"

Background {
    width: 300
    height: 290

    ListView {
        anchors.fill: parent
        anchors.margins: 20

        clip: true

        model: spaceMen

        delegate: spaceManDelegate

        section.property: "nation"
        section.delegate: sectionDelegate
    }

    Component {
        id: spaceManDelegate

        Item {
            id: spaceManWrapper
            required property string name
            width: ListView.view.width
            height: 20
            Text {
                anchors.left: parent.left
                anchors.verticalCenter: parent.verticalCenter
                anchors.leftMargin: 8
                font.pixelSize: 12
                text: spaceManWrapper.name
                color: '#1f1f1f'
            }
        }
    }

    Component {
        id: sectionDelegate

        BlueBox {
            id: sectionWrapper
            required property string section
            width: ListView.view ? ListView.view.width : 0
            height: 20
            text: sectionWrapper.section
            fontColor: '#e0e0e0'
        }
    }


    ListModel {
        id: spaceMen

        ListElement { name: "Abdul Ahad Mohmand"; nation: "Afganistan"; }
        ListElement { name: "Marcos Pontes"; nation: "Brazil"; }
        ListElement { name: "Alexandar Panayotov Alexandrov"; nation: "Bulgaria"; }
        ListElement { name: "Georgi Ivanov"; nation: "Bulgaria"; }
        ListElement { name: "Roberta Bondar"; nation: "Canada"; }
        ListElement { name: "Marc Garneau"; nation: "Canada"; }
        ListElement { name: "Chris Hadfield"; nation: "Canada"; }
        ListElement { name: "Guy Laliberte"; nation: "Canada"; }
        ListElement { name: "Steven MacLean"; nation: "Canada"; }
        ListElement { name: "Julie Payette"; nation: "Canada"; }
        ListElement { name: "Robert Thirsk"; nation: "Canada"; }
        ListElement { name: "Bjarni Tryggvason"; nation: "Canada"; }
        ListElement { name: "Dafydd Williams"; nation: "Canada"; }
    }
}

The ObjectModel

In some cases you might want to use a list view for a large set of different items. You can solve this using dynamic QML and Loader, but another options is to use an ObjectModel from the QtQml.Models module. The object model is different from other models as it lets you put the actual visual elements side the model. That way, the view does not need any delegate.

在某些情况下,您可能希望对大量的项使用列表视图。您可以使用动态QML和Loader元素类型解决这个问题,但另一个选择是使用QtQml.Models模块中的ObjectModel。对象模型与其他模型不同,因为它允许您将实际的可见元素放在模型里。这样,视图就不需要任何委托。

Qt6 QML Book/模型视图/技术进阶_第5张图片

In the example below we put three Rectangle elements into the ObjectModel. However, one rectangle has a Text element child while the last one has rounded corners. This would have resulted in a table-style model using something like a ListModel. It would also have resulted in empty Text elements in the model.

在下面的示例中,我们将三个矩形Rectangle元素放入ObjectModel中。但是,一个矩形具有文本Text元素子元素,而最后一个矩形具有圆角。这将产生一个使用类似ListModel的表样式模型。它还会导致模型中出现空文本Text元素。

import QtQuick
import QtQml.Models

Rectangle {
    width: 320
    height: 320
    
    gradient: Gradient {
        GradientStop { position: 0.0; color: "#f6f6f6" }
        GradientStop { position: 1.0; color: "#d7d7d7" }
    }
    
    ObjectModel {
        id: itemModel
        
        Rectangle { height: 60; width: 80; color: "#157efb" }
        Rectangle { height: 20; width: 300; color: "#53d769" 
            Text { anchors.centerIn: parent; color: "black"; text: "Hello QML" }
        }
        Rectangle { height: 40; width: 40; radius: 10; color: "#fc1a1c" }
    }
    
    ListView {
        anchors.fill: parent
        anchors.margins: 10
        spacing: 5
        
        model: itemModel
    }
}

Another aspect of the ObjectModel is that is can be dynamically populated using the getinsertmoveremove, and clear methods. This way, the contents of the model can be dynamically generated from various sources and still easily shown in a single view.

ObjectModel的另一个方面是可以使用get、insert、move、remove和clear方法动态填充。通过这种方式,模型的内容可以从各种源动态生成,并且仍然可以轻松地在单个视图中显示。

Models with Actions

带有行为的模型

The ListElement type supports the binding of Javascript functions to properties. This means that you can put functions into a model. This is very useful when building menus with actions and similar constructs.

ListElement元素类型支持将Javascript函数绑定到属性。这意味着您可以将函数放入模型中。这在使用行为和类似构造构建菜单时非常有用。

The example below demonstrates this by having a model of cities that greet you in different ways. The actionModel is a model of four cities, but the hello property is bound to functions. Each function takes an argument value, but you can have any number arguments.

下面的例子通过一个以不同方式向您问候的城市模型来说明这一点。actionModel是四个城市的模型,但是hello属性绑定到函数。每个函数都有一个参数值value,但可以有任意数量的参数。

In the delegate actionDelegate, the MouseArea calls the function hello as an ordinary function and this results a call to the corresponding hello property in the model.

在委托actionDelegate中,MouseArea将函数hello作为普通函数调用,这将导致调用模型中相应的hello属性。

import QtQuick

Rectangle {
    width: 120
    height: 300

    gradient: Gradient {
        GradientStop { position: 0.0; color: "#f6f6f6" }
        GradientStop { position: 1.0; color: "#d7d7d7" }
    }
    
    ListModel {
        id: actionModel
        
        ListElement {
            name: "Copenhagen"
            hello: function(value) { console.log(value + ": You clicked Copenhagen!"); }
        }
        ListElement {
            name: "Helsinki"
            hello: function(value) { console.log(value + ": Helsinki here!"); }
        }
        ListElement {
            name: "Oslo"
            hello: function(value) { console.log(value + ": Hei Hei fra Oslo!"); }
        }
        ListElement {
            name: "Stockholm"
            hello: function(value) { console.log(value + ": Stockholm calling!"); }
        }
    }

    ListView {
        anchors.fill: parent
        anchors.margins: 20

        focus: true

        model: actionModel
        delegate: Rectangle {
            id: delegate

            required property int index
            required property string name
            required property var hello

            width: ListView.view.width
            height: 40

            color: "#157efb"

            Text {
                anchors.centerIn: parent
                font.pixelSize: 10
                text: delegate.name
            }
            
            MouseArea {
                anchors.fill: parent
                onClicked: delegate.hello(delegate.index)
            }
        }

        spacing: 5
        clip: true
    }
}

Tuning Performance

性能优化

The perceived performance of a view of a model depends very much on the time needed to prepare new delegates. For instance, when scrolling downwards through a ListView, delegates are added just outside the view from the bottom and are removed just as they leave sight over the top of the view. This becomes apparent if the clip property is set to false. If the delegates take too much time to initialize, it will become apparent to the user as soon as the view is scrolled too quickly.

模型视图的感知性能在很大程度上取决于准备新委托准备的时间。例如,当向下滚动ListView时,委托将从底部添加到视图的外部,并在离开视图顶部时移除。如果“clip”属性设置为false,则这一点更明显。如果委托项初始化花费的时间太长,则一旦视图滚动过快,用户就会发现这一点。

To work around this issue you can tune the margins, in pixels, on the sides of a scrolling view. This is done using the cacheBuffer property. In the case described above, vertical scrolling, it will control how many pixels above and below the ListView that will contain prepared delegates. Combining this with asynchronously loading Image elements can, for instance, give the images time to load before they are brought into view.

要解决此问题,可以调整滚动视图侧面的边距(以像素为单位)。这是使用cacheBuffer属性实现的。在上述情况下,垂直滚动将控制ListView,包含准备好的上方和下方委托的像素数。例如,将其与异步加载图像元素相结合,可以在图像进入视图之前为其提供加载时间。

Having more delegates sacrifices memory for a smoother experience and slightly more time to initialize each delegate. This does not solve the problem of complex delegates. Each time a delegate is instantiated, its contents are evaluated and compiled. This takes time, and if it takes too much time, it will lead to a poor scrolling experience. Having many elements in a delegate will also degrade the scrolling performance. It simply costs cycles to move many elements.

拥有更多的委托项会牺牲内存以获得更流畅的体验,并会有更多的时间初始化每个委托项。这并不能解决复杂委托的问题。每次实例化委托项时,都会对其内容进行计算和编译。这需要时间,如果需要太多时间,将导致较差的滚动体验。委托中有许多元素也会降低滚动性能。移动多项元素需消耗周期时间。

To remedy the two latter issues, it is recommended to use Loader elements. These can be used to instantiate additional elements when they are needed. For instance, an expanding delegate may use a Loader to postpone the instantiation of its detailed view until it is needed. For the same reason, it is good to keep the amount of JavaScript to a minimum in each delegate. It is better to let them call complex pieced of JavaScript that resides outside each delegate. This reduces the time spent compiling JavaScript each time a delegate is created.

为了解决后两个问题,建议使用加载器Loader元素类型。当需要时,可以使用这些元素来实例化其他元素。例如,扩展委托可以使用加载器Loader将其详细视图的实例化推迟到需要时。出于同样的原因,最好将每个委托中的JavaScript量保持在最小。最好让他们调用驻留在每个委托外部的复杂JavaScript片段。这减少了每次创建委托项时,编译JavaScript所花费的时间。

TIP

Be aware that using a Loader to postpone initialization does just that - it postpones a performance issue. This means that the scrolling performance will be improved, but the actual contents will still take time to appear.

请注意,使用加载程序Loader延迟初始化,只会延迟性能问题。这意味着滚动性能将得到改善,但实际内容仍需要时间才能显示。

  示例源码下载

你可能感兴趣的:(Qt6,QML,Book,qt,qt6,qml)