前言
本文主要想要记录面试时可能会被问到的一些qml常见问题,帮助面试前快速复习和回顾。
由于是记录文章,所以会不断往后添加问题,必要时会另开新文章。
一、QML中组件呈现方式的方法有哪些?
1.基础属性绑定(最直接的 UI 数据呈现)
这是 QML 的基石。无论是 QML 内部的 property 还是从 C++ 暴露过来的对象属性,只要把 UI 组件的属性(如 text)和它进行绑定,数据一变,界面自动刷新。
cpp
import QtQuick
import QtQuick.Controls
Window {
width: 400; height: 200; visible: true
title: "基础属性绑定"
// 1. 在 QML 内部定义属性变量
property string statusText: "等待操作..."
property int clickCount: 0
Column {
anchors.centerIn: parent
spacing: 20
// 2. 直接将属性绑定到组件上
Label { text: statusText; font.pixelSize: 20 }
Button {
text: "点击我"
onClicked: {
clickCount++
// 修改属性,界面上的 Label 会自动更新
statusText = "按钮已被点击 " + clickCount + " 次"
}
}
}
}
注:如果是 C++ 交互,你只需要把 statusText 换成从 C++ 注册进来的单例对象属性(如 cppBackend.statusText)即可,绑定语法完全一致。
2.绑定C++侧对象属性
这部分是上述方法的补充,详情可以回看之前学习记录的博客文章。
核心思想是将C++元素暴露给qml侧,让它能够进行调用。这里主要分为几种思路。
(1)上下文属性暴露
通过以下接口,我们可以将某个属性暴露给qml侧。
cpp
QString lastName = "Doe";
QString firstName = "John";
engine.rootContext()->setContextProperty("lastname", QVariant::fromValue(lastName));
engine.rootContext()->setContextProperty("firstname", QVariant::fromValue(firstName));
可以看到,这种方式并没有创建什么c++实例对象,单纯只是将一个变量注册暴露给qml。另外,传参的名字类似别名,可以和原变量名不一样。
但上下文属性暴露同样支持暴露实例对象,
cpp
CppClass1 cppclass1;
engine.rootContext()->setContextProperty("CppClass1",&cppclass1);
这种方法也是给类实例对象取别名,其公共接口使用Q_INVOKABLE 修饰后,可在qml中直接被调用。而信号不需要修饰也可以用。
重点来了,如果需要使用其变量,我们需要先在头文件声明中,对某个成员变量进行Q_PROPERTY属性声明。
这里的成员变量和Q_PROPERTY声明的属性,本质上不是绑定关系,是因为我们在该属性的具体方法(读写、通知)中调用了成员变量,故形成了绑定关系。此后,在qml中调用该属性,就相当于绑定了成员变量。如:text: xxx.name
(2)上下文对象暴露
和上述的方法类似,但这种方法暴露实例对象的时候,不需要取别名,qml使用的时候没有名字修饰,直接引用属性(变量)名(需要Q_PROPERTY修饰),这看上去比较精简方便,但有命名冲突的风险。
个人不推荐这种方式,如果需要单例,我们可以使用qmlRegisterSingletonInstance的方式
(3)单例
略。详情看博客文章。
3.信号槽
qml中同样支持信号槽的方式。
如果想要在qml中呈现数据,可以在qml中接收某个c++对象的信号。
方法大概如下,首先,我们需要对c++某个实例对象进行属性暴露,然后在qml中Connections中实现槽函数。具体实现类似如下:
cpp
Connections{
target: CppSignalSender
onCallQml: function(parameter){
console.log("This is QML: callQml signal cought:")
mText.text = parameter
}
onCppTimer: function(value){
mRectText.text = value
}
}
4.ListView中呈现数据
由于面试官问到过这个,我就记录一下。(相关笔记55-57)
实际上,在qml中,这是比较典型的model-view结构,还涉及到delegate的概念。
我不想过多展开,简单来说,view即视图层的组件,即listview,model是数据来源,而delegate则是委托,它比较抽象,除了决定ui样式,还可以决定数据的表现方式。
比如model的一项数据(listelement)里面有name和price两个属性,而delegate可以决定呈现方式为rectangle里内嵌一个文本,文本绑定了:name+"---"+price。这是相当灵活的表现形式。
listview对应的就是listmodel,有关model的定义形式比较丰富,较为正式的是这种,ListElement是其中一个元素:
cpp
ListModel{
id: modelId
ListElement{
country:"China"
capital: "BeiJing"
}
ListElement{
// ...
}
}
还有就是这种内联定义的方式:
cpp
model: ["January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"]
方式有很多,但这种方式的数据来源都是qml。
如果想要从c++侧获取数据来源,可以在那边定义model,然后通过属性暴露的方式,进行绑定。又或者通过信号槽的方式发送过来。虽然还没有验证过,但应该是可以的。