2014-09-12 10 views
12

Ich werde verrückt. Ich habe eine ListView in einer ScrollView, verbunden mit einem Modell, das QAbstractListModel erbt. Wenn dem Modell Objekte hinzugefügt werden, zeigt die ListView diese mithilfe eines Delegaten an. So weit, ist es gut.QML ListView-Methode positionViewAtEnd() tut genau das Gegenteil

Aber ich möchte wirklich, dass die Ansicht nach unten gescrollt bleibt (wie ein Chat-Fenster), und es fällt mir sehr schwer, das zu ermöglichen. Hier ist der relevante QML-Code:

Rectangle { 
    ScrollView { 
     [anchor stuff] 

     ListView { 
      id: messageList 
      model: textMessageFiltered 
      delegate: messageDelegate 
     } 
    } 

    TextField { 
     id: messageEditor 
     [anchor stuff] 

     onAccepted: { 
      controller.sendTextMessage(text) 
      text = "" 

      /* This works. */ 
      //messageList.positionViewAtEnd(); 
     } 
    } 

    Component { 
     id: messageDelegate 
     Rectangle { 
      anchors.left: parent.left 
      anchors.right: parent.right 

      color: "white" 
      height: nameText.height + 4 

      Text { 
       id: nameText 
       wrapMode: Text.Wrap 
       text: "<b>" + authorName + " (" + authorId + ")</b> " + message 
       [anchor stuff] 
      } 
      ListView.onAdd: { 
       console.log("This prints just fine!") 
       messageList.positionViewAtEnd() 
      } 
     } 
    } 
} 

Die wirklich seltsame Sache ist, dass messageList.positionViewAtEnd() (am Ende der Datei) tatsächlich springt es zum Anfang zurück. Ohne den Anruf bleibt die Ansicht dort, wo sie ist, selbst wenn neue Einträge in der Liste erscheinen. Und in der Tat, wenn man sich die Qt documentation for the ListView.positionViewAtEnd() aussehen, heißt es:

Positioniert den Blick auf die beginnen oder enden unter Berücksichtigung ...

Ist das ein dummer Fehler in die Dokumentation, oder was? Ich habe alles versucht, was mir einfällt, um diese Arbeit zu erledigen, besonders die Methode positionViewAtIndex() und die Verwendung von Textmarkern, um das Scrollen zu erzwingen. Aber nichts funktioniert. Notieren Sie sich den /* This works. */ Kommentar in dem obigen Quellcode. Wenn das aktiviert ist, funktioniert es völlig gut! (außer natürlich, es springt auf den ListView.count() - 2 Index, anstelle des Endes der Liste)

Hat jemand eine Idee, was hier falsch sein könnte? Irgendwelche Beispiele, die ich versuchen könnte zu beweisen, dass es einen schrecklichen, schrecklichen Fehler in QML gibt?

Ich benutze Qt 5.3.1 mit QtQuick 2.0 (oder 2.1 oder 2.2 scheitern auch). Ich habe viele, viele andere Konfigurationen und Code ausprobiert, also bitte fragen Sie, ob Sie weitere Informationen benötigen. Ich habe meinen Google-Fu völlig erschöpft.

Danke!


bearbeiten 1

Während die akzeptierte Antwort das oben genannte Problem löst, geht es um das Hinzufügen der Component.onCompleted zu den Delegierten. Dies scheint Probleme beim Scrollen der Liste zu verursachen, weil (wie ich glaube) die Delegaten der Ansicht hinzugefügt werden, wenn Sie nach oben scrollen, was dazu führt, dass der onCompleted-Trigger aufgerufen wird, selbst wenn das Modellelement nicht neu ist. Dies ist höchst unerwünscht. Tatsächlich friert die Anwendung ein, wenn ich versuche, nach oben zu scrollen und dann neue Elemente zur Liste hinzuzufügen.

Es scheint, dass ich ein model.onAdd() - Signal brauche, anstatt die Existenz einer Delegat-Instanz zu verwenden, um den Bildlauf auszulösen. Irgendwelche Ideen?


Edit 2

Und wie funktioniert das nicht?

Der Text "Dies druckt richtig" druckt, also warum positioniert es nicht? Tatsächlich scheint es die Position nach oben zurückzusetzen. Also habe ich versucht positionViewAtBeginning(), aber das hat das Gleiche gemacht.

Ich bin total ratlos. Es fühlt sich an wie ein Käfer.

+0

Was ist los mit Ihrer auskommentierten Lösung? Das funktioniert für mich mit Qt 5.4 auf Ubuntu (es ist genau am Ende von dem, was ich sehen kann, positioniert, nicht zählen - 2). Bitte erstellen Sie einen Fehlerbericht für den Hang beim Scrollen und onCountChanged funktioniert nicht. – Mitch

+1

Das Problem ist, dass das Element nicht sofort zur Liste hinzugefügt wird (das tatsächliche Element wird von einer Serveranforderung hinzugefügt), und so ist es entweder um eins (auf dem Sender) oder überhaupt nicht aktualisiert (auf dem anderer Kunde). Der 'positionViewAtBeginning()' _must_ Vorgang muss in der Liste selbst aktualisiert werden. Ich habe eine schlechte Bug-Anfrage (QTBUG-41571) eingereicht, also hoffe ich, dass dieser Beitrag ausreicht, um das Problem zu klären. – jmbeck

+0

Haben Sie es geschafft, das Problem zu lösen? Ich habe das genaue Problem und kann keine Lösung finden. Danke! –

Antwort

5

Es ist eine Notiz in documentation:

Hinweis: Methoden nur aufgerufen werden soll, nachdem die Komponenten abgeschlossen hat. Um die Ansicht beim Start zu positionieren, sollte diese Methode von Component.onCompleted aufgerufen werden.

ändern ListView.onAdd: zu

Component.onCompleted: { 
    console.log("This prints just fine!") 
    messageList.positionViewAtEnd() 
} 

Und es funktioniert gut.

In Ihrem Fall gibt das ListView add Signal aus, bevor der neue Delegat erstellt und abgeschlossen wird. Der ListView arbeitet noch an etwas hinter der Szene, so dass positionViewAtEnd nicht wie erwartet funktionieren kann. Und /* This works. */, weil es aufgerufen wird, nachdem der neue Delegat abgeschlossen ist. Gehen Sie jedoch nicht davon aus, dass dies immer funktioniert. Folgen Sie einfach dem Hinweis, rufen Sie positionViewAtEnd in Component.onCompleted, in der Dokumentation.

+0

Sie haben Recht. Vielen Dank. Für zukünftige Googler ist der zweite wichtige Teil, dass "Component.onCompleted" in der * Delegate *, ** nicht ** in der Liste selbst erscheinen muss. Das habe ich vermisst. – jmbeck

+0

Uh oh. Ich habe Probleme mit dem Scrollen. Siehe meine erste Bearbeitung. – jmbeck

+0

Hmm ... du hast Recht. ListView erstellt neue Delegaten, wenn Sie in der Liste blättern, und meine Antwort schlägt fehl. Vielleicht können Sie die Anzahl der Elemente im Modell prüfen, bevor Sie 'positionViewAtEnd' aufrufen, aber ich denke nicht, dass dies eine gute Lösung ist. – mcchu

3

Sie müssen auch die currentIndex einstellen.

testme.qml

import QtQuick 2.2 
import QtQuick.Controls 1.1 
import QtQuick.Window 2.0 

ApplicationWindow { 
    title: qsTr("Hello World") 
    width: 300 
    height: 240 

    ScrollView { 
     anchors.fill: parent 

     ListView { 
      anchors.fill: parent 

      id: messageList 
      model: messageModel 
      delegate: Text { text: mytextrole } 
      highlight: Rectangle { color: "red" } 
      highlightMoveDuration: 0 

      onCountChanged: { 
       var newIndex = count - 1 // last index 
       positionViewAtEnd() 
       currentIndex = newIndex 
      } 
     } 
    } 

    ListModel { 
     id: messageModel 
     ListElement { mytextrole: "Dog"; } 
     ListElement { mytextrole: "Cat"; } 
    } 

    Timer { 
     property int counter: 0 
     running: true 
     interval: 500 
     repeat: true 

     onTriggered: { 
      messageModel.append({"mytextrole": "Line" + (counter++)}) 
     } 
    } 
} 

Es gibt immer noch etwas Springen zu dem ersten Element und zurück nach unten für einen Bruchteil einer Sekunde springen.

+0

Interessant. Vielleicht ist dies die Quelle des Fehlers in Qt. – jmbeck