QML Book 第六章 模型、视图及代理 2

6.3 动态视图

Repeater 适用于有限和静态数据集,但在现实世界中,模型通常更复杂和更大。 在这里,需要一个更智能的解决方案。为此,Qt Quick 提供了 ListView 和 GridView 元素。 这些都是基于 Flickable 的元素,因此用户可以在较大的数据集中滑动查看视图中的内容。 同时,它们限制了同时实例化的 delegate 的数量。对于大型的数据模型,这意味着场景中一次加载的元素将变得更少。

listview-basic
gridview-basic

两个元素的用法相似。 因此,我们将从 ListView 开始,然后再介绍 GridView,前者相对比较基础。ListView 类似于 Repeater元素。 它使用一个模型,实例化一个delegate 并且在代理展示的内容之间,可以有间距(spacing)属性。下面的列表显示了一个简单的设置如何实现这些。

import QtQuick 2.5
import "../common"

Background {
    width: 80
    height: 300

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

        clip: true

        model: 100

        delegate: numberDelegate
        spacing: 5
    }

    Component {
        id: numberDelegate

        GreenBox {
            width: 40
            height: 40
            text: index
        }
    }
}
listview-basic

如果模型包含的数据超出了屏幕窗口可以用于展示数据的范围,则 ListView 将仅显示列表项中的一部分。但是,由于 Qt Quick 的默认行为,列表视图不会限制显示代理的区域。 这意味着代理可能在列表视图之外可见,并且在列表视图之外的代理的动态创建和销毁对用户是可见的。为了防止这种情况,必须通过将 clip 属性设置为true 的方式在 ListView 元素上激活裁剪。下图显示了与 clip 属性默认为 false 时相比较的结果。

listview-clip

对于用户来说,ListView 是一个可滚动区域。它支持动态滚动,这意味着它可以快速滑动屏幕实现快速移动内容的目的。默认情况下,它也可以通过到达视图内容结束位置的回弹效果,使用户意识到已经到达视图的结束位置。

视图滚动结束时的行为是使用 boundsBehavior 属性控制的。这是一个枚举值,可以配置默认值 Flickable.DragAndOvershootBounds,这意味着的视图可以在其边界之外拖动并自动回弹到视图结束位置,值 Flickable.StopAtBounds 则意味着该视图永远不会移动到其边界之外。介于以上两者之间的值,Flickable.DragOverBounds,让用户可以拖动视图超出其边界,但轻击将返回视图结束位置。

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

6.3.1 视图方向

列表视图默认提供了一个垂直滚动列表,但是水平滚动有时也同样有用。列表视图的方向通过 orientation 属性进行控制。 它可以设置为默认值 ListView.Vertical 或 ListView.Horizontal。水平列表视图如下所示。

import QtQuick 2.5
import "../common"

Background {
    width: 480
    height: 80

    ListView {
        anchors.fill: parent
        anchors.margins: 20
        spacing: 4
        clip: true
        model: 100
        orientation: ListView.Horizontal
        delegate: numberDelegate
    }

    Component {
        id: numberDelegate

        GreenBox {
            width: 40
            height: 40
            text: index
        }
    }
}
listview-horizontal

如你所知,水平方向默认从左到右流动。 这可以通过 layoutDirection 属性进行控制,该属性可以设置为 Qt.LeftToRight 或 Qt.RightToLeft,具体取决于视图方向。

6.3.2 键盘导航和高亮

在基于触摸的设置中使用 ListView 时,视图本身就足够了。 在具有键盘的场景中,或者甚至只需用箭头键选择项目,就需要一个指示当前项目的机制。 在 QML 中,这称为高亮显示。

视图支持与代理一起显示在视图中的高亮代理 。它可以被认为是一个额外的代理,但是它只被实例化一次,并被移动到与当前项目相同的位置显示。

下面的例子,将用于展示上述内容。有两个属性涉及这个工作。首先,focus 属性要设置为 true。这给了 ListView 的键盘焦点。其次,highlight 属性设置为指出要使用的高亮代理。高亮代理被赋予当前项的 x,y 和 height 值。如果未指定 width,则也使用当前项的宽度。

在示例中,ListView.view.width 附加属性用于 width 值。代理可以使用的附加属性将在本章的代理部分进一步讨论,但是很高兴我们可以提前知道同样的属性也可以用于高亮代理之中。

import QtQuick 2.5
import "../common"

Background {
    width: 240
    height: 300

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

        clip: true

        model: 100

        delegate: numberDelegate
        spacing: 5

        highlight: highlightComponent
        focus: true
    }

    Component {
        id: highlightComponent

        GreenBox {
            width: ListView.view.width
        }
    }

    Component {
        id: numberDelegate

        Item {
            width: ListView.view.width
            height: 40

            Text {
                anchors.centerIn: parent

                font.pixelSize: 10

                text: index
            }
        }
    }
}
listview-highlight

当与 ListView 结合使用高亮时,可以使用许多属性来控制其行为。 highlightRangeMode 控制高亮显示在视图中显示的内容。默认设置 ListView.NoHighlightRange 意味着视图中的项目的高亮和可见范围根本不相关。

ListView.StrictlyEnforceRange 的值确保高亮显示始终可见。如果操作尝试将突出显示区域移动到视图的可见部分之外,则当前项目将相应更改,以使突出显示保持可见。

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

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

要更加精确地控制高亮的移动,可以将 highlightFollowCurrentItem 属性设置为 false。这意味着视图不再对高亮代理的移动负责。此时,高亮代理的运动可以通过我们的自定义的 Behavior 或动画来进行控制。

在下面的示例中,高亮代理的 y 属性绑定到 ListView.view.currentItem.y 附加属性。 这样可确保突出显示符合当前项目。然而,由于我们不让视图本身来移动高亮,我们可以控制高亮元素的移动方式。这是通 Behavior on y 的方法完成的。在下面的例子中,移动分为三个步骤:淡出,移动,淡入。请注意 SequentialAnimation 元素如何与 PropertyAnimation 和 NumberAnimation 组合使用,以创建更复杂的运动动画效果。

Component {
        id: highlightComponent

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

            y: ListView.view.currentItem.y

            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
            }
        }
    }

6.3.3 页眉和页脚属性

在 ListView 内容的最后,可以介绍一下 header 和 footer 属性。这些可以被认为是列表开头或末尾的特殊代理位置。对于水平列表,这些不会出现在头部或尾部,而是在开始或结束时显示,具体取决于所使用的 layoutDirection 属性的值。

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

import QtQuick 2.5
import "../common"

Background {
    width: 240
    height: 300

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

        clip: true

        model: 4

        delegate: numberDelegate
        spacing: 2

        header: headerComponent
        footer: footerComponent
    }

    Component {
        id: headerComponent

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

        }
    }

    Component {
        id: footerComponent

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

    Component {
        id: numberDelegate

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

** 注意: **
页眉和页脚代理不会实现 ListView 的 spacing 属性,而是紧挨着显示在列表中的代理。这意味着如果有必要我们必须自己来实现页眉和页脚项的 spacing 的效果。

listview-header-footer

6.3.4 网格视图

使用 GridView 非常类似于使用 ListView。唯一的区别是网格视图将代理放置在二维网格而不是线性列表中。

gridview-basic

与列表视图相比,网格视图不依赖于间距和其代理的大小。相反,它使用 cellWidth 和 cellHeight 属性来控制代理内容的大小。然后,默认情况下每个代理项目都放在每个这样的单元格的左上角。

import QtQuick 2.5
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: numberDelegate
    }

    Component {
        id: numberDelegate

        GreenBox {
            width: 40
            height: 40
            text: index
        }
    }
}

GridView 包含页眉和页脚,可以使用高亮显示代理,并支持快照模式以及各种边界行为。它也可以在不同的方向和方向上定向。

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • 翻译自“Collection View Programming Guide for iOS” 0 关于iOS集合视...
    lakerszhy阅读 3,817评论 1 22
  • 6.5 视图知识进阶 6.5.1 PathView PathView 元素是 Qt Quick 中最强大的,也是最...
    赵者也阅读 1,207评论 0 1
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,439评论 25 707
  • 最近公司项目有WiFi定位这块需求,查看一些资料后,看到知乎上有一个回答貌似很正确,贴出来,有哪些不对的地方可以提...
    soulDxl阅读 4,901评论 1 4