QML

QML动画和过度

动画(Animation)和过度(Transition)元素

    Transition - 状态改变的过度动画
    SequentialAnimation - 串行执行动画
    ParallelAnimation - 并行执行动画
    Behavior - 为属性变化指定默认动画
    PropertyAction - 动画中设置立即改变的属性值(Sets immediate property changes during animation)
    PauseAnimation - 在动画中引入暂停
    SmoothedAnimation - 运行属性平滑的过度到一个新的值
    SpringAnimation - 以弹跳式的方式给属性设置一个新值
    ScriptAction - 动画中执行脚本

动画属性元素的数据类型

    PropertyAnimation - 属性改变动画
    NumberAnimation - qreal类型属性改变动画
    Vector3dAnimation - QVector3d类型属性改变动画
    ColorAnimation - 颜色改变动画
    RotationAnimation -旋转动画
    ParentAnimation - parent更改动画
    AnchorAnimation - 描点改变动画

在QML中,向属性值应用animation元素来创建动画.Animation元素通过篡改属性的值来产生平滑的过度.同样,也可使用状态过度(state transition)向状态变化设置一个动画.

要创建动画,需要使用与要创建动画的属性类型相匹配的animation元素,应用的动画(animation)与需要的行为类型有关.
触发动画

有很多种方式在对象上设置动画.
直接的属性动画Direct Property Animation

要实现立即移动或动画移动,可直接设置属性值.这可以在信号处理函数或附加属性中实现..

 Rectangle {
     id: blob
     width: 75; height: 75
     color: "blue"

     MouseArea {
         anchors.fill: parent
         onClicked: blob.color = "green"
     }
 }

然而,为产生更多控制,属性动画(property animation)使用在属性变化值之间进行插值实现了平滑移动.属性动画提供了定时控制和由easing curves指定的不同插值曲线.

 Rectangle {
     id: flashingblob
     width: 75; height: 75
     color: "blue"
     opacity: 1.0

     MouseArea {
         anchors.fill: parent
         onClicked: {
             animateColor.start()
             animateOpacity.start()
         }
     }

     PropertyAnimation {id: animateColor; target: flashingblob; properties: "color"; to: "green"; duration: 100}

     NumberAnimation {
         id: animateOpacity
         target: flashingblob
         properties: "opacity"
         from: 0.99
         to: 1.0
         loops: Animation.Infinite
         easing {type: Easing.OutBack; overshoot: 500}
    }
 }

特定的属性动画元素(property animation elements)与PropertyAnimation元素的实现相比效率更好.他们向不同的QML类型如int,color,rotation设置动画,PropertyAnimation可以设置parent改变动画.

不同动画属性的更多信息详见Controlling Animations小节.
状态变化的过度

States是属性的配置集合,针对不同的状态设置不同的属性值.状态变化直接导致其中的属性变化;动画平滑过度可产生生动的状态变化效果.

Transition元素自动实现了动画元素(animation elements)的功能,为状态变化引起的属性变化产生插值.要设置一个对象的过度(transition),可构造其transitions属性.

按钮可能有两个状态,用户点击时为pressed状态,用户释放鼠标后为released状态.我们可以为每个状态设置不同的属性配置.过度(transition)可以为状态从pressed改变为released产生动画.同样也可为状态从released改变为pressed产生动画.

 Rectangle {
     width: 75; height: 75
     id: button
     state: "RELEASED"

     MouseArea {
         anchors.fill: parent
         onPressed: button.state = "PRESSED"
         onReleased: button.state = "RELEASED"
     }

     states: [
         State {
             name: "PRESSED"
             PropertyChanges { target: button; color: "lightblue"}
         },
         State {
             name: "RELEASED"
             PropertyChanges { target: button; color: "lightsteelblue"}
         }
     ]

     transitions: [
         Transition {
             from: "PRESSED"
             to: "RELEASED"
             ColorAnimation { target: button; duration: 100}
         },
         Transition {
             from: "RELEASED"
             to: "PRESSED"
             ColorAnimation { target: button; duration: 100}
         }
     ]
 }

to和from属性绑定到特定的状态名称上,可以设置过度(transition)针对的属性变化.为简化或使过度对称,可设置to属性为通配符星号("*"),指示这个过度可用于所有的状态改变.

     transitions:
         Transition {
             to: "*"
             ColorAnimation { target: button; duration: 100}
         }

默认的行为动画(Default Animation as Behaviors)

默认的属性动画使用行为动画(behavior animations)来设置.使用指定属性的Behavior元素声明的动画,使属性值变化时产生动画.然而,Behavior元素有一个enabled属性可设置行为动画的使能.

下面是一个球(ball)组件,可对其x,y,color属性设置行为动画(behavior animation).行为动画被设置为模拟弹性效果.当球移动时就会在属性值变化上应用这个弹性效果.

 Rectangle {
     width: 75; height: 75; radius: width
     id: ball
     color: "salmon"

     Behavior on x {
         NumberAnimation {
             id: bouncebehavior
             easing {
                 type: Easing.OutElastic
                 amplitude: 1.0
                 period: 0.5
             }
         }
     }
     Behavior on y {
         animation: bouncebehavior
     }
     Behavior {
         ColorAnimation { target: ball; duration: 100 }
     }
 }

有很多种方式给属性设置行为动画.Behavior on <property>声明是给属性设置行为动画的便利方式.

行为属性的演示内容见Behaviors example.
串行或并行执行动画

动画可并行或串行运行.并行动画可同时执行一组动画,而串行动画按顺序执行一组动画:一个执行完毕后在执行另一个.在SequentialAnimation 和ParallelAnimation中分组的动画将串行或并行执行.

下面的banner组件需要一个挨一个的显示多个图标或广告.opacity属性变为1.0来表示一个不透明对象.使用SequentialAnimation元素,前一个动画执行完毕后才会执行opacity动画.ParallelAnimation元素将同时执行动画.

 Rectangle {
     id: banner
     width: 150; height: 100; border.color: "black"

     Column {
         anchors.centerIn: parent
         Text {
             id: code
             text: "Code less."
             opacity: 0.01
         }
         Text {
             id: create
             text: "Create more."
             opacity: 0.01
         }
         Text {
             id: deploy
             text: "Deploy everywhere."
             opacity: 0.01
         }
     }

     MouseArea {
         anchors.fill: parent
         onPressed: playbanner.start()
     }

     SequentialAnimation {
         id: playbanner
         running: false
         NumberAnimation { target: code; property: "opacity"; to: 1.0; duration: 200}
         NumberAnimation { target: create; property: "opacity"; to: 1.0; duration: 200}
         NumberAnimation { target: deploy; property: "opacity"; to: 1.0; duration: 200}
     }
 }

一旦有动画被放在了SequentialAnimation 或 ParallelAnimation中, 他们就不会再独立的启动或停止.串行或并行动画必须作为一个组合来启动或停止.

SequentialAnimation元素在执行过度动画(transition animations)时也很有效,因为在过度中的动画是并行执行的.

有关在QML中创建和组合多个动画的演示请见Animation basics example.
控制动画

有不同的方式控制动画.
动画回放

所有的动画元素都是从Animation元素继承的;这些元素为动画元素提供了必要的属性和方法.动画元素具有start(),stop(),resume(),pause()和complete()方法--这些方法控制了动画的执行.
Easing

Easing曲线定义动画如何在起始值和终止值见产生插值.不同的easing曲线定义了一系列的插值.easing曲线简化了创建动画的效果--如弹跳效果, 加速, 减速, 和周期动画.

QML对象中可能对每个属性动画都设置了不同的easing曲线.同时也有不同的参数用于控制曲线,有些是特除曲线独有的.更多信息见easing 文档.

easing example 可视化的演示了不同easing曲线类型效果.
其他动画元素

此外,QML提供了几个对动画有用的其他元素:

    PauseAnimation: 允许停止动画
    ScriptAction: 允许在动画期间执行JavaScript,可与StateChangeScript配合来重用已存在的脚本.
    PropertyAction: 在动画期间直接修改属性,而不会产生属性变化动画

下面是针对不同属性类型的特殊动画元素

    SmoothedAnimation: 特殊的NumberAnimation,当目标值发生改变时提供平滑的动画效果
    SpringAnimation: 指定mass, damping 和epsilon等特殊属性来提供一个弹簧效果的动画
    ParentAnimation: 用在parent变化时的动画(见ParentChange)

    AnchorAnimation: 用在描点变化时的动画(见AnchorChanges)

使用QML视图显示数据

视图是包含项目的集合.他们富有特色,可自定义风格和行为

Qt Quick图形元素提供了几个标准的视图:

    ListView 水平或垂直列表中排列项目
    GridView 在一个有效空间的网格内排列项目
    PathView 在路径上排列项目
    WebView - 可在QtWebKit QML Module中使用.

与其他视图不同,WebView 不具有全部视图特性,需要与Flickable组合创建一个像Web浏览器一样执行的项目.

这些元素具有的属性和行为相互独立.更多信息见他们的文档.
模型

视图在屏幕上显示模型(models).模型可以是简单的整形列表或一个C++模型.

要给视图设置模型,需要给视图的model属性绑定到一个模型.

 ListModel {
     id: petlist
     ListElement { type: "Cat" }
     ListElement { type: "Dog" }
     ListElement { type: "Mouse" }
     ListElement { type: "Rabbit" }
     ListElement { type: "Horse" }
 }
 ListView {
     id: view
     anchors.fill: parent

     model: petlist
     delegate: petdelegate
 }

更多信息见QML Data Models 文档.
视图代理

视图需要使用代理(delegate)来可视化的表现列表中的项.视图以代理作为模版显示列表中的每个项.模型中的项使用index属性来访问.

 Component {
     id: petdelegate
     Text {
         id: label
         font.pixelSize: 24
         text: if (index == 0)
             label.text = type + " (default)"
         else
             text: type
     }
 }

美化视图

可使用decoration属性来自定义视图的header,footer,section属性.通过向这些属性绑定其他可视对象,就可美化视图.footer中可能包含一个Rectangle元素作为边框,或在header中显示列表的logo图标.

假如一个俱乐部要使用它们的商标颜色来修饰其成员列表.成员列表包含在一个模型中,代理显示模型中的内容.

 ListModel {
     id: nameModel
     ListElement { name: "Alice" }
     ListElement { name: "Bob" }
     ListElement { name: "Jane" }
     ListElement { name: "Harry" }
     ListElement { name: "Wendy" }
 }
 Component {
     id: nameDelegate
     Text {
         text: name;
         font.pixelSize: 24
     }
 }

可以向header和footer属性绑定可视对象来美化这个俱乐部的成员列表.这个可视对象可以直接定义,或在其他文件中定义,或在组件元素中定义..

 ListView {
     anchors.fill: parent
     clip: true
     model: nameModel
     delegate: nameDelegate
     header: bannercomponent
     footer: Rectangle {
         width: parent.width; height: 30;
         gradient: clubcolors
     }
     highlight: Rectangle {
         width: parent.width
         color: "lightgray"
     }
 }

 Component {     //instantiated when header is processed
     id: bannercomponent
     Rectangle {
         id: banner
         width: parent.width; height: 50
         gradient: clubcolors
         border {color: "#9EDDF2"; width: 2}
         Text {
             anchors.centerIn: parent
             text: "Club Members"
             font.pixelSize: 32
         }
     }
 }
 Gradient {
     id: clubcolors
     GradientStop { position: 0.0; color: "#8EE2FE"}
     GradientStop { position: 0.66; color: "#7ED2EE"}
 }

 
ListView的小节

ListView 可在sections中包含很多分组,相关的列表项按他们所在的小节进行标记.而且小节还可以指定代理(delegates).

如下列表中包含了人员姓名和所在小组的信息.

 ListModel {
     id: nameModel
     ListElement { name: "Alice"; team: "Crypto" }
     ListElement { name: "Bob"; team: "Crypto" }
     ListElement { name: "Jane"; team: "QA" }
     ListElement { name: "Victor"; team: "QA" }
     ListElement { name: "Wendy"; team: "Graphics" }
 }
 Component {
     id: nameDelegate
     Text {
         text: name;
         font.pixelSize: 24
         anchors.left: parent.left
         anchors.leftMargin: 2
     }
 }

ListView 元素具有一个叫做section的附加属性(attached property) 可在一个小节内组合临近或相关的元素.section的property属性指明列表元素中的一个属性作为小节名称.criteria属性指明如何显示小节的名称,而delegate与视图的delegate相同.

 ListView {
     anchors.fill: parent
     model: nameModel
     delegate: nameDelegate
     focus: true
     highlight: Rectangle {
         color: "lightblue"
         width: parent.width
     }
     section {
         property: "team"
         criteria: ViewSection.FullString
         delegate: Rectangle {
             color: "#b0dfb0"
             width: parent.width
             height: childrenRect.height + 4
             Text { anchors.horizontalCenter: parent.horizontalCenter
                 font.pixelSize: 16
                 font.bold: true
                 text: section
             }
         }
     }
 }


QML数据模型(Model)


QML数据模型(Model)
QML中的ListView,GridView和Repeater等元素需要数据模型来提供要显示的数据.这些元素需要一个为模型中的每一项数据生成一个实例的代理组件(delegate component).模型可以是静态的,也可对其动态修改,插入,删除,移动.

给代理提供的数据通过叫做角色的数据绑定到代理.下面的ListModel有两个角色,type和age,ListView带有一个代理,并绑定这些角色以显示他们的值:

 import QtQuick 1.0

 Item {
     width: 200; height: 250

     ListModel {
         id: myModel
         ListElement { type: "Dog"; age: 8 }
         ListElement { type: "Cat"; age: 5 }
     }

     Component {
         id: myDelegate
         Text { text: type + ", " + age }
     }

     ListView {
         anchors.fill: parent
         model: myModel
         delegate: myDelegate
     }
 }
如果模型属性和代理的属性有名称冲突,可以在角色名称前加上模型名称.例如,如果Text元素也有type或age属性,上面的text元素会显示这些属性值,而不是模型中的项目.这时,可以使用model.age和model.type,确保代理显示模型中的数据项.

模型中的项目索引角色也可用在代理中.数据项从模型中删除后这个索引值被设置为-1.如果绑定这个索引角色,需要注意这个逻辑上可能的索引值-1,表示这个数据项还不可用.(通常这个数据项很快就会被删除,也可能通过在某些视图设置delayRemove扩展属性,由延时代理销毁.)

对于没有角色名称的数据模型(如下面的QStringList)可以使用modelData角色来引用数据.只有一个角色的模型也可使用modelData角色.这时modelData角色包含的数据与命名的角色相同.

QML提供了几个内建的数据模型元素.此外,可在C++中创建模型,用于QML元素.

视图引用模型中的数据并显示.QML使用Positioner和Repeater项来定位排列模型中的数据项.

QML数据模型ListModel

ListModel是QML中简单的层次元素.其中的角色由ListElement属性指定.

 ListModel {
     id: fruitModel

     ListElement {
         name: "Apple"
         cost: 2.45
     }
     ListElement {
         name: "Orange"
         cost: 3.25
     }
     ListElement {
         name: "Banana"
         cost: 1.95
     }
 }
上面的模型有两个角色--name和cost.他们可以绑定到ListView的代理,如:

 ListView {
     anchors.fill: parent
     model: fruitModel
     delegate: Row {
         Text { text: "Fruit: " + name }
         Text { text: "Cost: $" + cost }
     }
 }
ListModel提供了可直接在JavaScript中调用的方法.这时,第一次插入的项决定使用模型的视图可用的角色.例如,如果创建了一个空ListModel,并在JavaScript中填充,第一次插入时指定的角色将显示在视图中:

 ListModel { id: fruitModel }
     ...
 MouseArea {
     anchors.fill: parent
     onClicked: fruitModel.append({"cost": 5.95, "name":"Pizza"})
 }
当点击MouseArea时,fruitModel中会产生两个角色,cost和name.后面增加其他角色,使用模型的视图也只能使用这两个.调用ListModel::clear()重置模型中的角色.

XmlListModel

XmlListModel用来从XML数据源构造模型.角色由XmlRole元素指定.

下面的模型有三个角色:title,link和description:

 XmlListModel {
      id: feedModel
      source: "http://rss.news.yahoo.com/rss/oceania"
      query: "/rss/channel/item"
      XmlRole { name: "title"; query: "title/string()" }
      XmlRole { name: "link"; query: "link/string()" }
      XmlRole { name: "description"; query: "description/string()" }
 }
RSS范例展示了如何使用XmlListModel显示RSS源.

VisualItemModel

VisualItemModel允许QML元素作为模型.

这个模型中包含数据和代理;VisualItemModel的子项提供了代理的内容.这个模型不提供任何角色.

 VisualItemModel {
     id: itemModel
     Rectangle { height: 30; width: 80; color: "red" }
     Rectangle { height: 30; width: 80; color: "green" }
     Rectangle { height: 30; width: 80; color: "blue" }
 }

 ListView {
     anchors.fill: parent
     model: itemModel
 }

注意上例中不需要代理.模型中的数据项提供了可视化元素,将显示到视图中..

C++数据模型

C++中定义的模型可用于QML.可向QML元素暴露C++中已存的数据模型或复杂的数据集.

C++中可使用QStringList, QList<QObject*> 或QAbstractItemModel类定义模型.前面两个可用于暴露简单的数据集,QAbstractItemModel则可定义更复杂的模型.

基于QStringList的模型

可定义简单的QStringList模型,使用modalData角色访问其中的内容.

下面的ListView中有一个代理使用modelData角色使用模型中的数据项:

 ListView {
     width: 100; height: 100
     anchors.fill: parent

     model: myModel
     delegate: Rectangle {
         height: 25
         width: 100
         Text { text: modelData }
     }
 }
Qt应用程序可以加载这个QML文档并设置一个叫做myModel的QStringList类型的值:

     QStringList dataList;
     dataList.append("Item 1");
     dataList.append("Item 2");
     dataList.append("Item 3");
     dataList.append("Item 4");

     QDeclarativeView view;
     QDeclarativeContext *ctxt = view.rootContext();
     ctxt->setContextProperty("myModel", QVariant::fromValue(dataList));
完整范例见Qt的范例:examples/declarative/modelviews/stringlistmodel目录.

注意:视图无法知道QStringList中的内容变化.如果QStringList发生变化,需要再次调用QDeclarativeContext::setContextProperty()重置模型.

基于QObjectList的模型

QObject*列表也可作为模型.QList<QObject*>提供了一个对象列表的属性.

下面的应用程序创建了一个DataObject对象,使用Q_PROPERTY声明的命名属性,当QList<DataObject>暴露给QML时,可按属性名称作为角色来访问:

 class DataObject : public QObject
 {
     Q_OBJECT

     Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
     Q_PROPERTY(QString color READ color WRITE setColor NOTIFY colorChanged)
     ...
 };

 int main(int argc, char ** argv)
 {
     QApplication app(argc, argv);

     QList<QObject*> dataList;
     dataList.append(new DataObject("Item 1", "red"));
     dataList.append(new DataObject("Item 2", "green"));
     dataList.append(new DataObject("Item 3", "blue"));
     dataList.append(new DataObject("Item 4", "yellow"));

     QDeclarativeView view;
     QDeclarativeContext *ctxt = view.rootContext();
     ctxt->setContextProperty("myModel", QVariant::fromValue(dataList));
     ...
QObject*可作为modelData的属性来访问.为方便,对象的属性也可直接用于代理上下文.这里,view.qml在ListView代理中引用了数据模型属性:

 ListView {
     width: 100; height: 100
     anchors.fill: parent

     model: myModel
     delegate: Rectangle {
         height: 25
         width: 100
         color: model.modelData.color
         Text { text: name }
     }
 }
注意使用color属性时使用了全名称标识.QML对象的属性不能与模型对象属性同名,可使用modelData对象来访问数据项.

完整范例见examples/declarative/modelviews/objectlistmodel.

注意:无法知道QList中的内容发生了变化.如果QList变化了,有必要调用QDeclarativeContext::setContextProperty()重置模型.

QAbstractItemModel

可使用QAbstractItemModel的子类定义模型.如果有一个复杂的模型无法用其他方式实现,就可以使用这种方式.QAbstractItemModel在发生变化时会自动通知QML视图.

QAbstractItemModel子类向QML暴露的角色可使用QAbstractItemModel::setRoleNames()设置.默认的角色名称由Qt设置:

Qt Role QML角色名称
Qt::DisplayRole 显示
Qt::DecorationRole 修饰

下面的应用程序有一个QAbstractItemModel子类模型叫做AnialModel,具有type和size角色.为在QML中访问这些属性调用了QAbstractItemModel::setRoleNames():

 class Animal
 {
 public:
     Animal(const QString &type, const QString &size);
     ...
 };

 class AnimalModel : public QAbstractListModel
 {
     Q_OBJECT
 public:
     enum AnimalRoles {
         TypeRole = Qt::UserRole + 1,
         SizeRole
     };

     AnimalModel(QObject *parent = 0);
     ...
 };

 AnimalModel::AnimalModel(QObject *parent)
     : QAbstractListModel(parent)
 {
     QHash<int, QByteArray> roles;
     roles[TypeRole] = "type";
     roles[SizeRole] = "size";
     setRoleNames(roles);
 }

 int main(int argc, char ** argv)
 {
     QApplication app(argc, argv);

     AnimalModel model;
     model.addAnimal(Animal("Wolf", "Medium"));
     model.addAnimal(Animal("Polar bear", "Large"));
     model.addAnimal(Animal("Quoll", "Small"));

     QDeclarativeView view;
     QDeclarativeContext *ctxt = view.rootContext();
     ctxt->setContextProperty("myModel", &model);
     ...
这个模型显示在ListView中,代理可访问type和size角色:

 ListView {
     width: 200; height: 250
     anchors.fill: parent

     model: myModel
     delegate: Text { text: "Animal: " + type + ", " + size }
 }
模型更改后QML视图自动更新.记住模型更改必须遵守标准规则,当模型修改后必须调用QAbstractItemModel::dataChanged(),QAbstractItemModel::beginInsertRows()等等,通知视图模型被修改了.更多信息见模型子类的参考.

完整范例见examples/declarative/modelviews/abstractitemmodel 目录.

QAbstractItemModel表现为一个层次表,但现在QML只提供了可显示列表数据的视图.为了显示模型层次的子列表,VisualDataModel元素提供了几个属性和方法与QAbstractItemModel类型的模型共用:

hasModelChildren 确定节点是否有子节点.
VisualDataModel::rootIndex 指定跟节点
VisualDataModel::modelIndex() 返回可给VisualDataModel::rootIndex赋值的QModelIndex类型值
VisualDataModel::parentModelIndex() 返回可给VisualDataModel::rootIndex赋值的QModelIndex类型值

向QML暴露C++数据模型

上例中使用QDeclarativeContext::setContextProperty()设置可用于QML组件的模型.另一种方式是在C++插件中注册一个C++模型类作为QML类型.这样可以直接在QML中声明模型元素:

 class MyModelPlugin : public QDeclarativeExtensionPlugin
 {
 public:
     void registerTypes(const char *uri)
     {
         qmlRegisterType<MyModel>(uri, 1, 0,
                 "MyModel");
     }
 }

 Q_EXPORT_PLUGIN2(mymodelplugin, MyModelPlugin);
  MyModel {
     id: myModel
     ListElement { someProperty: "some value" }
 }
 ListView {
     width: 200; height: 250
     model: myModel
     delegate: Text { text: someProperty }
 }
 

写C++插件的更多信息见Writing QML extensions with C++.

其他数据模型--整形

一个整数可指示模型包含的元素个数.这时,模型没有任何数据角色.

下面范例中创建一个有五个元素的ListView:

 Item {
     width: 200; height: 250

     Component {
         id: itemDelegate
         Text { text: "I am item number: " + index }
     }

     ListView {
         anchors.fill: parent
         model: 5
         delegate: itemDelegate
     }

 }

对象实例

一个对象实例可指定为模型.对象属性可作为角色.

下例创建一个有一个项的列表,展示Text.text的颜色.注意这里使用了全命名model.color避免命名冲突.

 Rectangle {
     width: 200; height: 250

     Text {
         id: myText
         text: "Hello"
         color: "#dd44ee"
     }

     Component {
         id: myDelegate
         Text { text: model.color }
     }

     ListView {
         anchors.fill: parent
         anchors.topMargin: 30
         model: myText
         delegate: myDelegate
     }
 }

在代理中访问视图和模型

可在代理中访问其所在的视图,在ListView的代理中使用ListView.view属性,或GridView中使用GridVie.view等.而且可使用ListView.view.model来访问模型的属性.

这对于在多个视图时使用同一个代理很有用,例如,想为每个视图创建不同的修饰部分或其他特性,需要针对每个视图设置不同的属性.同样,可能需要访问或显示模型的属性.

下面例子中,代理显示模型的language属性,一个字段的颜色依赖于fruit_color属性.

 Rectangle {
      width: 200; height: 200

     ListModel {
         id: fruitModel
         property string language: "en"
         ListElement {
             name: "Apple"
             cost: 2.45
         }
         ListElement {
             name: "Orange"
             cost: 3.25
         }
         ListElement {
             name: "Banana"
             cost: 1.95
         }
     }

     Component {
         id: fruitDelegate
         Row {
                 Text { text: " Fruit: " + name; color: ListView.view.fruit_color }
                 Text { text: " Cost: $" + cost }
                 Text { text: " Language: " + ListView.view.model.language }
         }
     }

     ListView {
         property color fruit_color: "green"
         model: fruitModel
         delegate: fruitDelegate
         anchors.fill: parent
     }
 }
其他重要的情形是当有些情况下(如鼠标点击)代理需要更新模型中的数据.这时可以在模型中定义一个函数,如:

         setData(int row, const QString & field_name, QVariant new_value),
...在代理中调用这个函数:

         ListView.view.model.setData(index, field, value)
...假设field保存了需要更新的字段名称,value中保存了新值.


在QML中管理动态对象


QML提供了很多种方式动态创建和管理QML对象.Loader,Repeater,ListView,GridView和PathView都支持动态对象管理.对象也可在C++中创建并管理,这是QML\C++相结合的应用程序首选方法.

QML也支持在Javascript代码中动态创建对象.这在当前QML文件无法满足需要是很有用,而且不会卷入额外的C++组件.

本文中下面的动态场景范例展示了这个概念.


动态创建对象

在JavaScript中动态创建对象有两种方式.可调用Qt.createComponent()动态创建一个组件对象,或使用Qt.createQmlObject()从一个描述QML的字符串创建元素.最好是有一个定义在.qml文件中的组件,需要动态创建这个组件的实例.否则,就需要在运行时使用一个QML描述字符串创建元素.
动态创建组件

要动态加载QML文件中的组件,可在QML全局对象中调用Qt.createComponent()函数.这个函数只有一个指向QML文件的URL参数并创建这个URL指向的组件对象.
创建了组件后,可以调用其createObject()方法创建组件的实例.这个函数有一个或两个参数:
  第一个参数是新元素的parent.由于图形项(graphical item)在场景中必须有parent,否则就无法显示,还是推荐使用这种方式设置一个parent.然而,如果希望稍后在设置可以传递一个null实参.

  第二个参数是可选的属性名称-值的映射列表,用来初始化元素的属性值.这个参数指定的属性值在对象构造完成之前被赋给对象的属性,避免了有些属性在用于绑定前必须被初始化而发生的错误.在属性用于绑定前就已经设置了值.另外,与对象创建后再设置属性值和绑定相比,具有稍微的效率优势.   
下面是一个范例,首先定义Sprite.qml文件:

 import QtQuick 1.0

 Rectangle { width: 80; height: 50; color: "red" }

主应用程序文件是main.qml,导入可创建Spite对象的componentCreation.js JavaScript文件:

 import QtQuick 1.0
 import "componentCreation.js" as MyScript

 Rectangle {
     id: appWindow
     width: 300; height: 300

     Component.onCompleted: MyScript.createSpriteObjects();
 }

下面是componentCreation.js. 注意在调用createObject()前先判断组件状态是否为Component.Ready,如果QML是从网上加载的,不会立刻完成的,必须等待.

 var component;
 var sprite;

 function createSpriteObjects() {
     component = Qt.createComponent("Sprite.qml");
     if (component.status == Component.Ready)
         finishCreation();
     else
         component.statusChanged.connect(finishCreation);
 }

 function finishCreation() {
     if (component.status == Component.Ready) {
         sprite = component.createObject(appWindow, {"x": 100, "y": 100});
         if (sprite == null) {
             // Error Handling
             console.log("Error creating object");
         }
     } else if (component.status == Component.Error) {
         // Error Handling
         console.log("Error loading component:", component.errorString());
     }
 }

如果从本地文件加载QML文件,可以忽略finishCreation()函数直接调用createObject():

 function createSpriteObjects() {
     component = Qt.createComponent("Sprite.qml");
     sprite = component.createObject(appWindow, {"x": 100, "y": 100});

     if (sprite == null) {
         // Error Handling
         console.log("Error creating object");
     }
 }

注意这两个实例中,createObject()函数的第一个参数是appWindow,使新创建的对象作为main.qml中的appWindow对象的子对象.否则,新创建的对象不会在场景中显示.
当用相对路径引用文件时,路径是相对于执行Qt.CreatComponent()函数的文件而言的.

要连接动态创建对象上的信号(或槽),可使用信号的connect()方法.

基于QML的字符串创建对象
如果在运行时还没有建立QML文件,可以使用Qt.CreateQmlObject()函数使用QML描述字符串来创建QML对象,如下所示:

 var newObject = Qt.createQmlObject('import QtQuick 1.0; Rectangle {color: "red"; width: 20; height: 20}',
     parentItem, "dynamicSnippet1");

第一个参数是创建QML的字符串.与创建新的QML文件一样,需要导入必要的类型.第二个参数是新对象的parent;这个parent必须在场景中存在.第三个参数新对象相关的文件连接,用于导出错误报表.
如果QML中使用相对路径导入文件,路径是相对于定义了parent对象的文件而言的.

维护动态创建的对象

要管理动态创建的对象,必须要确保创建上下文的生命周期比被创建的对象长.否则当创建上下文被释放后,对动态对象的绑定不在生效.
事实上创建上下文依赖于对象是如何创建的:

    如果使用Qt.createComponent(),创建上下文是调用这个方法的QDeclarativeContext
    如果调用Qt.createQmlObject(),创建上下文是传递给这个函数的parent参数的上下文
    如果定义了Component{} 对象并在其上调用了createObject(),创建上下文是定义的那个Component

同时主要动态创建对象可像其他对象一样使用,在QML中没有id属性.


删除动态对象

在很多用户接口中,将对象的opacity设置为0或将其移动到屏幕之外,而不是直接删除对象.如果创建了很多动态对象,删除无用的对象会获得显著的效率提升.
主要不要手动删除由QML元素动态创建的对象(如Loader和Repeater).同时,避免删除不是由你创建的动态对象.
对象可调用destroy()方法删除.这个方法有一个可选参数(默认值为0)指定对象被删除的大概延时(毫秒).
下面是一个范例.application.qml创建了五个SelfDestroyingRect.qml组件.每个实例都会运行一个NumberAnimation动画,动画完成后,在其根对象上调用destroy()方法释放自己:
application.qml   

 import QtQuick 1.0

 Item {
     id: container
     width: 500; height: 100

     Component.onCompleted: {
         var component = Qt.createComponent("SelfDestroyingRect.qml");
         for (var i=0; i<5; i++) {
             var object = component.createObject(container);
             object.x = (object.width + 10) * i;
         }
     }
 }

    

SelfDestroyingRect.qml


 import QtQuick 1.0

 Rectangle {
     id: rect
     width: 80; height: 80
     color: "red"

     NumberAnimation on opacity {
         to: 0
         duration: 1000

         onRunningChanged: {
             if (!running) {
                 console.log("Destroying...")
                 rect.destroy();
             }
         }
     }
 }

也可以在application.qml中调用object.destroy()删除其创建的对象.
注意在一个对象上调用destroy()是安全的.在对象上调用destroy()不会马上释放,而是在脚本阻塞后或下一帧时执行清理(除非设置了非0的延时).
注意如果SelfDestroyRect如下方式创建:

 Item {
     SelfDestroyingRect {
         // ...
     }
 }

由于只能动态释放动态创建的对象,这时会发生错误.

使用Qt.createQmlObject()创建的对象也同样使用destroy()释放:

 var newObject = Qt.createQmlObject('import QtQuick 1.0; Rectangle {color: "red"; width: 20; height: 20}',
     parentItem, "dynamicSnippet1");
 newObject.destroy(1000);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值