implicitWidth
:隐式推荐宽度。
官方文档:
- 当控件不是布局的子项时,在没有明确指定
width
时,控件希望占据的大小,当设置width
属性时,会忽略掉implicitWidth
。[后面示例1]
- 如果一个组件是布局的子项,那么布局会使用组件的隐式大小(
implicitWidth
)属性来确定组件的首选大小(preferredSize
),在这种情况下,如果组件具有显示的宽度或者高度设置,这些显示设置会被忽略。[后面示例2]
- 大多数情况下,组件的默认隐式大小为0,即没有大小,但是有一些组件具有固定的隐式大小,这些大小是无法覆盖的,例如
Image
和Text
组件。[后面示例3]
示例1:
Window {
id: container
width: 600
height: 600
visible: true
title: qsTr("MyQML")
color: "white"
Rectangle {
x: 100
y: 100
// width: 100
height: 100
color: "red"
implicitWidth: 200
}
}
非布局子项时,这里没有设置width属性
,会使用implicitWidth
的值,效果:
同时设置width
和implicitWidth
属性,
Window {
id: container
width: 600
height: 600
visible: true
title: qsTr("MyQML")
color: "white"
Rectangle {
x: 100
y: 100
width: 100
height: 100
color: "red"
implicitWidth: 200
}
}
这时width
会起效,implicitWidth
被忽略,效果:
示例2:
在布局中,同时设置width
和implicitWidth
属性,这时优先使用implicitWidth
,width
会被忽略,
Window {
id: container
width: 600
height: 600
visible: true
title: qsTr("MyQML")
color: "white"
RowLayout {
width: 600
height: 150
spacing: 0
Rectangle {
height: parent.height
color: "blue"
implicitWidth: 500
width: 200
}
}
}
效果图:
如果只设置width
属性,
Window {
id: container
width: 600
height: 600
visible: true
title: qsTr("MyQML")
color: "white"
RowLayout {
width: 600
height: 150
spacing: 0
Rectangle {
height: parent.height
color: "blue"
// implicitWidth: 500
width: 200
}
}
}
效果如图:
如果width
属性也没有设置,
Window {
id: container
width: 600
height: 600
visible: true
title: qsTr("MyQML")
color: "white"
RowLayout {
width: 600
height: 150
spacing: 0
Rectangle {
height: parent.height
color: "blue"
// implicitWidth: 500
// width: 200
}
}
}
那么这个控件将不会显示出来,因为默认情况下,Rectangle
的implicitWidth
值为0。
示例3:
接下来我们就来看特例,比如Text
,首先就是只设置width
属性:
RowLayout {
width: 600
height: 150
spacing: 0
Text {
text: qsTr("text")
font.pixelSize: 30
height: parent.height
//为了让字体居中显示
horizontalAlignment: Text.AlignHCenter
onWidthChanged: console.log("width:", width)
onImplicitWidthChanged: console.log("ImplicitWidth", implicitWidth)
width: 200
// implicitWidth: 200
// Layout.fillWidth: true
// Layout.preferredWidth: 300
}
}
运行结果:
日志打印:
qml: ImplicitWidth 54
qml: ImplicitWidth 54
qml: width: 54
可以发现width
没有任何效果!! ,可以发现width
和implicitWidth
都是54,而54就是该Text
的真实宽度,我们直接不设置width
属性看看,
RowLayout {
width: 600
height: 150
spacing: 0
Text {
text: qsTr("text")
font.pixelSize: 30
height: parent.height
//为了让字体居中显示
horizontalAlignment: Text.AlignHCenter
onWidthChanged: console.log("width:", width)
onImplicitWidthChanged: console.log("ImplicitWidth", implicitWidth)
// width: 200
// implicitWidth: 200
// Layout.fillWidth: true
// Layout.preferredWidth: 300
}
}
效果和日志打印和前面一模一样,更进一步说明对于Text
来说,当其在布局中时,width
属性是被忽略的。这时我们参考前面示例2中的逻辑,可以使用implicitWidth
来设置吗?
RowLayout {
width: 600
height: 150
spacing: 0
Text {
text: qsTr("text")
font.pixelSize: 30
height: parent.height
//为了让字体居中显示
horizontalAlignment: Text.AlignHCenter
onWidthChanged: console.log("width:", width)
onImplicitWidthChanged: console.log("ImplicitWidth", implicitWidth)
// width: 200
implicitWidth: 200
// Layout.fillWidth: true
// Layout.preferredWidth: 300
}
}
上面代码会直接报错:qrc:/qt/qml/content/App.qml:31:32: Invalid property assignment: "implicitWidth" is a read-only property
,可以发现Text
的implicitWidth
属性是只读的,也就是说该属性的值是由其内部管理。
那如果想让在布局中设置Text
宽度该如何实现呢?就需要Layout
属性了:
RowLayout {
width: 600
height: 150
spacing: 0
Text {
text: qsTr("text")
font.pixelSize: 30
height: parent.height
//为了让字体居中显示
horizontalAlignment: Text.AlignHCenter
onWidthChanged: console.log("width:", width)
onImplicitWidthChanged: console.log("ImplicitWidth", implicitWidth)
// width: 200
// implicitWidth: 200
// Layout.fillWidth: true
Layout.preferredWidth: 300
}
}
效果图:
日志打印:
qml: width: 54
qml: ImplicitWidth 54
qml: ImplicitWidth 54
qml: width: 300
可以发现这时的Text
宽度终于符合预期了。这里使用Layout.preferredWidth
属性,简单说明:该属性用于设置处于布局中的组件的首选宽度,如果设置为-1,则布局会使用implicitWidth
属性,默认值就是-1。
这里可以做个总结:当组件在布局中时,优先级来看,Layout.preferredWidth > implicitWidth > width
。
深入理解
问题1:为什么需要implicitWidth
属性?直接使用width
不可以吗?
简单说明implicitWidth
属性的使用后,我们来思考一下该属性有什么用,帮助文档是这样说的:对于定义基于其内容具有首选大小的组件,设置隐式大小非常有用。
简单理解就是implicitWidth
它反应了组件的自然宽度,对于布局管理器来说,需要依赖该属性。更通俗的理解:width
和height
反映了组件的实际尺寸,而隐式尺寸则是组件固有的属性。
啥是自然尺寸?
比如对于Image
组件来说,其自然尺寸就是将其像素点一对一地显示在屏幕上,但是由于我们可以缩放Image
,这里缩放后地大小就是真实尺寸,这样侧面说明Image
的自然尺寸不能被修改。
举个简单例子,假如现在需要开发一个相册,我们不知道图片的尺寸,同时我们不想缩小图片,但是在必要时可以放大图片,这时就需要Image
的自然尺寸了,
Image {
width: Math.max(150, implicitWidth)
height: Math.max(150, implicitHeight)
}
在这个例子中,如果图片原来大小为300x300
,则会为300x300
,而小于150x150
的会放大。
自然尺寸在布局的作用?
在自定义组件中,如果定义组件大小有多个选择,其中一个方式就是基于父节点:
Item {
id: root
Rectangle {
width: root.width * 0.2
height: root.height * 0.2
color: 'red'
}
Rectangle {
x: 0.2 * root.width
y: 0.2 * root.height
width: root.width * 0.8
height: root.height * 0.8
color: 'green'
}
}
在这种情况下,只要给组件设置了宽高,它都可以正常运行,这种就是没有自然尺寸的情况,组件的大小完全取决于父控件设置的宽高。
那如果完全不需要自然尺寸,会有什么问题呢?假如我们尝试在类似于布局的组件上设置宽度属性,那么该组件会根据子项的宽度而不是隐式宽度来计算其自身宽度,但是子项又受父节点的影响,这样最终会形成一个绑定循环。
设置组件的隐式大小就可以打破这种循环依赖的关系,这里也就解释了在前面的示例中,对于RowLayout
为什么会优先采用implicitWidth
,而不是width
。
问题2:有什么使用场景?
- 对于一个可重用的自定义组件来说,当需要设置跟节点尺寸时,使用
implicitWidth/implicitHeight
而不是width/height
可以更加方便的在布局中使用。 - 如果新建一个自定义组件时,其大小可以根据内容自行调整,则设置隐式宽高属性。
我们来看一下Qt帮助文档的例子,
// Label.qml
import QtQuick 2.0
Item {
property alias icon: image.source
property alias label: text.text
implicitWidth: text.implicitWidth + image.implicitWidth
implicitHeight: Math.max(text.implicitHeight, image.implicitHeight)
Image { id: image }
Text {
id: text
wrapMode: Text.Wrap
anchors.left: image.right; anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
这里自定义组件包含2个水平放置的Image
和Text
,当我们设置icon
和label
属性时,该自定义控件就有了隐式尺寸,基控件的自然大小,这时放入到布局中,也可以正常显示了。