Auf dieser Seite

Qt Quick Demo – RSS-Nachrichten

Ein QML-RSS-Newsreader, der die benutzerdefinierten QML-Typen XmlListModel und XmlListModelRole zum Herunterladen von XML-Daten, ListModel und ListElement zum Erstellen einer Kategorieliste sowie ListView zur Anzeige der Daten verwendet.

RSS-Newsreader mit Kategorieseitenleiste und Schlagzeilen

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 “ zum Erstellen einer Fußzeile für die Listenansicht der Nachrichten.
  • Verwendung des Typs „ Image “ zum Erstellen einer Schaltfläche zum Schließen der App.

Ausführen des Beispiels

Sie können das Beispiel wie folgt ausführen:

Verwendung benutzerdefinierter 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 der Datei „ 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

Die benutzerdefinierten Eigenschaften werden später zum Laden von XML-Daten und zur Anpassung des Bildschirmlayouts je nach Ausrichtung verwendet.

Erstellen einer Kategorieliste

In der Datei „ Main.qml “ verwenden wir den benutzerdefinierten Typ „ RssFeeds “, den wir in der Datei „ RssFeeds.qml “ festlegen, um eine Liste der 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 die 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 dem Unterschied, dass sie anstelle von Eigenschaften eine Sammlung von Rollendefinitionen enthalten. Rollen definieren sowohl den Zugriff auf die Daten als auch 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 sollen, und die Rolle „ image “, um ein Bild für die Kategorie anzuzeigen.

In „ Main.qml “ verwenden wir den Typ „ ListView “, 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).

Wir verwenden die Eigenschaft „ anchors.top “, um die Listenansicht in beiden Ausrichtungen am oberen Bildschirmrand zu positionieren.

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 festzulegen, dass „ selected “ den Wert „ true “ annimmt, wenn „ delegate “ das aktuelle Element ist.

Wir verwenden die Eigenschaft „ source “ vom Typ „ Image “, um das Bild zentriert im Delegaten anzuzeigen, das für das Listenelement durch die Rolle „ image “ im Listenmodell „ rssFeeds “ festgelegt wurde:

    Image {
        anchors.centerIn: parent
        source: delegate.image
    }

Wir verwenden einen Typ vom Typ „ Text “, 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 oben im Listenelement mit einem Rand von 20 Pixeln zu positionieren. Mit den Eigenschaften „ font “ passen wir die Schriftgröße und die Textformatierung an.

Wir verwenden die Eigenschaften „ color “ und „ scale “, um den Text heller zu gestalten und ihn etwas zu vergrößern, wenn das Listenelement das aktuelle Element ist. Durch Anwenden eines „ Behavior “ auf die Eigenschaft animieren wir die Aktionen zum Auswählen und Abwählen von Listenelementen.

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 “ ist auf „ delegate “ gesetzt, damit Benutzer an einer beliebigen Stelle innerhalb des Listenelements tippen können.

Um die XML-Daten für die Kategorieliste bei einem „ clicked “-Ereignis zu laden, setzen wir die Eigenschaft „ currentIndex “ der angehängten Eigenschaft „ ListView “ und lösen das Signal „ clicked “ in „ onClicked “ aus.

Herunterladen von XML-Daten

In „ Main.qml “ verwenden wir einen Typ vom Typ „ XmlListModel “ als Datenquelle für „ ListView “-Elemente, um Nachrichtenartikel 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 Nachrichtenartikel für die ausgewählte Kategorie abzurufen.

Die Eigenschaft „ query “ legt fest, dass der Typ „ XmlListModel “ für jedes „ <item> “-Element im XML-Dokument ein Modellelement generiert.

Wir verwenden den Typ „ XmlListModelRole “, um die Attribute des Modellelements festzulegen. Jedes Modellelement verfügt über die Attribute „ title “, „ content “, „ link “ und „ pubDate “, die den Werten der entsprechenden „ <item> “ im XML-Dokument entsprechen:

        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: "" }
    }

Zur Darstellung der Daten verwenden wir das Modell „ feedModel “ im Typ „ ListView “:

    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 Rand der Nachrichtenliste im Hochformat links von „ window “ und am unteren Rand von „ categories “ sowie im Querformat rechts von „ categories “ und am unteren Rand von „ window “ 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 beschneiden wir die Darstellung der Nachrichtenartikel auf das Begrenzungsrechteck der Listenansicht, um grafische Artefakte zu vermeiden, wenn Nachrichtenartikel ü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 „ feedModel “-Modell zu laden, und nutzen „ NewsDelegate “ als Delegat, um jedes Element in der Liste zu instanziieren.

In ` NewsDelegate.qml` verwenden wir den Typ „ Column “, 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(/&#39;/g, "'")
            width: delegate.width - titleImage.width
            wrapMode: Text.WordWrap
            font.pixelSize: 26
            font.bold: true
        }
    }

Wir generieren mithilfe der JavaScript-Funktion „ timeSinceEvent() “ eine textuelle Darstellung, 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 Signal-Handler „ onLinkActivated “, um die URL in einem externen Browser zu öffnen, wenn Nutzer den Link auswählen.

Feedback an Benutzer geben

In „ CategoryDelegate.qml “ verwenden wir den benutzerdefinierten Typ „ BusyIndicator “, um die Aktivität während des Ladens 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 verringern. Wir binden die Eigenschaft „ visible “ an die angehängte Eigenschaft „ isCurrentItem “ der Listenansicht „ delegate “ sowie an die Eigenschaft „ loading “ des Hauptfensters, um das Indikatorbild anzuzeigen, wenn ein Element der Kategorieliste das aktuelle Element ist und XML-Daten geladen werden.

Wir definieren den Typ „ BusyIndicator “ in „ BusyIndicator.qml “. Wir verwenden einen „ Image “-Typ, um ein Bild anzuzeigen, und wenden ein „ NumberAnimation “ auf dessen „ rotation “-Eigenschaft 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 Apps können Sie auch den Typ „ BusyIndicator “ aus dem Qt Quick Controls Moduls 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 Apps können Sie ebenfalls den Typ „ ScrollView “ aus dem Qt Quick Controls Moduls verwenden.

Zunächst erstellen wir eine Bildlaufleiste in der Kategorie-Listenansicht. Wir binden die Eigenschaft „ orientation “ an die Eigenschaft „ isPortrait “ sowie 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
    }

Genau 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.

Wir verwenden die Eigenschaft „ anchors.right “, um die Bildlaufleiste am rechten Rand der Kategorieliste zu verankern.

    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 Nachrichtenlistenansicht. Wir möchten, dass unabhängig von der Bildschirmausrichtung eine vertikale Bildlaufleiste auf der rechten Seite der Ansicht erscheint. Daher setzen wir die Eigenschaft „ width “ auf „ 8 “ und binden die Eigenschaft „ anchors.right “ an die Eigenschaft „ window.right “. Wir verwenden die Eigenschaft „ anchors.top “, um den oberen Rand der Bildlaufleiste im Hochformat am unteren Rand der Kategorieliste und im Querformat am oberen Rand der Nachrichtenliste zu verankern. Mit der Eigenschaft „ anchors.bottom “ verankern wir den unteren Rand der Bildlaufleiste in beiden Ausrichtungen am unteren Rand der Listenansicht.

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 den Typ „ BorderImage “, um den Schieberegler an der x- und y-Position anzuzeigen, die wir mithilfe 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 Schiebereglers in Abhängigkeit von der Bildschirmausrichtung zu berechnen.

Wir verwenden ` states `, um die Bildlaufleiste sichtbar zu machen, wenn der Benutzer den Bildlaufbereich verschiebt:

    states: State {
        name: "visible"
        when: container.orientation == Qt.Vertical ?
                  container.scrollArea.movingVertically :
                  container.scrollArea.movingHorizontally
        PropertyChanges { container { opacity: 1.0 } }
    }

Wir verwenden „ transitions “, um ein „ NumberAnimation “ auf die Eigenschaft „ opacity “ anzuwenden, wenn sich der Zustand von „ "visible" “ in den Standardzustand ändert:

    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 Nachrichtenlistenansicht 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 den Typ ` Image `, um eine einfache Schaltfläche 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()
            }
        }
    }

Wir verwenden „ anchors “, um die Schaltfläche „Schließen“ in der oberen rechten Ecke der Nachrichtenlistenansicht mit Rändern von 4 Pixeln zu positionieren. Da die Schaltfläche „Schließen“ im Hochformat die Kategorieliste überlappt, animieren wir die Eigenschaft „ opacity “, um die Schaltfläche fast vollständig transparent zu machen, wenn Benutzer durch die Kategorieliste scrollen.

Wir verwenden den Signal-Handler „ onClicked “ innerhalb eines „ MouseArea “, um die Funktion „ quit() “ aufzurufen, wenn der Nutzer die Schaltfläche „Schließen“ auswählt.

Beispielprojekt @ code.qt.io

Siehe auch QML-Anwendungen.

© 2026 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.