2015-08-13 28 views
10

Chciałbym wiedzieć, czy możliwe jest użycie (kilku) różnych delegatów dla QML ListView.Różni delegaci dla QML ListView

W zależności od pojedynczego obiektu w modelu ListView, chciałbym wizualizować obiekty z różnymi delegatami.

Ten fragment kodu wyjaśnia, co chcę osiągnąć:

main.qml

import QtQuick 2.4 
import QtQuick.Controls 1.3 
import QtQuick.Window 2.2 
import QtQuick.Dialogs 1.2 

ApplicationWindow { 
    title: qsTr("Hello World") 
    width: 640 
    height: 480 
    visible: true 

    ListModel { 
     id: contactsModel 
     ListElement { 
      name: "Bill Smith" 
      position: "Engineer" 
     } 
     ListElement { 
      name: "John Brown" 
      position: "Engineer" 
     } 
     ListElement { 
      name: "Sam Wise" 
      position: "Manager" 
     } 
    } 

    ListView { 
     id: contactsView 
     anchors.left: parent.left 
     anchors.top: parent.top 
     width: parent.width 
     height: parent.height 
     orientation: Qt.Vertical 
     spacing: 10 
     model: contactsModel 
     delegate: { 
      if (position == "Engineer") return Employee; //<--- depending on condition, load Contact{} 
      else if (position == "Manager") return Manager; //<--- depending on condition, load Person{} 
     } 
    } 
} 

Employee.qml (Jednym z możliwych komponentów, które chciałbym użyć jako delegat)

import QtQuick 2.4 

Rectangle{ 
    width: 200 
    height: 50 
    color: ListView.isCurrentItem ? "#003366" : "#585858" 
    border.color: "gray" 
    border.width: 1 

    Text{ 
     anchors.centerIn: parent 
     color: "white" 
     text: name 
    } 
} 

Manager.qml (inny komponent, którego chciałbym użyć jako delegata)

import QtQuick 2.4 

Rectangle{ 
    width: 200 
    height: 50 
    color: "red" 
    border.color: "blue" 
    border.width: 1 

    Text{ 
     anchors.centerIn: parent 
     color: "white" 
     text: name 
    } 
} 

Byłbym wdzięczny za radę! Dzięki!

+0

W tym przypadku można również rozważyć utworzenie unikalnego 'delegata', który używa' position' w wiązaniach, aby zmienić jego aspekt. W przeciwnym razie możesz użyć 'Loader', ale musisz przekazać pewne informacje do wewnętrznego' item', tzn. Prawdziwych delegatów. Również rozwiązanie Folibis może działać, ale nie sądzę, aby tak było w tym przypadku, ponieważ "pozycja" sama w sobie jest rolą. – BaCaRoZzo

+0

Dlaczego po prostu nie zdefiniujesz 'delegata' zawierającego zarówno' Rectangle's i ustawić ich 'visible' pole na podstawie' position'? – skypjack

Odpowiedz

-1

Oczywiście, jest to możliwe. ListView.delegate to rodzaj wskaźnika do Component, który narysuje elementy, aby można było je zmienić.

Na przykład:

Employee { id: delegateEmployee } 
Manager { id: delegateManager} 
... 
ListView { 
    property string position 
    delegate: position == "Engineer" ? delegateEmployee : delegateManager 
} 
+0

To nie rozwiąże problemu, ponieważ musisz zdefiniować 'position' zanim lista zostanie wypełniona, więc delegat będzie typu i nie zmieni się w zależności od zawartości przedmiotu. – skypjack

0

ile masz tylko dwa rodzaje, następujący kod jest łatwy do utrzymania w łatwy do zrozumienia:

delegate: Item { 
    Employee { visible = position === "Engineer" } 
    Manager { visible = position === "Manager" } 
} 

W przypadku liczby typów będzie rośnie, to nie jest odpowiednie rozwiązanie, ponieważ łatwo prowadzi do piekła wypowiedzi.

9

wierzę, że byłoby lepiej wdrożyć jednego delegata bazową dla wszystkich rodzajów position który ładuje konkretną implementację w zależności od position lub jakichkolwiek innych właściwości danych za pomocą Loader

BaseDelegate { 
    property var position 

    Loader { 
     sourceComponent: { 
      switch(position) { 
       case "Engineer": return engineerDelegate 
      } 
     } 
    } 

    Component { 
     id: engineerDelegate 
     Rectangle { 
      Text { } 
     } 
    } 
} 
+0

Czy istnieje ryzyko ponoszenia problemów z wydajnością, jeśli liczba pozycji jest wysoka? Wiem, że 'Loader's nie blokuje komponentów, ale wiele z nich będzie działało pod maską. Czy się mylę? – skypjack

+0

To zależy od złożoności delegatów, ale nie od modułu ładującego. – Andrii

+1

@skypjack ['Loader's] (https://youtu.be/2zWtxmhfyVo?t=1737) są słodkie. ;) – BaCaRoZzo

3

I wprowadziły go następująco:

ListView { 
    id: iranCitiesList 
    model: sampleModel 
    delegate: Loader { 
     height: childrenRect.height 
     width: parent.width 
     sourceComponent: { 
      switch(itemType) { 
      case "image" : 
       return imageDel; 
      case "video": 
       return videoDel; 
      } 
     } 
    } 
    ImageDelegate { id: imageDel } 
    VideoDelegate { id: videoDel } 
} 


ImageDelegate.qml

Component { 
    Image { /*...*/ } 
} 


VideoDelegate.qml

Component { 
    Item { /*....*/ } 
} 

Ostatnia uwaga, należy sprawdzić szerokość i wysokość delegatów. W moim przypadku musiałem ponownie ustawić szerokość i wysokość mojego uczestnika w Loader.
Powodzenia - Mousavi

+0

Zrobiłem kilka badań dotyczących wydajności. Wydaje się, że wydajność jest bardzo dobra i nie ma potrzeby optymalizacji. –

5

miałem ten sam problem, dokumentacja Qt zapewnia dość dobrą odpowiedź: http://doc.qt.io/qt-5/qml-qtquick-loader.html#using-a-loader-within-a-view-delegate

Najprostszym rozwiązaniem jest inline Component z Loader ustawić plik source:

ListView { 
    id: contactsView 
    anchors.left: parent.left 
    anchors.top: parent.top 
    width: parent.width 
    height: parent.height 
    orientation: Qt.Vertical 
    spacing: 10 
    model: contactsModel 
    delegate: Compenent { 
     Loader { 
      source: switch(position) { 
       case "Engineer": return "Employee.qml" 
       case "Manager": return "Manager.qml" 
      } 
     } 
    } 
} 

Każda próba użycia Loader.srcComponent spowoduje brak jakiejkolwiek zmiennej z modelu (w tym index). Jedynym sposobem na obecność zmiennych jest to, że dzieci Component znajdują się wewnątrz głównego Component, ale tylko jeden może być obecny, więc jest bezużyteczny.