问题
I want to create, in QML, a TV-schedule where the vertical axis is a list of Channels and the horizontal axis is time-based. For example something like
(source: zappware.com)
Initially, I created
- a vertical ListView with
- model = the list of Channels
- delegate = a horizontal ListView
- every horizontal ListView has
- model = the list of Events
- delegate = an Item where the width is proportional to the duration of the Event
So far so good. Only drawback is that the horizontal ListViews scroll one by one while they should scroll together.
So somehow, the contentX property of every horizontal ListView should be bound to the contentX property of the moving/flicking horizontal ListView. Note that this binding is dynamic: when flicking in the first row, all other rows should bind to the contentX of the first row. But this should be changed when flicking in the second row.
Any advice on how this can be done?
I tried a somewhat different approach by
- creating a Flickable Item on top of the vertical ListView (with contentWidth the complete time-window).
- binding every horizontal ListView to the contentX of this Flickable (this is a static binding)
This resulted in nice synchronous scrolling but I still have some issues
- I had to do some tricks to ensure that flicking is only horizontal or vertical but not both
- I'm not able anymore to click on individual Events; I guess events are intercepted by the Flickable
- I'm also not sure about the memory impact of such a Flickable with a huge contentWidth?
Feedback appreciated!
回答1:
I'd say have only one vertical list view for the channels. But the channel names only, not the actual programs. Instead of a horizontal view for the programs, you can cram them all together in a single flickable, using the begin time and duration to layout the programs in the flickable by binding their x and width properties to the former.
Then you can bind the channel list view together with the vertical scrolling of the program items, so that you have the programs corresponding to their appropriate channels. This way you can scroll vertically from both, and only scroll horizontally the programs.
Here is a quick example:
ApplicationWindow {
id: main
width: 500
height: 100
visible: true
color: "white"
ListModel {
id: modC
ListElement { name: "Ch1" }
ListElement { name: "Ch2" }
ListElement { name: "Ch3" }
}
ListModel {
id: modP1
ListElement { name: "p1"; start: 0; duration: 6 }
ListElement { name: "p2"; start: 6; duration: 6 }
ListElement { name: "p3"; start: 12; duration: 6 }
ListElement { name: "p4"; start: 18; duration: 6 }
}
ListModel {
id: modP2
ListElement { name: "p1"; start: 0; duration: 12 }
ListElement { name: "p2"; start: 12; duration: 12 }
}
ListModel {
id: modP3
ListElement { name: "p1"; start: 0; duration: 8 }
ListElement { name: "p2"; start: 8; duration: 8 }
ListElement { name: "p3"; start: 16; duration: 8 }
}
property var subMod : [ modP1, modP2, modP3 ]
Component {
id: progDelegate
Rectangle {
property var source
x: source.start * 50
width: source.duration * 50
height: 50
color: "lightblue"
border.color: "black"
Text {
text: source.name
}
}
}
Row {
anchors.fill: parent
ListView {
id: list
height: parent.height
width: 100
model: modC
delegate: Item {
width: 100
height: 50
Rectangle {
anchors.fill: parent
color: "red"
border.color: "black"
Text {
anchors.centerIn: parent
text: name
}
}
Component.onCompleted: {
var mod = subMod[index]
for (var i = 0; i < mod.count; ++i) progDelegate.createObject(flick.contentItem, {"source": mod.get(i), "y": index * 50})
}
}
}
Flickable {
id: flick
height: parent.height
width: parent.width - list.width
contentWidth: 1200
contentHeight: contentItem.childrenRect.height
clip: true
flickableDirection: Flickable.HorizontalFlick
contentY: list.contentY
}
}
}
来源:https://stackoverflow.com/questions/29700908/two-dimensional-table-with-nested-scrolling-areas-in-qml