Qt6 QML Book/模型视图/动态的视图

Dynamic Views

动态的视图

Repeaters work well for limited and static sets of data, but in the real world, models are commonly more complex – and larger. Here, a smarter solution is needed. For this, Qt Quick provides the ListView and GridView elements. These are both based on a Flickable area, so the user can move around in a larger dataset. At the same time, they limit the number of concurrently instantiated delegates. For a large model, that means fewer elements in the scene at once.

重复器Repeater适用于有限的静态数据集,但在现实世界中,模型通常更复杂——也更大。在就需要一个更智能的解决方案。为此,Qt Quick提供了ListView和GridView元素类型。这两个都基于一个Flickable区域,因此用户可以在更大的数据集中移动。同时,它们限制了并发实例化委托的数量。对于大型模型,这意味着场景中,同一时间会实例化较少的元素对象。

image

The two elements are similar in their usage. We will begin with the ListView and then describe the GridView with the former as the starting point of the comparison. Notice that the GridView places a list of items into a two-dimensional grid, left-to-right or top-to-bottom. If you want to show a table of data you need to use the TableView which is described in the Table Models section.

这两个元素类型的用法相似。我们将从ListView开始,然后讲述GridView,前者作为比较的起点。请注意,GridView将项目列表放置在二维网格中,从左到右或从上到下。如果要显示数据表,需要使用“表格模型”部分中,讲述的TableView。

The ListView is similar to the Repeater element. It uses a model, instantiates a delegate and between the delegates, there can be spacing. The listing below shows how a simple setup can look.

ListView类似于Repeater元素类型。它使用一个model模型,实例化一个delegate委托,委托之间可以有间距spacing。下面的列表显示了一个简单的设置生效后的外观。

import QtQuick
import "../common"

Background {
    width: 80
    height: 300

    ListView {
        anchors.fill: parent
        anchors.margins: 20
        clip: true
        model: 100
        delegate: GreenBox {
            required property int index
            width: 40
            height: 40
            text: index
        }
        spacing: 5
    }
}

If the model contains more data than can fit onto the screen, the ListView only shows part of the list. However, as a consequence of the default behavior of Qt Quick, the list view does not limit the screen area within which the delegates are shown. This means that delegates may be visible outside the list view and that the dynamic creation and destruction of delegates outside the list view is visible to the user. To prevent this, clipping must be activated on the ListView element by setting the clip property to true. The illustration below shows the result of this (left view), compared to when the clip property is false (right view).

如果模型包含的数据超过了屏幕所能容纳的大小,则ListView仅显示列表的一部分。但是,由于Qt Quick的默认行为,列表视图不限制显示委托的屏幕区域。这意味着委托可能在列表视图外部可见,并且用户可以看到在列表视图外部动态创建和销毁委托。为了防止出现这种情况,必须将clip属性设置为true,来激活ListView元素上的剪裁。下图显示了clip属性为true(左图)时的结果,可比较clip属性为false(右图)时的结果。

To the user, the ListView is a scrollable area. It supports kinetic scrolling, which means that it can be flicked to quickly move through the contents. By default, it also can be stretched beyond the end of contents, and then bounces back, to signal to the user that the end has been reached.

对于用户来说,ListView是一个可滚动的区域。它支持动态滚动,这意味着它可以快速移动内容。默认情况下,它也可以拉伸到内容的结尾之外,然后反弹回来,并向用户发出已到达结尾的信号。 

The behavior at the end of the view is controlled using the boundsBehavior property. This is an enumerated value and can be conimaged from the default behavior, Flickable.DragAndOvershootBounds, where the view can be both dragged and flicked outside its boundaries, to Flickable.StopAtBounds, where the view never will move outside its boundaries. The middle ground, Flickable.DragOverBounds lets the user drag the view outside its boundaries, but flicks will stop at the boundary.

视图末端的行为使用boundsBehavior属性进行控制。这是一个枚举值,默认行为是Flickable.DragAndOvershootBounds,视图可以在其边界外拖动和弹动;Flickable.StopAtBounds,视图永远不会移动到其边界之外;还有折中的,Flickable.DragOverBounds允许用户将视图拖动到其边界之外,但弹动将在边界处停止。

It is possible to limit the positions where a view is allowed to stop. This is controlled using the snapMode property. The default behavior, ListView.NoSnap, lets the view stop at any position. By setting the snapMode property to ListView.SnapToItem, the view will always align the top of an item with its top. Finally, the ListView.SnapOneItem, the view will stop no more than one item from the first visible item when the mouse button or touch was released. The last mode is very handy when flipping through pages.

可以限制允许视图停止的位置。这是使用snapMode属性控制的。默认行为是ListView.NoSnap,使视图在任何位置停止。通过将snapMode属性设置为ListView.SnapToItem,视图将始终将项目的顶部与其顶部对齐。最后是ListView.SnapOneItem,当释放鼠标按钮或触摸时,视图将从第一个可见项目停止最多一个项目。最后一种模式在翻页时非常方便。

Orientation

方向

The list view provides a vertically scrolling list by default, but horizontal scrolling can be just as useful. The direction of the list view is controlled through the orientation property. It can be set to either the default value, ListView.Vertical, or to ListView.Horizontal. A horizontal list view is shown below.

默认情况下,列表视图提供垂直滚动列表,但水平滚动也同样有用。列表视图的方向通过orientation 属性控制。可以将其设置为默认值ListView.Vertical,或ListView.Horizontal。水平的水平列表视图如下所示。

import QtQuick
import "../common"

Background {
    width: 480
    height: 80

    ListView {
        anchors.fill: parent
        anchors.margins: 20
        spacing: 4
        clip: true
        model: 100
        orientation: ListView.Horizontal
        delegate: GreenBox {
            required property int index
            width: 40
            height: 40
            text: index
        }
    }
}

As you can tell, the direction of the horizontal flows from the left to the right by default. This can be controlled through the layoutDirection property, which can be set to either Qt.LeftToRight or Qt.RightToLeft, depending on the flow direction.

可以看出,默认情况下,水平方向从左向右流动。这可以通过layoutDirection属性进行控制,该属性可以根据流向,设置为Qt.LeftToRightQt.RightToLeft

Keyboard Navigation and Highlighting

键盘导航和高亮显示

When using a ListView in a touch-based setting, the view itself is enough. In a scenario with a keyboard, or even just arrow keys to select an item, a mechanism to indicate the current item is needed. In QML, this is called highlighting.

在基于触摸设置中使用ListView时,视图本身就足够了。在使用键盘或方向键选择项目的场景中,需要一种机制来指示当前项目。在QML中,这称为高亮显示。

Views support a highlight delegate which is shown in the view together with the delegates. It can be considered an additional delegate, only that it is only instantiated once, and is moved into the same position as the current item.

视图支持高亮显示委托,高亮显示委托与项委托一起显示在视图中。它可以被视为一个额外的委托,它只被实例化一次,并被移动到当前项的位置。

In the example below this is demonstrated. There are two properties involved for this to work. First, the focus property is set to true. This gives the ListView the keyboard focus. Second, the highlight property is set to point out the highlighting delegate to use. The highlight delegate is given the xy and height of the current item. If the width is not specified, the width of the current item is also used.

下面的示例对此进行了演示。这需要两个属性才能工作。首先,focus属性设置为true。这为ListView提供了键盘焦点。其次,highlight属性设置为要使用的高亮显示委托。高亮显示代理将获得当前项的x、y和高度。如果未指定宽度,则将使用当前项目的宽度。

In the example, the ListView.view.width attached property is used for width. The attached properties available to delegates are discussed further in the delegate section of this chapter, but it is good to know that the same properties are available to highlight delegates as well.

在本例中,ListView.view.width附加属性作为宽度。本章的“委托”部分将进一步讨论委托可用的附加属性,但最好知道,同样的属性也可用于高亮显示委托。

import QtQuick
import "../common"

Background {
    width: 240
    height: 300

    ListView {
        id: view

        anchors.fill: parent
        anchors.margins: 20

        focus: true

        model: 100
        delegate: numberDelegate
        highlight: highlightComponent
        
        spacing: 5
        clip: true
    }

    Component {
        id: highlightComponent

        GreenBox {
            width: ListView.view ? ListView.view.width : 0
        }
    }

    Component {
        id: numberDelegate

        Item {
            id: wrapper

            required property int index

            width: ListView.view ? ListView.view.width : 0
            height: 40

            Text {
                anchors.centerIn: parent
                font.pixelSize: 10
                text: wrapper.index
            }
        }
    }
}

When using a highlight in conjunction with a ListView, a number of properties can be used to control its behavior. The highlightRangeMode controls how the highlight is affected by what is shown in the view. The default setting, ListView.NoHighlightRange means that the highlight and the visible range of items in the view not being related at all.

将高亮显示与ListView结合使用时,可以使用许多属性来控制其行为。highlightRangeMode控制高光如何受视图中显示内容的影响。默认设置为ListView.NoHighlightRange,表示视图中项目的高光和可见范围无关连。

The value ListView.StrictlyEnforceRange ensures that the highlight is always visible. If an action attempts to move the highlight outside the visible part of the view, the current item will change accordingly, so that the highlight remains visible.

值为ListView.StrictlyEnforceRange确保高光始终可见。如果有操作试图将高光移到视图的可见部分外,则当前项将相应地更改,以便高光保持可见。

The middle ground is the ListView.ApplyRange value. It attempts to keep the highlight visible but does not alter the current item to enforce this. Instead, the highlight is allowed to move out of view if necessary.

折中的值是ListView.ApplyRange。它尝试保持高亮显示可见,但不会更改当前项以强制执行此操作。相反,如果需要,高光可以移出视图。

In the default configuration, the view is responsible for moving the highlight into position. The speed of the movement and resizing can be controlled, either as a velocity or as a duration. The properties involved are highlightMoveSpeedhighlightMoveDurationhighlightResizeSpeed and highlightResizeDuration. By default, the speed is set to 400 pixels per second, and the duration is set to -1, indicating that the speed and distance control the duration. If both a speed and a duration is set, the one that results in the quickest animation is chosen.

在默认配置中,视图负责将高光移动到位。可以通过速度和持续时间,来控制移动速度,并调整其大小。涉及的属性有highlightMoveSpeed、highlightMoveDuration、highlightResizeSpeed和highlightResizeDuration。默认情况下,速度设置为每秒400像素,持续时间设置为-1,表示速度和距离控制持续时间。如果同时设置了速度和持续时间,则会选择产生最快动画的速度或持续时间。

To control the movement of the highlight more in detail, the highlightFollowCurrentItem property can be set to false. This means that the view is no longer responsible for the movement of the highlight delegate. Instead, the movement can be controlled through a Behavior or an animation.

要更详细地控制高亮显示的移动,可以将highlightFollowCurrentItem属性设置为false。这意味着视图不再负责高亮显示委托的移动。相反,可以通过Behavior或动画来控制移动。

In the example below, the y property of the highlight delegate is bound to the ListView.view.currentItem.y attached property. This ensures that the highlight follows the current item. However, as we do not let the view move the highlight, we can control how the element is moved. This is done through the Behavior on y. In the example below, the movement is divided into three steps: fading out, moving, before fading in. Notice how SequentialAnimation and PropertyAnimation elements can be used in combination with the NumberAnimation to create a more complex movement.

在下面的示例中,高亮显示委托的y属性绑定到ListView.view.currentItem.y附加属性。这样可以确保高亮显示在当前项上。但是,由于不允许视图移动高光,因此可以控制元素的移动方式。这是通过Behavior on y完成的。在下面的示例中,移动分为三个步骤:淡出、移动、淡入前。请注意,SequentialAnimation和PropertyAnimation元素类型如何与NumberAnimation结合使用,以创建更复杂的运动动画。

Component {
    id: highlightComponent

    Item {
        width: ListView.view ? ListView.view.width : 0
        height: ListView.view ? ListView.view.currentItem.height : 0

        y: ListView.view ? ListView.view.currentItem.y : 0

        Behavior on y {
            SequentialAnimation {
                PropertyAnimation { target: highlightRectangle; property: "opacity"; to: 0; duration: 200 }
                NumberAnimation { duration: 1 }
                PropertyAnimation { target: highlightRectangle; property: "opacity"; to: 1; duration: 200 }
            }
        }

        GreenBox {
            id: highlightRectangle
            anchors.fill: parent
        }
    }
}

页眉和页脚

At each end of the ListView contents, a header and a footer element can be inserted. These can be considered special delegates placed at the beginning or end of the list. For a horizontal list, these will not appear at the head or foot, but rather at the beginning or end, depending on the layoutDirection used.

在ListView内容的两端,都可以插入页眉header和页脚footer元素。可以将这些看作特殊委托,放在列表的开头或结尾。对于水平列表,它们不会显示在头部或底部,而是显示在开头或结尾,具体取决于所使用的layoutDirection布局方向。

The example below illustrates how a header and footer can be used to enhance the perception of the beginning and end of a list. There are other uses for these special list elements. For instance, they can be used to keep buttons to load more contents.

下面的示例说明了如何使用页眉和页脚来增强对列表开头和结尾的感知。这些特殊列表元素还有其他用途。例如,它们可以用来保留按钮以加载更多内容。

import QtQuick
import "../common"

Background {
    width: 240
    height: 300

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

        clip: true

        model: 4
        delegate: numberDelegate
        header: headerComponent
        footer: footerComponent

        spacing: 2
    }

    Component {
        id: headerComponent

        YellowBox {
            width: ListView.view ? ListView.view.width : 0
            height: 20
            text: 'Header'

        }
    }

    Component {
        id: footerComponent

        YellowBox {
            width: ListView.view ? ListView.view.width : 0
            height: 20
            text: 'Footer'
        }
    }

    Component {
        id: numberDelegate

        GreenBox {
            required property int index
            
            width: ListView.view.width
            height: 40
            
            text: 'Item #' + index
        }
    }
}

image

TIP

Header and footer delegates do not respect the spacing property of a ListView, instead they are placed directly adjacent to the next item delegate in the list. This means that any spacing must be a part of the header and footer items.

页眉和页脚委托不考虑ListView的间距属性spacing,而是直接与列表中的下一个项目委托相邻放置。这意味着任何间距都必须是页眉和页脚项目的一部分。

The GridView

网格视图

Using a GridView is very similar to using a ListView. The only real difference is that the grid view places the delegates in a two-dimensional grid instead of in a linear list.

使用GridView与使用ListView非常相似。唯一真正的区别是栅格视图将委托放置在二维栅格中,而不是线性列表中。

image

Compared to a list view, the grid view does not rely on spacing and the size of its delegates. Instead, it uses the cellWidth and cellHeight properties to control the dimensions of the contents delegates. Each delegate item is then placed in the top left corner of each such cell.

与列表视图相比,栅格视图不依赖于委托的间距和大小。相反,它使用cellWidth和cellHeight属性来控制内容委托的尺寸。然后将每个委托项放置在每个单元格的左上角。

import QtQuick
import "../common"

Background {
    width: 220
    height: 300

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

        clip: true

        model: 100

        cellWidth: 45
        cellHeight: 45

        delegate: GreenBox {
            required property int index
            width: 40
            height: 40
            text: index
        }
    }
}

GridView contains headers and footers, can use a highlight delegate and supports snap modes as well as various bounds behaviors. It can also be orientated in different directions and orientations.

GridView包含页眉和页脚,可以使用高亮显示委托,并支持捕捉模式以及各种边界行为。它也可以使用不同的方向和流向。

The orientation is controlled using the flow property. It can be set to either GridView.LeftToRight or GridView.TopToBottom. The former value fills a grid from the left to the right, adding rows from the top to the bottom. The view is scrollable in the vertical direction. The latter value adds items from the top to the bottom, filling the view from left to right. The scrolling direction is horizontal in this case.

使用属性flow控制方向。它可以设置为GridView.LeftToRightGridView.TopToBottom。前一个值从左到右填充网格,从上到下添加行。视图可在垂直方向上滚动。后一个值从上到下添加项,从左到右填充视图。在这种情况下,滚动方向是水平的。

In addition to the flow property, the layoutDirection property can adapt the direction of the grid to left-to-right or right-to-left languages, depending on the value used.

除了flow属性外,layoutDirection属性还可以根据使用的值将网格方向调整为从左到右或从右到左。

示例源码下载

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值