4. QML语言入门:QML语法基础与基本元素
好,咱们开始聊QML。说实话,我第一次接触QML的时候,心里是有点抵触的——又学一门新语言?但用了一周之后,我彻底改观了。QML的声明式语法,说白了就是「你想要什么,就直接写什么」,特别适合做车载界面这种需要频繁迭代的场景。
4.1 QML语法基础
QML的语法结构其实很简单。一个QML文件就是一个对象树,每个对象都有属性、信号和绑定。我习惯把QML理解成「带逻辑的JSON」——结构清晰,但能跑起来。
4.1.1 属性(Property)
属性就是对象的特征。比如一个矩形的宽、高、颜色。写法很直接:
Rectangle {
width: 200
height: 100
color: "blue"
radius: 10
}
嗯,这里要注意:属性名是大小写敏感的。我在项目中遇到过有人把color写成Color,结果界面死活不显示颜色——找了半天才发现是大小写问题。
属性还可以是表达式。比如:
Rectangle {
width: parent.width * 0.5
height: parent.height * 0.3
}
你想想看,这种写法多方便。父容器一变,子元素自动跟着调整。这就是QML的「绑定」机制在起作用。
4.1.2 信号(Signal)
信号是QML里的事件机制。比如按钮被点击、滑块被拖动,都会发射信号。我刚开始学的时候,总觉得信号和槽的概念很抽象。后来我告诉自己:信号就是「喊一声」,槽就是「听到后干活」。
Button {
text: "点击我"
onClicked: {
console.log("按钮被点击了")
// 这里可以写更多逻辑
}
}
我曾经在车载项目中犯过一个低级错误:信号处理函数里写了耗时的操作,结果界面卡住了。后来才意识到,信号处理应该尽量轻量,复杂逻辑要放到后台去。
4.1.3 绑定(Binding)
绑定是QML最强大的特性之一。它让属性之间自动保持同步。比如:
Rectangle {
width: 200
height: 100
color: mouseArea.pressed ? "red" : "blue"
MouseArea {
id: mouseArea
anchors.fill: parent
}
}
这里color绑定了mouseArea.pressed的状态。鼠标按下时变红,松开后变蓝。不需要写任何更新代码——绑定自动搞定。
Qt.binding()函数显式创建绑定,这样代码更清晰,也更容易调试。
4.2 基本元素
QML里最常用的三个基本元素就是Rectangle、Text和Image。别看它们简单,车载界面里80%的UI都是靠它们拼出来的。
4.2.1 Rectangle(矩形)
Rectangle是QML的「万能积木」。它可以画背景、做边框、当容器。我习惯用它来做所有需要「框起来」的东西。
Rectangle {
width: 300
height: 200
color: "#2C3E50"
border.color: "#3498DB"
border.width: 2
radius: 8
// 渐变效果
gradient: Gradient {
GradientStop { position: 0.0; color: "#2C3E50" }
GradientStop { position: 1.0; color: "#34495E" }
}
}
嗯,这里有个坑:radius属性只对纯色背景有效。如果你用了渐变或者图片背景,圆角可能不会生效。我曾经在这个问题上折腾了一下午。
4.2.2 Text(文本)
Text元素用来显示文字。车载界面对文字的要求很高——字体大小、颜色、对齐方式都要精确控制。
Text {
text: "当前车速: 80 km/h"
font.family: "Arial"
font.pixelSize: 24
font.bold: true
color: "#ECF0F1"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
我建议在车载项目里,字体大小至少用pixelSize而不是pointSize。因为不同屏幕的DPI不一样,pointSize可能会导致文字忽大忽小。
4.2.3 Image(图片)
Image用来显示图片。车载界面里,图标、背景图、按钮图片都靠它。
Image {
source: "qrc:/images/speedometer.png"
width: 100
height: 100
fillMode: Image.PreserveAspectFit
smooth: true
}
注意fillMode属性。我见过很多新手直接用默认的Stretch模式,结果图片被拉伸得面目全非。车载屏幕上,图标比例一定要保持,所以我一般用PreserveAspectFit。
4.3 布局管理器
布局管理器帮你自动排列子元素。手动计算坐标?那是上古时代的做法了。
4.3.1 Row(行布局)
Row把子元素从左到右排成一行。适合做导航栏、按钮组。
Row {
spacing: 10
anchors.centerIn: parent
Rectangle { width: 50; height: 50; color: "red" }
Rectangle { width: 50; height: 50; color: "green" }
Rectangle { width: 50; height: 50; color: "blue" }
}
说白了,Row就是「排排坐,吃果果」。每个元素之间的间距由spacing控制。
4.3.2 Column(列布局)
Column和Row相反,从上到下排列。适合做列表、菜单。
Column {
spacing: 5
anchors.centerIn: parent
Text { text: "设置"; font.pixelSize: 18 }
Text { text: "导航"; font.pixelSize: 18 }
Text { text: "音乐"; font.pixelSize: 18 }
}
我习惯在Column外面包一层Rectangle,这样整个列表看起来有背景,更美观。
4.3.3 Grid(网格布局)
Grid把元素排成网格。适合做图标矩阵、功能面板。
Grid {
columns: 3
spacing: 10
anchors.centerIn: parent
Rectangle { width: 80; height: 80; color: "#E74C3C" }
Rectangle { width: 80; height: 80; color: "#3498DB" }
Rectangle { width: 80; height: 80; color: "#2ECC71" }
Rectangle { width: 80; height: 80; color: "#F39C12" }
Rectangle { width: 80; height: 80; color: "#9B59B6" }
Rectangle { width: 80; height: 80; color: "#1ABC9C" }
}
你想想看,如果手动计算每个方块的位置,得写多少代码?Grid一行搞定。
4.3.4 StackView(堆叠视图)
StackView是QML里做页面切换的利器。它像一叠卡片,每次只显示最上面那张。车载界面里,从主菜单切换到设置页面,用StackView最合适。
StackView {
id: stackView
anchors.fill: parent
initialItem: mainPage
Component {
id: mainPage
Rectangle {
color: "#2C3E50"
Text {
text: "主页面"
anchors.centerIn: parent
color: "white"
font.pixelSize: 24
}
}
}
Component {
id: settingsPage
Rectangle {
color: "#34495E"
Text {
text: "设置页面"
anchors.centerIn: parent
color: "white"
font.pixelSize: 24
}
}
}
}
// 切换页面
Button {
text: "进入设置"
onClicked: stackView.push(settingsPage)
}
嗯,这里要注意:push和pop操作要成对使用。我曾经在项目中连续push了5个页面,结果用户按返回键时,界面跳得飞快——因为StackView里堆了太多页面。后来我加了个限制,最多保留3层。
好了,这一章的内容就到这里。下一章我们会深入QML的动画系统——让界面动起来,才是车载娱乐系统的灵魂。