如何在 Qt/QML 中实现主明细视图(第 2 部分)
How to implement Master Details View in Qt/QML (part 2)
我之前问过如何在 Qt/QML 中实现主详细信息视图:。
继续努力,我得出了以下 QML 布局模型:
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.4
Item {
y: 50
Layout.fillHeight: true
width: appWindow.width
RowLayout {
id: mainLayout
anchors.fill: parent
ListModel {
id: navigation
ListElement {
item: "Item 1"
}
ListElement {
item: "Item 2"
}
ListElement {
item: "Item 3"
}
ListElement {
item: "Item 4"
}
ListElement {
item: "Item 5"
}
ListElement {
item: "Item 6"
}
ListElement {
item: "Item 7"
}
ListElement {
item: "Item 8"
}
ListElement {
item: "Item 9"
}
ListElement {
item: "Item 10"
}
ListElement {
item: "Item 11"
}
}
ScrollView{
Layout.fillHeight: true
verticalScrollBarPolicy: Qt.ScrollBarAlwaysOn
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
ListView {
id: listview
Layout.fillHeight: true
Layout.preferredWidth: 300
contentWidth: 300
model: navigation
delegate: Rectangle {
id: wrapper
width: 300
height: 50
Text {
id: itemInfo
text: item
color: "red"
}
}
}
}
Rectangle {
x: 300
y: 50
Layout.preferredWidth: appWindow.width - listview.width-4
height: appWindow.height - 50
color: "green"
border.width: 1
}
}
}
主视图本质上是一个包含多个项目的 ListView(每个项目代表一个可选择的元素,这将触发细节视图的更新,当前由绿色矩形表示(见下面的截图)
目前我仍然有以下几点问题:
如何修改Layout,让ListView覆盖整个屏幕高度?
当我 "scroll" 通过 ListView 时,我是否注意到很多屏幕闪烁?我怎样才能最大限度地减少这种情况?
如何防止显示整个上方状态栏(显示电池充电等设备系统信息的地方)?
编辑: 修改代码,在ScrollView中添加ListView。在这种情况下,ScrollView 的高度与屏幕高度相同,这也是我想要的(在顶部减去 50 的偏移量,见下图)。我认为 ListView 的行为符合预期,并没有比它的项目占用更多 space。
现在需要实现的是改变SrollView的Background颜色,使其与ListView的颜色匹配。在那种情况下,它会看起来好像 ListView 占据了整个 space。
为了让你的ListView占满所有的高度,你可以简单的设置为填满布局的高度。但是,请确保 Layout(及其在您的情况下的父级)也具有正确的大小。对于 QML 中的 Item
,大小默认为 (0,0)。
ListView {
id: listview
//...
Layout.fillHeight: true
//...
}
关于"flickering",你可以试试增加ListView cacheBuffer
属性,它对应内容的高度,以像素为单位,是预加载的。但是,如果这是 really flickering,您可能无能为力。
当屏幕刷新率错误的刷新显示时会出现闪烁,通常通过使用多个缓冲区 and/or 同步来解决。 QtQuick 隐藏了所有这些复杂性并使用 OpenGL 进行渲染,但我(还)没有看到 Android 最近的 Qt 版本有任何闪烁。
- 您可以通过编辑 Android 清单文件来删除状态栏,如 in this other post 所述,或者更糟的情况是通过 JNI。
要隐藏状态栏,最简单的方法就是指定一个主题并应用到manifest文件中。其他解决方案需要修改 activity 等。
在 yourApp/android/res/values
中创建具有以下内容的 theme.xml
:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
</style>
</resources>
然后在清单中,在添加屏幕方向的同一行中,添加主题:
android:theme="@style/AppTheme"
并且在您的主要 window 中使用 Window.FullScreen
可见性而不是最大化。
对于布局,看来您可以做的更少。 Layout
没有任何问题,只是 IMO 它更适合标准的可扩展 "micro" GUI 元素,如按钮等,而不是自定义宏元素。这是一个简洁但实用的示例:
Row {
anchors.fill: parent
ListView {
id: lv
width: 200
height: parent.height
model: 30
delegate: Rectangle {
width: 200
height: 50
color: index == lv.currentIndex ? "lightgray" : "white"
Text {
text: "item " + index
color: "red"
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
onClicked: lv.currentIndex = index
}
}
Rectangle {
anchors.right: parent.right
width: 5
height: parent.height * parent.visibleArea.heightRatio
color: "grey"
y: parent.height * parent.visibleArea.yPosition
}
}
Rectangle {
width: parent.width - lv.width
height: parent.height
color: "green"
Text {
anchors.centerIn: parent
text: "selected item n" + lv.currentIndex
color: "white"
font.pointSize: 15
}
}
}
结果:
虽然垂直偏移的原因并不完全清楚,但如果您想在顶部放置空闲 space,只需不要用根 Row
填充整个父级元素,而是相应地调整它的大小。
我有点懵,怎么会,你认为ScrollView
是需要的。
我从你的示例中删除了它,向 ListView
添加了剪辑,我就完成了。
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
ApplicationWindow
{
id: appWindow
width: 1024
height: 800
visible: true
Item {
y: 50
Layout.fillHeight: true
width: appWindow.width
RowLayout {
id: mainLayout
anchors.fill: parent
ListModel {
id: navigation
ListElement { item: "Item 1" }
Component.onCompleted: {
for (var i = 2; i < 50; i++) append({ item: 'Item' + i })
}
}
ListView {
id: listview
Layout.fillHeight: true
Layout.preferredWidth: 300
contentWidth: 300
model: navigation
clip: true //<--- Add this, so there won't be no elements outside the ListView-area
delegate: Rectangle {
id: wrapper
width: 300
height: 50
Text {
id: itemInfo
text: item
color: "red"
}
}
}
Rectangle {
x: 300
y: 50
Layout.preferredWidth: appWindow.width - listview.width-4
height: appWindow.height - 50
color: "green"
border.width: 1
}
}
}
}
有几件事你可能会误解:
ListView
没有提供任何背景。如果你想要这样,你需要在它后面画一些东西,例如一个 Rectangle
ListView
本身不提供 ScrollBar
。您需要像这样添加它们:
ScrollBar.vertical: ScrollBar { }
ScrollBar
没有 native 风格。句柄默认会消失。您可以在这里找到多个关于如何设置 ScrollBar
.
样式的问题
- 如果不剪裁
ListView
,您会看到一些元素突出 ListView
并突然消失。如果你没有任何内容可以涵盖这一点,你应该设置 clip: true
我之前问过如何在 Qt/QML 中实现主详细信息视图:
继续努力,我得出了以下 QML 布局模型:
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.4
Item {
y: 50
Layout.fillHeight: true
width: appWindow.width
RowLayout {
id: mainLayout
anchors.fill: parent
ListModel {
id: navigation
ListElement {
item: "Item 1"
}
ListElement {
item: "Item 2"
}
ListElement {
item: "Item 3"
}
ListElement {
item: "Item 4"
}
ListElement {
item: "Item 5"
}
ListElement {
item: "Item 6"
}
ListElement {
item: "Item 7"
}
ListElement {
item: "Item 8"
}
ListElement {
item: "Item 9"
}
ListElement {
item: "Item 10"
}
ListElement {
item: "Item 11"
}
}
ScrollView{
Layout.fillHeight: true
verticalScrollBarPolicy: Qt.ScrollBarAlwaysOn
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
ListView {
id: listview
Layout.fillHeight: true
Layout.preferredWidth: 300
contentWidth: 300
model: navigation
delegate: Rectangle {
id: wrapper
width: 300
height: 50
Text {
id: itemInfo
text: item
color: "red"
}
}
}
}
Rectangle {
x: 300
y: 50
Layout.preferredWidth: appWindow.width - listview.width-4
height: appWindow.height - 50
color: "green"
border.width: 1
}
}
}
主视图本质上是一个包含多个项目的 ListView(每个项目代表一个可选择的元素,这将触发细节视图的更新,当前由绿色矩形表示(见下面的截图)
目前我仍然有以下几点问题:
如何修改Layout,让ListView覆盖整个屏幕高度?
当我 "scroll" 通过 ListView 时,我是否注意到很多屏幕闪烁?我怎样才能最大限度地减少这种情况?
如何防止显示整个上方状态栏(显示电池充电等设备系统信息的地方)?
编辑: 修改代码,在ScrollView中添加ListView。在这种情况下,ScrollView 的高度与屏幕高度相同,这也是我想要的(在顶部减去 50 的偏移量,见下图)。我认为 ListView 的行为符合预期,并没有比它的项目占用更多 space。
现在需要实现的是改变SrollView的Background颜色,使其与ListView的颜色匹配。在那种情况下,它会看起来好像 ListView 占据了整个 space。
为了让你的ListView占满所有的高度,你可以简单的设置为填满布局的高度。但是,请确保 Layout(及其在您的情况下的父级)也具有正确的大小。对于 QML 中的
Item
,大小默认为 (0,0)。ListView { id: listview //... Layout.fillHeight: true //... }
关于"flickering",你可以试试增加ListView
cacheBuffer
属性,它对应内容的高度,以像素为单位,是预加载的。但是,如果这是 really flickering,您可能无能为力。
当屏幕刷新率错误的刷新显示时会出现闪烁,通常通过使用多个缓冲区 and/or 同步来解决。 QtQuick 隐藏了所有这些复杂性并使用 OpenGL 进行渲染,但我(还)没有看到 Android 最近的 Qt 版本有任何闪烁。
- 您可以通过编辑 Android 清单文件来删除状态栏,如 in this other post 所述,或者更糟的情况是通过 JNI。
要隐藏状态栏,最简单的方法就是指定一个主题并应用到manifest文件中。其他解决方案需要修改 activity 等。
在 yourApp/android/res/values
中创建具有以下内容的 theme.xml
:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
</style>
</resources>
然后在清单中,在添加屏幕方向的同一行中,添加主题:
android:theme="@style/AppTheme"
并且在您的主要 window 中使用 Window.FullScreen
可见性而不是最大化。
对于布局,看来您可以做的更少。 Layout
没有任何问题,只是 IMO 它更适合标准的可扩展 "micro" GUI 元素,如按钮等,而不是自定义宏元素。这是一个简洁但实用的示例:
Row {
anchors.fill: parent
ListView {
id: lv
width: 200
height: parent.height
model: 30
delegate: Rectangle {
width: 200
height: 50
color: index == lv.currentIndex ? "lightgray" : "white"
Text {
text: "item " + index
color: "red"
anchors.centerIn: parent
}
MouseArea {
anchors.fill: parent
onClicked: lv.currentIndex = index
}
}
Rectangle {
anchors.right: parent.right
width: 5
height: parent.height * parent.visibleArea.heightRatio
color: "grey"
y: parent.height * parent.visibleArea.yPosition
}
}
Rectangle {
width: parent.width - lv.width
height: parent.height
color: "green"
Text {
anchors.centerIn: parent
text: "selected item n" + lv.currentIndex
color: "white"
font.pointSize: 15
}
}
}
结果:
虽然垂直偏移的原因并不完全清楚,但如果您想在顶部放置空闲 space,只需不要用根 Row
填充整个父级元素,而是相应地调整它的大小。
我有点懵,怎么会,你认为ScrollView
是需要的。
我从你的示例中删除了它,向 ListView
添加了剪辑,我就完成了。
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
ApplicationWindow
{
id: appWindow
width: 1024
height: 800
visible: true
Item {
y: 50
Layout.fillHeight: true
width: appWindow.width
RowLayout {
id: mainLayout
anchors.fill: parent
ListModel {
id: navigation
ListElement { item: "Item 1" }
Component.onCompleted: {
for (var i = 2; i < 50; i++) append({ item: 'Item' + i })
}
}
ListView {
id: listview
Layout.fillHeight: true
Layout.preferredWidth: 300
contentWidth: 300
model: navigation
clip: true //<--- Add this, so there won't be no elements outside the ListView-area
delegate: Rectangle {
id: wrapper
width: 300
height: 50
Text {
id: itemInfo
text: item
color: "red"
}
}
}
Rectangle {
x: 300
y: 50
Layout.preferredWidth: appWindow.width - listview.width-4
height: appWindow.height - 50
color: "green"
border.width: 1
}
}
}
}
有几件事你可能会误解:
ListView
没有提供任何背景。如果你想要这样,你需要在它后面画一些东西,例如一个Rectangle
ListView
本身不提供ScrollBar
。您需要像这样添加它们:ScrollBar.vertical: ScrollBar { }
ScrollBar
没有 native 风格。句柄默认会消失。您可以在这里找到多个关于如何设置ScrollBar
. 样式的问题
- 如果不剪裁
ListView
,您会看到一些元素突出ListView
并突然消失。如果你没有任何内容可以涵盖这一点,你应该设置clip: true