qml编码规范与指导

qml编码规范与指导

说明

良好的编程规范能够大幅提升一个程序的可读性、可维护性。编码规范是你们达成一致的约定,这样你们的代码就能够互相看懂,维护起来更容易,思想更畅快的交流,经验更快的获得传播。javascript

QML是一种声明方式设计的语言,用来设计应用程序的界面,包括式样和表现行为。在QML中,一个用户界面被制定为一棵树形的对象模型而且包含了对象的属性,这棵树树根对应的就是qml文件中的根组件。
JavaScript在QML中做为一种脚本语言,对QML进行逻辑方面的编程,也能够说QML是对JavaScript的扩展,提供了js主机环境。
QML中的JavaScript是特殊的JavaScript,是简化的JavaScript。java

Item {
    id:root
    width:800
    height:480
    Rectangle {
        x:200
        y: 60
        width:200
        height:160
        color:“red”
        visible: false
    }
}

Rectangle组件中一些语法的声明能够写成JavaScript的方式:c++

Item {
    id:root
    width:800
    height:480
    property bool visibleState: false
    property bool lightState: false
    Rectangle {
        x: parent.widht/4
        y: parent.height/8
        width:parent.widht/4
        height:parent.height/3
        color:root.lightState ? “red” : "green"
        visible: root.visibleState ? true : false
    }
}

经过以上两段QML代码中Rectangle组件属性的赋值状况能够看出,组件的属性能够直接赋值,也可经过JavaScript代码赋值。git

指导性原则

统一的代码风格,减小代码冗余,加强其代码可读性,还能够提高工做效率,让程序更清晰、健壮。编程

详细说明

文件命名

这里的文件分为 _*.qml_ 文件和 _*.js_ 文件。框架

qml文件命名

因每个qml文件的使用方法可能不固定,在某些状况下做为程序的入口文件,文件名的首字母能够大写,也能够小写,但在另外一些状况下可能做为Component(控件,类)来使用,首字母必须大写。例如:
mainScreen.qml是一个程序界面的入口文件,在后续的维护中,须要在mainScreen.qml中添加一系列的按钮,在这种状况下,能够从新写一个继承于mainScreen的界面入口文件main.qml,此时则需从新命名mainScreen.qml为MainScreen.qml。
为了方便qml文件的维护,避免上述状况的发生,qml文件的命名规则为:函数

  • 仅由英文字母组成。
  • 以大写字母开始,遵循大驼峰的命名规则,例如:布局

    SwithcButton.qml
    ListButton.qml
    MainScreen.qml

js文件命名

  • js文件名由小写的英文字母和_组成。
  • js文件名一概以“ impl ”结尾。
    例如:datetime_helper_impl.js
    这样便于在qml中导入
    *.js_ 文件时,方便命名别名。

编写qml文件

import

import的意思是导入模块,相似java语言中的import包,用来导入qml文件所要用到的内置模块、自定义插件、控件、js文件等。
qml文件中,import模块的顺序为:动画

  1. 导入 Qt 内置模块
  2. 导入框架自定义 Qt Quick 模块
  3. 导入项目自定义 Qt Quick 模块
  4. 导入本地 _*.qml_ 组件包
  5. javascript 导入脚本文件
  6. javascript 引用脚本模块

例如:ui

// ModuleA.qml

import QtQuick 2.6
import QtQuick.Controls 2.1

import net.phoneyou.EasyUI 1.0 as HUI
import net.phoneyou.EasyUI.Material 1.0 as HUIMaterial
import net.phoneyou.Roshan.MapRender 1.0 as MapRender2D

import your_project_name 1.0
import "path_to_package"

import "my_component_impl.js" as MyComponent
.import "utils.js" as Utils

为了不不一样模块之间的命名冲突,咱们在导入模块时可使用“as”关键字设置该模块在本 qml 文件中的“别名”。

对于"as"的相关规则说明:

  • 导入Qt内置模块的版本号为对应的Qt版本的最高版本号。
  • js文件导入到qml中必需要有别名。
  • 在导入模块中,自己就有命名冲突时,需给其中至少一个模块都设置别名
  • 不对 Qt 内置模块设置别名
  • 对于自定义模块需设置别名
  • 别名做为模块在所属 qml 文件中的代号,需保持文件内的惟一性
  • 别名的命名规则采用“大驼峰”

为了保证 Qt 内部组件和命名空间不被污染以及保持代码能够顺利升级--好比升级 Qt 版本,须要确保不对 Qt 模块设置别名,而对其余,好比HUI、Roshan以及其余三方的或者项目内的模块,进行“别名”处理。尤为是那些对常见控件进行定制的模块。

根组件

qml文件描述的实际上一个由 “UI组件节点” 组成的“树”。而“根组件”也就是“根节点”,在qml文件有且只有一个。

根组件做为逻辑上的顶层组件,直接承担起了 qml 文件的全部定义,其属性、信号、方法即为该 qml 组件的属性、信号、方法。

其余组件只能做为其子节点存在,对外是不可见的,相似 private 做用域,他们的属性、信号、方法命名需添加双下划线("__")前缀,而根组件没有任何的先后缀。

关于根组件的类型:

  • 若是该qml文件声明的是对已有组件进行改造的可复用的组件,好比 “ImageButton”,那么该根组件应该为“Button”或者其余的什么“Button”,对应的别名应该设置为“button”或“root”。
  • 若是该qml文件声明的是一个复合的功能型界面,好比说“ReplaybackPanel”,“DataDelegateItem”,它是一系列组件的组合,通常将根组件设定为“Item”或者“Rectange”,其别名应设置为“root”。

根组件定义

  1. id
  2. 自定义属性property
  3. 信号signal
  4. JavaScript function
  5. 自带属性设置object properties
  6. 子控件child objects
  7. 状态states
  8. 动画animation、渐变transitions

例如:

Rectangle {
      id: root
      /*! 鼠标按下状态,当鼠标按下时为true */
      property bool pressed: false

      /*! 图片资源 */
      property alias source: photoImage.source

      /*! 鼠标点击事件 */
      signal clicked

      /*! 返回photoImage的width */
      function doSomething()
      {
          return photoImage.width
      }

      width: {
          if (photoImage.width > 200) {
              photoImage.width;
          } else {
              200;
          }
      }
      height: 480
      color: "gray"

      Rectangle {
           id: photoRect
           x: 20
           y: 20
           width: parent.width
           height: 200
           color: "white"
           Image {
               id: photoImage
               anchors.centerIn: parent
            }
      }
      MouseArea {
          anchors.fill: parent
          onPressed: {
              root.pressed = true
          }
          onClicked: {
              root.clicked()
          }
      }
      states: State {
          name: "selected"
          when: root.pressed
          PropertyChanges {
              target: root.border
               color: "red"
          }
      }

      transitions: Transition {
          from: ""
          to: "selected"
          ColorAnimation {
              target: root.border
              duration: 200
          }
      }
  }
id

在声明组件时,能够同时声明其“id”属性。“id”属性是该组件的代号,声明该属性的好处是,能够被它的做用域为:。

同一qml文件中,每一个控件均可以设置id属性,且不能相同,id相同会有书写错误提示。

具体命名规则以下:

  • 由字母组成,首字母必须小写,以小驼峰的形式命名。
  • 以控件类型结尾,例如:
    将Button的id设置为:xxxxBtn 或 xxxxButton
    将Text的id设置为: xxxxTxt 或 xxxxText
    将ComboBox的id设置为: xxxxCmb 或 xxxxComboBox
    这样根据id就能够知道其对应的是什么控件。
    只有在明确知道其控件的缩写时,才能在id中使用缩小,不然一概使用控件全称。
    部分控件的缩写详见“经常使用控件的缩写”
  • 控件默认不设置id属性,若某个控件的id须要被使用(控件间存在关联关系),才要设置id。
    例如控件的布局存在相对位置的关系或控件间的属性在逻辑上存在依赖关系等,则须要设置id,例如:

    Item{
          width: 200
          height: 300
          Button{
              id: saveBtn
              width: 60
              height: 30
              text: "Save"
          }
          Button{
              anchors {
                  top: saveBtn.bottom
                  topMargin: 10
                  left: saveBtn.left
              }
              width: 60
              height: 30
              visible: saveBtn.visible
          }
      }
    代码中的Item和第二个Button没有被引用到,没必要设置id,这样可避免设置过多的无用id,徒增命名的烦恼。
  • 当子控件要使用父控件的属性或根据父控件设置相对位置时,不要使用父控件的id,用parent代替id。例如:

Item {
    id: root
    width: 800
    height: 480
    Rectangle{
        id: testRect
        anchors.centerIn: parent
        width: parent.width/2
        height: 200
        color: "red"

        Button{
            id: testBtn
            width: parent.width/2
            height: 40
            text: "1233"
            highlighted: (parent.color === "red") ? false : true
        }

    }
}
  • 一个qml文件中,设置id的个数不要超过7个,多余7个,则要进行qml代码的拆分,建议设置3到4个。
  • 同一个qml文件中的控件id在文件中是通用的,可是delegate中的控件id除外,delegate中控件id只在delegate中有效,外部访问无效。
    由于 delegate为Component类型,外界没法访问。
    通俗点讲,就是delegate通常用来承载model数据的显示,一个id对于多个数据显示控件,外部在使用id时,没法定位当前使用是哪一个数据控件的id。例如:
ListView {
        id:listView
        anchors.centerIn: parent
        width: 160
        height: 240

        model: Qt.fontFamilies()

        delegate: ItemDelegate {
            id:fontDelegate
            text: modelData
            width: parent.width
            highlighted: ListView.isCurrentItem
            onClicked:{
                listView.currentIndex = index
                console.log("clicked:", modelData)
            }
        }
    }

代码中ItemDelegate的id名称fontDelegate,是不能在ItemDelegate以外被使用的。

property属性

在qml代码中,为了方便外界使用本自定义控件或者便于qml文件内部控件之间的关联,常须要自定义一些属性。
一个property是对象的一个属性,能够被赋为静态值或者是绑定到动态表达式上。一个property的值能够被其它的对象读取。通常状况下,property属性也能够被其它对象修改,除非该QML类型明确指定该property属性不能被修改。

  • 属性的命名规则:
    属性名称必须是以小写字母开头,仅由字母、数字和下划线组成,由 名词或形容词+名词 的小驼峰形式组合而成。
    当定义的属性仅限qml内部使用时,才会用到_,这样的私有属性的命名必须以两个下划线(__)开始,例如:
property var __locale: Qt.locale()
自定义属性

自定义属性的语法格式以下:

property <propertyType> <propertyName> : value
  • 定义一个自定义的property属性也就为该property属性隐式的建立了一个value-change信号,也就是关联了一个名为 on Changed的signal handler。 就是property属性的名称,并且首字母要大写。,以下所示:
Rectangle {
        property string someText: “ ”
        onSomeTextChanged: console.log("The someText will be: " + someText)
    }
  • 自定义的属性必须要有初始值,除了Object Types
  • 常见自定义的属性类型:
    bool,double,int,real,string,url,var,list
  • bool:true/false。
  • double:双精度实数,有小数点。
  • int:整数,能够是负数、0、正数。
  • real:实数,有小数点。
  • string:字符串。
  • url:资源路径,能够是绝对路径或相对路径,相对路径时就会被转换为URL对象,若是使用了Qt资源系统,就要用“qrc:///”代替“:/”。
  • var:基本类型是通用的类型,能够保存任意类型的值,包括lists和objects:
    任何标准的JavaScript类型均可以使用通用的var类型建立和存储。
    若是声明一个属性用于存储列表值,但不必定是存储QML对象类型值,这个时候你就须要声明var的property属性。
property var someNumber: 1.5
    property var someString: "aaa"
    property var someBool: true
    property var someList: [2,5,"one","two"]
    property var someObject: Rectangle {
                                width: 100
                                height: 100
                                color: "red"
                            }
   property var theArray: new Array()
   property var theDate: new Date()
另外,任何的QML对象类型均可以被用做property属性类型。例如:
property Item someItem
    property Rectangle someRectangle

这对于自定义QML类型也是适用的。

  • 自定义属性的调用方式:
    属性所属控件id. propertyName
属性别名
  • 属性别名就是保存对另外一个属性的引用。不像普通属性的定义,普通属性的定义须要分配一个新的,惟一的存储空间,而一个属性别名仅仅是链接到了属性上。
    属性别名的定义根属性定义差很少,只是属性别名须要使用alias关键字代替属性定义中的property类型,右边的值必须是合法的引用别名:
    属性别名的语法格式以下:
property alias <name> : aliasreference

name是咱们自定以的属性名,aliasreference是属性别名所引用的那个属性或对象,也就是说属性别名能够引用自一个单一的属性,也能够引用自一个复杂的对象。属性绑定是动态的,但不是双向的,而经过属性别名可达到动态、双向的效果,即修改name和aliasreference中任一个的值,与之关联的另外一个属性值也会随之改变。
须要注意的是,若是属性别名与已有的属性名相同,就会把已有的属性覆盖掉,当咱们再使用这个属性的时候,实际上使用的是咱们自定义的那个属性,例如:

Rectangle {
    id: rootRect
    // 引用属性
    property alias color: blueRect.color
   // 引用对象
    property alias theRect: blueRect

    color: "red"

    Rectangle {
        id: blueRect
        color: "#1234ff"
    }
}
  • 若是为一个已经存在属性建立一个同名的属性别名,这就会覆盖已经存在的属性。
    上面的代码中定义的color属性,会替代Rectangle原有的color属性,为了不已有属性被覆盖的问题,定义的属性别名不能与已有属性相同。
  • 若所引用的属性不是已经存在属性,则定义的属性别名要与引用的属性名称相同。 例如:
Rectangle {
    id: rootRect
    property alias count: nameList.count
    property alias currentIndex: nameList.currentIndex
    color: "red"
    ListView{
        id: nameList
    }
}

Rectangle没有count和currentIndex属性,则要将属性别名与引用属性名设置成同样。

  • 只有在组件彻底初始化以后属性别名才会被激活。若是未初始化的别名被引用了就会产生错误。
  • 为一个属性别名产生属性别名也会致使错误,例如:
property alias sureBtn: testBtn
property alias text: sureBtn.text
Button{
    id: testBtn
    width: 100
    height: 40
    text: "1233"
}
Component.onCompleted: console.log("text="+text)

产生的错误提示: Invalid alias reference. Unable to find id "sureBtn"

只读属性

任何对象的定义均可以使用readonly关键字定义只读属性,使用下面的语法:

readonly property <propertyType> <propertyName> : <initialValue>

只读属性必须在初始化的时候指定值。一旦只读属性被初始化了,它就不可能再被赋值了,不管是赋值(使用”=”)仍是其它的方式。
例如,下面的Component.onCompleted代码块就是非法的:

Item{
    id: root
    readonly property int chiledNum: 10
    width: 100
    height: 40
    Component.onCompleted: root.chiledNum = 20
}
信号

信号就是当某些事件发生的时候从对象类型中发出通知:例如,一个属性改变,一个动画开始或者中止,当一个图片下载完成,或者MouseArea类型当用户点击的时候就会发射一个点击信号。
当一个信号发射了,对象能够经过signal handler被通知。一个signalhandler的定义语法为on 是信号的名称,首字母要大写。Signalhandler必须在发射该信号的对象定义的内部实现,而且signalhandler必须包括JavaScript代码块,当signal handler被调用的时候该代码块就会被执行。
例如,MouseArea对象的定义中能够定义onClicked类型的signal handler,当MouseArea被点击了该signal handler就会被调用,使得控制台打印出消息:

Item{
    id: root
    width: 100
    height: 40
    MouseArea{
        anchors.fill: parent
        onClicked: console.log("Click!")
    }
}

qml文件中,定义信号signal的语法:

signal <signalName>[([<type> <parameter name>[, ...]])]

信号通常是相关联的事件触发的,信号名由英文字母和_组成,已小驼峰的形式命名。

  • 信号的命名规则遵循属性的命名规则,以小驼峰的形式命名。
  • 若是信号不带参数,则一概不使用()
  • 若是信号带参数,参数要写参数类型。
    信号参数的命名规则:
    msg+大驼峰形式
    必须以“msg”开头,便于标识是信号参数。
    例子:
signal clicked
signal hovered(string msgInfo)
signal activated(real msgXPosition, real msgYPosition)

信号的调用方式:信号所属控件id.信号

js代码

自定义的js函数,能够带参数,也能够不带出参数,且参数不用指明参数类型,由于函数的参数类型默认是万能的var类型。
函数名由字母和_组成,且首字母必须是小写,即以小驼峰的形式命名,例如:

function addZero(num){
    if(num < 10)
        return "0"+num
    else
        return num
}
  • 函数返回时不要使用圆括号。
  • 当定义的js函数,仅能在本qml文件使用,即便私有函数时,此函数的命名才用到_, 且是以双下划线(__)开始 + 小驼峰的方式命名,例如:
function __factorial(a) {
    a = parseInt(a);
    if (a <= 0)
        return 1;
    else
        return a * factorial(a - 1);
}
  • qml文件中js函数的调用:函数所在控件的id.函数名()
js文件

当一个qml文件中的js代码较多即:js函数代码超过80行时,应根据其分类或应用逻辑单独做成一个js文件,再将js文件导入qml文件。
js文件的导入方法:

  • qml中导入的js文件必需要有别名,别名的命名规则:
    以大驼峰的形式命名
    由js文件名中除了_和impl之外的字母组成,例如:
import "jsfile_impl.js" as Jsflie
  • 在js文件中导入另外一个js文件的2种方法 :
.import "filename_imp.js" as Filename
Qt.include "filename.js"`
布尔表达式
  • 在布尔表达式中,逻辑运算符 && 和|| 两边的条件表达式根据逻辑须要添加圆括号()
状态机
  • 在qml中,状态是定义在state类型中的一系列属性配置,不一样的配置可能有不一样的做用,为了方便起见,State元素都有一个when属性,能够绑定表达式来改变状态,当绑定的表达式评估为true,状态改变;当表达式评估为false,状态会退回到default state。
  • 显示一些UI控件,隐藏另外一些。
StateGroup {
    states: [
        State {
            name: "xxx"
            when: visible
            PropertyChanges {
                target: xxxBtn
                visible: true
            }
            PropertyChanges {
                target: yyyBtn
                visible: true
            }
            PropertyChanges {
                target: zzzBtn
                visible: fasle
            }
        }
    ]
}
  • 启动、暂停、中止动画。
StateGroup {
    states: [
        State {
            name: "start"
            when: 0 == animationState
            PropertyChanges {
                target: imageAnimation
                running: true
            }
        },
        State {
            name: "pause"
            when: 1 == animationState
            PropertyChanges {
                target: imageAnimation
                paused: true
            }
        },
        State {
            name: "stop"
            when: 2 == animationState
            PropertyChanges {
                target: imageAnimation
                running: false
            }
        }
    ]
}
  • 改变某个特定Item的property的值。
StateGroup {
    states: [
        State {
            name: "color"
            when: bChanged
            PropertyChanges {
                target: rootRect
                color: "red"
            }
        },
        State {
            name: "enabled"
            when: disabled
            PropertyChanges {
                target: rootRect
                enabled: false
            }
        }
    ]
}
  • 显示一个不一样的view或screen。
StateGroup {
   states: [
       State {
           name: "xxx"
           when: 0 == loaderState
           PropertyChanges {
               target: xxxLoader
               source: "Xxx.qml"
           }
       },
       State {
           name: "yyy"
           when: 1 == loaderState
           PropertyChanges {
               target: xxxLoader
               source: "Yyy.qml"
           }
       },
       State {
           name: "zzz"
           when: 2 == loaderState
           PropertyChanges {
               target: xxxLoader
               source: "Zzz.qml"
           }
       }
   ]
}
  • 在某种新的状态下执行某些脚本。
// statetest.js
function getColor()
{
    return "green";
}

// test.qml
import "statetest.js" as StateTest
Rectangle{
    id: rect
    width: 50; height: 50
    color: "red"
    MouseArea {
        anchors.fill: parent
        onClicked: rect.state = "changedColor"
    }
StateGroup {
    states:
        State {
            name: "changedColor"
            StateChangeScript{
                name: "myScript"
                script: rect.color = StateTest.getColor()
            }
        }
    }
}
  • 向用户呈现不一样的操做和功能
Rectangle {
    id: rect
    width: 120; height: 120
    color: "black"
    Rectangle { id: myRect; width: 50; height: 50; color: "red" }
    StateGroup {
        states:
            State {
                name: "reanchored"
                AnchorChanges { // 改变 myRect 的anchors属性
                target: myRect
                anchors.top: rect.top
                anchors.bottom: rect.bottom
            }
            PropertyChanges {
                target: myRect
                anchors.topMargin: 10
                anchors.bottomMargin: 10
            }
        }
    }
    // 鼠标事件
    MouseArea {
        anchors.fill: parent
        onClicked: rect.state = "reanchored"
    }
}
  • 把指定的item换一个item父节点
Item {
        width: 200
        height: 100
        Rectangle {
            id: redRect
            width: 100
            height: 100
            color: "red"
        }
        Rectangle {
            id: blueRect
            x: redRect.width
            width: 50
            height: 50
            color: "blue"
            states: State {
            name: "reparented"
            ParentChange {
                target: blueRect
                parent: redRect
                x: 10
                y: 10
            }
        }
        MouseArea {
            anchors.fill: parent
            onClicked: blueRect.state = "reparented"
        }
    }
}
  • states中每一个state之间的触发条件与PropertyChanges中的属性之间不要有关联,如有关联,可能会造成死循环。

  • 在qml中使用states时,必定要加上父层StateGroup,这样能够避免与原始控件中state名相同的state相冲突,产生没法预料的问题。

  • 能用条件运算符替代states的地方,尽可能用条件运算符,不要用states。条件运算符的执行速度比state快。

StateGroup {
    states:
        State {
            name: "name"
            when: bChanged
            PropertyChanges {
                target: root
                color: "red"
            }
        }
}

应修改成:

color: bChanged ? "red" : "white"`
  • 同一个State中有多个PropertyChanges,PropertyChanges的target不能设置同样,不然只执行第一个PropertyChanges。

例如:
错误的写法

StateGroup {
    states:
        State {
            name: "name"
            when: bChanged
            PropertyChanges {
                target: root
                color: "red"
            }
            PropertyChanges {
                target: root
                border.color: "red"
            }
        }
}

应该修改成:

StateGroup {
    states:
        State {
            name: "name"
            when: bChanged
            PropertyChanges {
                target: root
                color: "red"
                border.color: "red"
            }
        }
}

内部组件

与“根组件定义”相同,可是其属性、信号、方法命名需添加双下划线("__")前缀。
好比:

Item {
        id: root
        width: 800
        height: 480
        Item {
            id: subItem

            property bool __updateState:false

            signal __clicked

            function __doSomething(x)
            {
                return x+subItem.x
            }

            width: 400
            height: 400

        }
    }

注释

为了提升qml代码的可读性和便于后期维护,注释是必不可少的。

文件功能注释

文件功能注释是对此qml大概功能的一个概述,若此qml文件还做为控件别的qml文件使用,则还有写出大概的使用方法。
例以下面是combobox.qml文件功能的注释:

/*!
将一个model的数据如下拉框列表的形式显示,经过选择下拉框列表数据,改变当前显示的数据
    \qml
       ComboBox {
           width: 200
           model: [ "Banana", "Apple", "Coconut" ]
       }
    \endqml
*/

注释以“/!”开始,以“ /” 结束,包含的对文件功能的说明,和控件的使用方法,使用用例以“\qml”开始,以“\endqml”结束。

自定义属性、信号、方法注释

  • 自定义的属性、信号、方法必须添加注释。
    自定义属性、信号、方法注释格式为:“/! /”,写在其定义的上一行,例如:
/*! ComboBox下拉框列表当前选中的index值 */
    property int currentIndex: 0

其余注释

这里的其它注释是指除文件功能注释和自定义属性、信号、方法注释外的其它须要注释的代码。
代码注释格式为:“ // xxxxxxxxx ”,写在被注释代码上方,例如:

// temporarily set fillIndex to new item
fillIndex = __items.length

补充说明

object properties

  • 一行写一条语句

通常属性设置的顺序为:

id: rootRect
x: 10
y: 10
width: 100
height: 100
...

组属性的设置

在对控进行属性设置时,若是有同一类组的多个属性须要设置,为了提升代码可读性,则使用组的提示符,而不是点的运算符号。
例如:

anchors.top: parent.top
anchors.left: parent.left
anchors.topMargin: 10
anchors.leftMargin: 10

应该改写成:

anchors {
    top: parent.to
    left: parent.left
    topMargin: 10
    leftMargin: 10
}

Qml中和c++中均可以处理的逻辑

在qml文件中,有许多的逻辑处理既能够在qml中完成,也能够在c++中处理,原则是qml代码只处理界面显示逻辑,其它逻辑能在c++中处理的,一概在c++中处理,这样有利于提升qml的加载速度 。

MouseArea{ anchors.fill: parent }的特殊用法

MouseArea除了处理Item的鼠标响应,还有一个典型的用途是,MouseArea { anchors.fill: parent }可以屏蔽鼠标事件的渗透。
例如:

// main.qml
   Rectangle{
      width: 800
      height: 480
      Button{
            width: 150
            height: 50
            text: "Button"
            onClicked: {
                if(ld.source === “ ”){
                    ld.source= "TestChild.qml"
                }
            }
      }
      Loader{
        id:ld
      }
   }

   // TestChild.qml
   Rectangle{
      width: 800
      height: 480
   }

当TestChild界面加载后,在TestChild界面上与main界面的Button所处位置的重叠处点击,会触发main界面Button的点击事件,
这就是一个界面的鼠标事件参透到另外一界面。为了不鼠标渗透事件的发生,TestChild.qml应该修改成:

Rectangle{
      width: 800
      height: 480
      MouseArea { anchors.fill: parent }
   }

注意:MouseArea { anchors.fill: parent }要做为TestChild界面的第一个子控件,才能屏蔽整个TestChild的鼠标渗透,又不影响TestChild界面中其它可操做控件事件。

经常使用控件的缩写

1.Button -> btn
1.CheckBox -> chk
1.ComboBox -> cmb
1.RadioButton -> rdo
1.Text -> txt
1.Label -> lbl
1.Image -> img
1.DateTimePicker -> dtp
1.ListView -> lvw
1.ToolTip -> tip
1.GroupBox -> grp
1.Panel -> pnl
1.Calendar -> cal
1.TreeView ->trvw
1.ListView->lvw
1.GridView -> gvw
1.Repeater->rpt
1.Menu ->mnu
1.ProgressBar ->prg

属性的绑定

所谓属性绑定,就是创建目标对象对其它对象的依赖关系,当其它对象发生变化时,目标对象也会变化,自动更新相关的属性值,达到动态属性的效果,例如:

Item {
        id: root
        width: 800
        height: 480
        focus: true
        Keys.enabled: true
        // 从新绑定
        Keys.onEnterPressed: redRect.width = Qt.binding(function() {return parent.width/2})
        Rectangle {
            id: redRect
            anchors.fill: parent
            width: parent.width/3  // 绑定
            height: 100
            color: "red"
        }

        Button{
            width: 60
            height: 30
            text: “Button”
            onClicked: redRect.width = 150    // 绑定解除
        }
    }

上述例子中,redRect的width属性最初绑定到了root的width属性上,redRect的width会随着root的width变化而变化;Button按下后,redRect的width属性绑定解除;Enter键按下后,redRect的width属性从新绑定;Button再次按下后,redRect的width属性绑定解除。
也就是说若是属性绑定已经完成,那么在别的地方从新给这个属性赋值时,不论是赋个静态值,仍是想换个别的属性绑定表达式,都会破坏原来的绑定关系,而新的值也不会有动态属性的效果。

Keys的载体

Keys是专门用来处理键盘事件KeyEvent的,它定义了许多针对特定按键的信号,例如digit0Pressed(KeyEvent event)、spacePressed(KeyEvent event)等,不过使用pressed(KeyEvent event)和released(KeyEvent event)这两个普通的信号就能够处理大部分按键事件了,信号的参数类型是KeyEvent,参数名是event,包含了按键的详细信息。
这里说所的载体是指Keys所属的父组件,Keys的载体必须是Item,或从Item继承而来,不能是Window或从Window继承而来的组件,不然Keys无效。
例如:

Window {
      visible: true
      width: 360
      height: 360
      Keys.enabled: true;
      Keys.onEscapePressed: Qt.quit()             // 没有功能: 不退出
Window {
      visible: true
      width: 360
      height: 360
      Item{
            anchors.fill: parent
            focus: true
            Keys.enabled: true;
            Keys.onEscapePressed: Qt.quit()             // 有功能: 退出
      }

Loader

QML的Loader元素常常备用来动态加载QML组件。可使用source属性或者sourceComponent属性加载。这个元素最有用的地方是它能在qml组件须要的时候再建立,即延迟建立QML的时间。
加载与被加载组件中都有相同的事件,那么须要设置Loader的属性focus为true,且设置被加载组件 focus: true才能让事件不被传播到被加载组件。

main.qml
Item {
    property bool isFirst : false;
    width: 200
    height: 200

    Loader {
        id: pageLoader
        source: "Page2.qml"
        focus: true
    }

    Keys.onPressed: {
        console.log("Captured: ", event.text);
        event.accepted = true;
    }
}


// Page2.qml

Rectangle {
    width: 100
    height: 62
    Text {
        anchors.centerIn: parent
        text: "Page2 Test"
    }
    focus: true
    Keys.onPressed: {
        console.log("Loaded item captured: ", event.text)
        event.accepted = true
    }
}

若将main.qml中Item的focus设置为true,则上述代码的只有main响应按键消息。

  • 若是source或者sourceComponent更改了,任何先前实例化的项目都将被自动销毁。

  • 将source设置为空字符串或者sourceComponent设置为undefined,则销毁当前加载的项目,释放资源,并将Loader设置为空。

  • Loader出来的qml界面,在界面关闭时,必定要清空Loader资源。

qml的目录结构

在独立项目或者Roshan插件中,qml的目录结构都是相似的。
资源

qml.qrc
components
img
views

main.qml

  • components文件夹用来放置项目或插件的专有控件。
  • img文件夹用来放置项目或插件的图片资源。
  • view文件夹用来放置用来放置除专有控件、图片资源和main.qml外的其余文件。

model

QML中,model是为ListView,GridView和Repeater等元素来提供要显示数据的数据模型.这些元素须要一个为模型中的每一项数据生成一个实例的代理组件(delegate component).给代理提供的数据经过叫作角色的数据绑定到代理.
例以下述代码中,ListModel有两个角色:text 和 age,与代理模型Text的属性text的名称相冲突。
在这种状况下,若是直接在代理中绑定角色名称,则代理Text的text不会显示模型中text角色值,而是显示它自身的属性值。为了不这种冲突的产生,在角色前面加上model.便可正常显示。

Item{
        width: 200
        height: 200
        ListView{
            id: testList
            anchors.fill: parent
            model: testModel
            delegate: Text{
                width: lst.width
                height: 30
               // text: text + "-" + age
                text: model.text + "-" + model.age
            }
        }
        ListModel{
            id: testModel
            ListElement{
                text: "ccw"
                age: 12
            }
            ListElement{
                text: "ym"
                age: 13
            }
            ListElement{
                text: "zfb"
                age: 35
            }
            ListElement{
                text: "ded"
                age: 30
            }
        }
    }

其它

  1. js代码中,可以用条件运算符(? :)代替 if...else...地方,要用条件运算符,这样是代码更简洁明了。

  2. js中“==”和“===”的区别:
    ==用于通常比较,在比较的时候能够转换数据类型。
    ===用于严格比较,只要类型不匹配就返回flase。

  3. 当一个qml文件中,代码行达到200至300行时,要从其qml文件中分解出子qml文件。
    这样能够避免出现控件关联复杂,篇幅较长的代码。

  4. 只有一条语句的代码块,写成一行,例如:

  MouseArea { anchors.fill: parent }

在信号处理代码中,若是也只有一条执行语句,则写成一行,且不须要{},例如:

onPressed: {
        rootRect.color= "gray"
    }

应该写成:

onPressed: rootRect.color= "gray"
相关文章
相关标签/搜索