Qt Quick Demo - RSS-Nachrichten
Ein QML-RSS-Newsreader, der die benutzerdefinierten QML-Typen XmlListModel und XmlListModelRole verwendet, um XML-Daten herunterzuladen, ListModel und ListElement, um eine Kategorieliste zu erstellen, und ListView, um die Daten anzuzeigen.
RSS News demonstriert die folgenden Qt Quick Funktionen:
- Verwendung benutzerdefinierter QML-Typen.
- Verwendung von Listenmodellen und Listenelementen zur Darstellung von Daten.
- Verwendung von XML-Listenmodellen zum Herunterladen von XML-Daten.
- Verwendung von Listenansichten zur Anzeige von Daten.
- Verwendung des Typs Component zur Erstellung einer Fußzeile für die Listenansicht der Nachrichten.
- Verwendung des Typs Image, um eine Schaltfläche zum Schließen der Anwendung zu erstellen.
Ausführen des Beispiels
Zum Ausführen des Beispiels von Qt Creatorauszuführen, öffnen Sie den Modus Welcome und wählen Sie das Beispiel aus Examples aus. Weitere Informationen finden Sie unter Erstellen und Ausführen eines Beispiels.
Verwenden von benutzerdefinierten Typen
In der RSS-News-App verwenden wir die folgenden benutzerdefinierten Typen, die jeweils in einer separaten .qml-Datei definiert sind:
BusyIndicator.qml
CategoryDelegate.qml
NewsDelegate.qml
RssFeeds.qml
ScrollBar.qml
Erstellen des Hauptfensters
In Main.qml
verwenden wir einen Typ Rectangle mit benutzerdefinierten Eigenschaften, um das Hauptfenster der App zu erstellen:
Rectangle { id: window width: 800 height: 480 property string currentFeed: rssFeeds.get(0).feed property bool loading: feedModel.status === XmlListModel.Loading property bool isPortrait: Screen.primaryOrientation === Qt.PortraitOrientation
Wir werden die benutzerdefinierten Eigenschaften später zum Laden von XML-Daten und zum Anpassen des Bildschirmlayouts in Abhängigkeit von seiner Ausrichtung verwenden.
Erstellen einer Kategorieliste
In Main.qml
verwenden wir den benutzerdefinierten Typ RssFeeds
, den wir in RssFeeds.qml
angeben, um eine Liste von Feed-Kategorien zu erstellen:
RssFeeds { id: rssFeeds }
In RssFeeds.qml
verwenden wir einen ListModel Typ mit einem ListElement Typ, um eine Kategorieliste zu erstellen, in der Listenelemente Feed-Kategorien darstellen:
ListModel { ListElement { name: "Top Stories"; feed: "news.yahoo.com/rss/topstories"; image: "images/TopStories.jpg" } ListElement { name: "World"; feed: "news.yahoo.com/rss/world"; image: "images/World.jpg" } ListElement { name: "Europe"; feed: "news.yahoo.com/rss/europe"; image: "images/Europe.jpg" } ListElement { name: "Asia"; feed: "news.yahoo.com/rss/asia"; image: "images/Asia.jpg" } ListElement { name: "U.S. National"; feed: "news.yahoo.com/rss/us"; image: "images/USNational.jpg" } ListElement { name: "Politics"; feed: "news.yahoo.com/rss/politics"; image: "images/Politics.jpg" } ListElement { name: "Business"; feed: "news.yahoo.com/rss/business"; image: "images/Business.jpg" } ListElement { name: "Technology"; feed: "news.yahoo.com/rss/tech"; image: "images/Technology.jpg" } ListElement { name: "Entertainment"; feed: "news.yahoo.com/rss/entertainment"; image: "images/Entertainment.jpg" } ListElement { name: "Health"; feed: "news.yahoo.com/rss/health"; image: "images/Health.jpg" } ListElement { name: "Science"; feed: "news.yahoo.com/rss/science"; image: "images/Science.jpg" } ListElement { name: "Sports"; feed: "news.yahoo.com/rss/sports"; image: "images/Sports.jpg" } }
Listenelemente werden wie andere QML-Typen definiert, mit der Ausnahme, dass sie eine Sammlung von Rollendefinitionen anstelle von Eigenschaften enthalten. Rollen legen fest, wie auf die Daten zugegriffen wird, und enthalten die Daten selbst.
Für jedes Listenelement verwenden wir die Rolle name
, um den Kategorienamen anzugeben, die Rolle feed
, um die URL anzugeben, von der die Daten geladen werden, und die Rolle image
, um ein Bild für die Kategorie anzuzeigen.
In Main.qml
verwenden wir einen ListView Typ, um die Kategorieliste anzuzeigen:
ListView { id: categories property int itemWidth: 190 width: window.isPortrait ? parent.width : itemWidth height: window.isPortrait ? itemWidth : parent.height orientation: window.isPortrait ? ListView.Horizontal : ListView.Vertical anchors.top: parent.top model: rssFeeds delegate: CategoryDelegate { itemSize: categories.itemWidth isLoading: window.loading onClicked: function () { if (window.currentFeed == feed) feedModel.reload() else window.currentFeed = feed } }
Um die Kategorieliste im Hochformat horizontal am oberen Rand des Fensters und im Querformat vertikal auf der linken Seite anzuordnen, verwenden wir die Eigenschaft orientation
. Je nach Ausrichtung binden wir entweder die Breite oder die Höhe der Liste an einen festen Wert (itemWidth
).
Mit der Eigenschaft anchors.top
positionieren wir die Listenansicht in beiden Ausrichtungen am oberen Rand des Bildschirms.
Wir verwenden die Eigenschaft model
, um XML-Daten aus dem Modell rssFeeds
zu laden, und CategoryDelegate
als Delegat, um jedes Element in der Liste zu instanziieren.
Erstellen von Listenelementen
In CategoryDelegate.qml
verwenden wir den Typ Rectangle mit benutzerdefinierten Eigenschaften, um Listenelemente zu erstellen:
Rectangle { id: delegate required property real itemSize required property string name required property string feed required property string image required property int index required property bool isLoading property bool selected: ListView.isCurrentItem
Wir verwenden required
, um die Eigenschaften zu deklarieren, die wir vom Modell erwarten.
Wir setzen die Eigenschaft selected
auf die angehängte Eigenschaft ListView.isCurrentItem
, um anzugeben, dass selected
true
ist, wenn delegate
das aktuelle Element ist.
Wir verwenden die Eigenschaft Image type source
, um das Bild anzuzeigen, das im Delegaten zentriert ist und für das Listenelement durch die Rolle image
im Listenmodell rssFeeds
festgelegt wurde:
Image { anchors.centerIn: parent source: delegate.image }
Wir verwenden einen Text Typ, um den Listenelementen Titel hinzuzufügen:
Text { id: titleText anchors { left: parent.left; leftMargin: 20 right: parent.right; rightMargin: 20 top: parent.top; topMargin: 20 } font { pixelSize: 18; bold: true } text: delegate.name color: delegate.selected ? "#ffffff" : "#ebebdd" scale: delegate.selected ? 1.15 : 1.0 Behavior on color { ColorAnimation { duration: 150 } } Behavior on scale { PropertyAnimation { duration: 300 } }
Wir verwenden die Eigenschaft anchors
, um den Titel am oberen Rand des Listenelements mit einem 20-Pixel-Rand zu positionieren. Wir verwenden die Eigenschaften font
, um die Schriftgröße und die Textformatierung anzupassen.
Die Eigenschaften color
und scale
werden verwendet, um den Text aufzuhellen und etwas größer zu skalieren, wenn das Listenelement das aktuelle Element ist. Durch die Anwendung einer Behavior auf die Eigenschaft werden die Aktionen zum Auswählen und Aufheben der Auswahl von Listenelementen animiert.
Wir verwenden einen MouseArea -Typ, um XML-Daten herunterzuladen, wenn Benutzer auf ein Element der Kategorieliste tippen:
MouseArea { anchors.fill: delegate onClicked: { delegate.ListView.view.currentIndex = delegate.index delegate.clicked() } }
Die Eigenschaft anchors.fill
wird auf delegate
gesetzt, damit die Benutzer auf eine beliebige Stelle innerhalb des Listenelements tippen können.
Um die XML-Daten für die Kategorieliste bei einem clicked
-Ereignis zu laden, setzen wir die currentIndex
der ListView
angehängten Eigenschaft und geben das clicked
-Signal() in onClicked
aus.
Herunterladen von XML-Daten
In Main.qml
verwenden wir einen XmlListModel Typ als Datenquelle für ListView Elemente, um Nachrichten in der ausgewählten Kategorie anzuzeigen:
XmlListModel { id: feedModel source: "https://" + window.currentFeed query: "/rss/channel/item"
Wir verwenden die Eigenschaft source
und die benutzerdefinierte Eigenschaft window.currentFeed
, um Nachrichten für die ausgewählte Kategorie abzurufen.
Die Eigenschaft query
gibt an, dass XmlListModel ein Modellelement für jedes <item>
im XML-Dokument erzeugt.
Wir verwenden den Typ XmlListModelRole, um die Attribute der Modellelemente anzugeben. Jedes Modellelement hat die Attribute title
, content
, link
und pubDate
, die mit den Werten der entsprechenden <item>
im XML-Dokument übereinstimmen:
XmlListModelRole { name: "title"; elementName: "title"; attributeName: ""} XmlListModelRole { name: "content"; elementName: "content"; attributeName: "url" } XmlListModelRole { name: "link"; elementName: "link"; attributeName: "" } XmlListModelRole { name: "pubDate"; elementName: "pubDate"; attributeName: "" } }
Wir verwenden das Modell feedModel
in einem Typ ListView zur Anzeige der Daten:
ListView { id: list anchors.left: window.isPortrait ? window.left : categories.right anchors.right: closeButton.left anchors.top: window.isPortrait ? categories.bottom : window.top anchors.bottom: window.bottom anchors.leftMargin: 30 anchors.rightMargin: 4 clip: window.isPortrait model: feedModel footer: footerText delegate: NewsDelegate {} }
Um die Nachrichten unterhalb der Kategorieliste im Hochformat und rechts davon im Querformat aufzulisten, verwenden wir die benutzerdefinierte Eigenschaft isPortrait
, um den oberen Teil der Nachrichtenliste links von window
und unten von categories
im Hochformat und rechts von categories
und unten von window
im Querformat zu verankern.
Wir verwenden die Eigenschaft anchors.bottom
, um den unteren Rand der Listenansicht in beiden Ausrichtungen am unteren Rand des Fensters zu verankern.
Im Hochformat wird das Bild der Nachrichten an das Begrenzungsrechteck der Listenansicht geklammert, um grafische Artefakte zu vermeiden, wenn Nachrichten über andere Elemente gescrollt werden. Im Querformat ist dies nicht erforderlich, da sich die Liste vertikal über den gesamten Bildschirm erstreckt.
Wir verwenden die Eigenschaft model
, um XML-Daten aus dem Modell feedModel
zu laden, und verwenden NewsDelegate
als Delegat, um jedes Element in der Liste zu instanziieren.
In NewsDelegate.qml
verwenden wir einen Column Typ, um die XML-Daten anzuordnen:
Column { id: delegate required property string title required property string content required property string link required property string pubDate width: delegate.ListView.view.width spacing: 8
Innerhalb der Spalte verwenden wir eine Zeile und eine weitere Spalte, um Bilder und Titeltext zu positionieren:
Row { width: parent.width spacing: 8 Column { Item { width: 4 height: titleText.font.pixelSize / 4 } Image { id: titleImage source: delegate.content width: Math.min(delegate.width / 2, sourceSize.width) fillMode: Image.PreserveAspectFit } } Text { id: titleText text: delegate.title.replace(/'/g, "'") width: delegate.width - titleImage.width wrapMode: Text.WordWrap font.pixelSize: 26 font.bold: true } }
Mit der JavaScript-Funktion timeSinceEvent()
erzeugen wir eine Textdarstellung, die angibt, wie lange es her ist, dass der Eintrag veröffentlicht wurde:
Text { width: delegate.width font.pixelSize: 12 textFormat: Text.RichText font.italic: true text: delegate.timeSinceEvent(delegate.pubDate) + " (<a href=\"" + delegate.link + "\">Link</a>)" onLinkActivated: function(link) { Qt.openUrlExternally(link) } }
Wir verwenden den onLinkActivated
signal handler, um die URL in einem externen Browser zu öffnen, wenn Benutzer den Link auswählen.
Rückmeldung an die Benutzer
In CategoryDelegate.qml
verwenden wir den benutzerdefinierten Typ BusyIndicator
, um die Aktivität beim Laden der XML-Daten anzuzeigen:
BusyIndicator { scale: 0.8 visible: delegate.ListView.isCurrentItem && delegate.isLoading anchors.centerIn: parent }
Wir verwenden die Eigenschaft scale
, um die Größe des Indikators auf 0.8
zu reduzieren. Wir binden die Eigenschaft visible
an die angehängte Eigenschaft isCurrentItem
der Listenansicht delegate
und die Eigenschaft loading
des Hauptfensters, um das Indikatorbild anzuzeigen, wenn ein Element der Kategorieliste das aktuelle Element ist und die XML-Daten geladen werden.
Wir definieren den Typ BusyIndicator
in BusyIndicator.qml
. Wir verwenden den Typ Image, um ein Bild anzuzeigen, und wenden NumberAnimation auf seine Eigenschaft rotation
an, um das Bild in einer Endlosschleife zu drehen:
Image { id: container source: "images/busy.png"; NumberAnimation on rotation { running: container.visible from: 0; to: 360; loops: Animation.Infinite; duration: 1200 } }
In Ihren Anwendungen können Sie auch den Typ BusyIndicator aus dem Qt Quick Controls Modul verwenden.
Erstellen von Bildlaufleisten
In Main.qml
verwenden wir unseren eigenen benutzerdefinierten Typ ScrollBar
, um Bildlaufleisten in den Listenansichten der Kategorien und Nachrichten zu erstellen. In Ihren Anwendungen können Sie auch den Typ ScrollView aus dem Qt Quick Controls Modul verwenden.
Zunächst erstellen wir eine Bildlaufleiste in der Kategorie-Listenansicht. Wir binden die Eigenschaft orientation
an die Eigenschaft isPortrait
und an den Wert Horizontal
des Enum-Typs Qt::Orientation
, um eine horizontale Bildlaufleiste im Hochformat anzuzeigen, und an den Wert Vertical
, um eine vertikale Bildlaufleiste im Querformat anzuzeigen:
ScrollBar { id: listScrollBar orientation: window.isPortrait ? Qt.Horizontal : Qt.Vertical height: window.isPortrait ? 8 : categories.height; width: window.isPortrait ? categories.width : 8 scrollArea: categories; anchors.right: categories.right }
Wie bei der Listenansicht categories
passen wir die Breite und Höhe der Bildlaufleiste anhand der Eigenschaft isPortrait
an.
Wir verwenden die Eigenschaft scrollArea
, um die Bildlaufleiste in der Listenansicht categories
anzuzeigen.
Mit der Eigenschaft anchors.right
verankern wir die Bildlaufleiste an der rechten Seite der Kategorieliste.
ScrollBar { scrollArea: list width: 8 anchors.right: window.right anchors.top: window.isPortrait ? categories.bottom : window.top anchors.bottom: window.bottom }
Zweitens erstellen wir eine weitere Bildlaufleiste in der Listenansicht der Nachrichten. Wir möchten, dass eine vertikale Bildlaufleiste unabhängig von der Bildschirmausrichtung auf der rechten Seite der Ansicht angezeigt wird. Daher können wir die Eigenschaft width
auf 8
setzen und die Eigenschaft anchors.right
an die Eigenschaft window.right
binden. Wir verwenden die Eigenschaft anchors.top
, um die Bildlaufleiste im Hochformat am unteren Rand der Kategorieliste und im Querformat am oberen Rand der Nachrichtenliste zu verankern. Wir verwenden die Eigenschaft anchors.bottom
, um die Bildlaufleiste in beiden Ausrichtungen am unteren Rand der Listenansicht zu verankern.
Wir definieren den Typ ScrollBar
in ScrollBar.qml
. Wir verwenden einen Typ Item mit benutzerdefinierten Eigenschaften, um einen Container für die Bildlaufleiste zu erstellen:
Item { id: container property variant scrollArea property int orientation: Qt.Vertical opacity: 0
Wir verwenden einen BorderImage Typ, um den Daumen der Bildlaufleiste an der x- und y-Position anzuzeigen, die wir mit der Funktion position()
berechnen:
BorderImage { source: "images/scrollbar.png" border { left: 1; right: 1; top: 1; bottom: 1 } x: container.orientation == Qt.Vertical ? 2 : container.position() y: container.orientation == Qt.Vertical ? container.position() : 2 width: container.orientation == Qt.Vertical ? container.width - 4 : container.size() height: container.orientation == Qt.Vertical ? container.size() : container.height - 4 }
Wir verwenden die Funktion size
, um die Breite und Höhe des Daumens in Abhängigkeit von der Bildschirmausrichtung zu berechnen.
Wir verwenden states
, um die Bildlaufleiste sichtbar zu machen, wenn der Benutzer den Bildlaufbereich bewegt:
states: State { name: "visible" when: container.orientation == Qt.Vertical ? container.scrollArea.movingVertically : container.scrollArea.movingHorizontally PropertyChanges { container { opacity: 1.0 } } }
Wir verwenden transitions
, um NumberAnimation auf die Eigenschaft opacity
anzuwenden, wenn der Status von "visible"
in den Standardstatus wechselt:
transitions: Transition { from: "visible"; to: "" NumberAnimation { properties: "opacity"; duration: 600 } } }
Fußzeilen erstellen
In Main.qml
verwenden wir einen Component Typ mit einem Rectangle Typ, um eine Fußzeile für die News-Listenansicht zu erstellen:
pragma ComponentBehavior: Bound import QtQuick import QtQuick.Window import QtQml.XmlListModel Rectangle { id: window width: 800 height: 480 property string currentFeed: rssFeeds.get(0).feed property bool loading: feedModel.status === XmlListModel.Loading property bool isPortrait: Screen.primaryOrientation === Qt.PortraitOrientation onLoadingChanged: { if (feedModel.status == XmlListModel.Ready) list.positionViewAtBeginning() } RssFeeds { id: rssFeeds } XmlListModel { id: feedModel source: "https://" + window.currentFeed query: "/rss/channel/item" XmlListModelRole { name: "title"; elementName: "title"; attributeName: ""}
Wir binden die width
der Fußzeile an die Breite der Komponente und die height
an die Höhe der Schaltfläche Schließen, um sie auszurichten, wenn keine Nachrichten angezeigt werden.
Erstellen von Schaltflächen
In Main.qml
verwenden wir einen Image Typ, um eine einfache Drucktaste zu erstellen, auf die Benutzer tippen können, um die App zu schließen:
XmlListModelRole { name: "content"; elementName: "content"; attributeName: "url" } XmlListModelRole { name: "link"; elementName: "link"; attributeName: "" } XmlListModelRole { name: "pubDate"; elementName: "pubDate"; attributeName: "" } } ListView { id: categories property int itemWidth: 190 width: window.isPortrait ? parent.width : itemWidth height: window.isPortrait ? itemWidth : parent.height orientation: window.isPortrait ? ListView.Horizontal : ListView.Vertical anchors.top: parent.top model: rssFeeds delegate: CategoryDelegate { itemSize: categories.itemWidth isLoading: window.loading onClicked: function () { if (window.currentFeed == feed) feedModel.reload() else window.currentFeed = feed } } spacing: 3 } ScrollBar { id: listScrollBar orientation: window.isPortrait ? Qt.Horizontal : Qt.Vertical height: window.isPortrait ? 8 : categories.height; width: window.isPortrait ? categories.width : 8 scrollArea: categories; anchors.right: categories.right } ListView { id: list anchors.left: window.isPortrait ? window.left : categories.right anchors.right: closeButton.left anchors.top: window.isPortrait ? categories.bottom : window.top anchors.bottom: window.bottom anchors.leftMargin: 30 anchors.rightMargin: 4 clip: window.isPortrait model: feedModel footer: footerText delegate: NewsDelegate {} } ScrollBar { scrollArea: list width: 8 anchors.right: window.right anchors.top: window.isPortrait ? categories.bottom : window.top anchors.bottom: window.bottom } Component { id: footerText Rectangle { width: parent.width height: closeButton.height color: "lightgray" Text { text: "RSS Feed from Yahoo News" anchors.centerIn: parent font.pixelSize: 14 } } } Image { id: closeButton source: "content/images/btn_close.png" scale: 0.8 anchors.top: parent.top anchors.right: parent.right anchors.margins: 4 opacity: (window.isPortrait && categories.moving) ? 0.2 : 1.0 Behavior on opacity { NumberAnimation { duration: 300; easing.type: Easing.OutSine } } MouseArea { anchors.fill: parent onClicked: { Qt.quit() } } }
Mit anchors
positionieren wir die Schließen-Schaltfläche in der oberen rechten Ecke der Nachrichtenlistenansicht mit einem Rand von 4 Pixeln. Da die Schließen-Schaltfläche die Kategorieliste im Hochformat überlappt, animieren wir die Eigenschaft opacity
, um die Schaltfläche fast vollständig transparent zu machen, wenn der Benutzer die Kategorieliste durchblättert.
Wir verwenden den onClicked
Signalhandler innerhalb eines MouseArea, um die quit()
Funktion aufzurufen, wenn der Benutzer die Schließen-Schaltfläche auswählt.
Siehe auch QML-Anwendungen.
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.