As I indicated in my previous post, I am trying to port my Audiobook Reader application to BB10. I needed to make few changes to use BB10 Cascades API.
Previously I posted about how to create custom dialog box using Cascades QML API.
In this post I will talk about how to create custom list view.
For most case StandardListItem should be enough for use. Its looks like below.
But I wanted to learn creating custom component and also wanted to add some dynamic behavior to my list item.
I wanted to add Delete button on my list item. By default delete button is invisible, its get visible on long press event. It you tap on button the delete event is fired, touching anywhere else in item makes it invisible again. Also touching List item in normal state fire the selection event.
I was not sure how I can achieve this behavior with StandardListItem and I create my custom list item like below.
Normal condition, it looks like below.
On long press event on list item, it shows the delete button.
Now lets see how I created list item to satisfy above behavior.
First lets create list view, which uses the custom list item named ListDelegate and also have necessary function (selected and delete )to handle events from list item.
First we need to set touchPropogationMode to Full on root container, so the all sub component get chance to handle their event. Then I added to sub container to main container to hold actual list item content and one to hold delete button and added gesture handler to both of them.
Unfortunately I was not able to find easy way to emit signal from ListItem to ListView, so I ended up calling ListView function manually to indicate select and delete event. By calling list view function
To access current list items's index, we can use root.ListItem.indexPath property.
Previously I posted about how to create custom dialog box using Cascades QML API.
In this post I will talk about how to create custom list view.
For most case StandardListItem should be enough for use. Its looks like below.
But I wanted to learn creating custom component and also wanted to add some dynamic behavior to my list item.
I wanted to add Delete button on my list item. By default delete button is invisible, its get visible on long press event. It you tap on button the delete event is fired, touching anywhere else in item makes it invisible again. Also touching List item in normal state fire the selection event.
I was not sure how I can achieve this behavior with StandardListItem and I create my custom list item like below.
Normal condition, it looks like below.
On long press event on list item, it shows the delete button.
Now lets see how I created list item to satisfy above behavior.
First lets create list view, which uses the custom list item named ListDelegate and also have necessary function (selected and delete )to handle events from list item.
ListView { id: listView dataModel: listModel.model listItemComponents: [ ListDelegate { } ] //called by listdelegate manually function selected(index) { console.log("###### Index selected:" + index); } //called by listdelegate manually function delete(index) { console.log("###### Delete index:" + index); } }Following is code for ListDelete component. I removed some code to keep code short and relevant.
First we need to set touchPropogationMode to Full on root container, so the all sub component get chance to handle their event. Then I added to sub container to main container to hold actual list item content and one to hold delete button and added gesture handler to both of them.
Unfortunately I was not able to find easy way to emit signal from ListItem to ListView, so I ended up calling ListView function manually to indicate select and delete event. By calling list view function
root.ListItem.view.selected() and root.ListItem.view.delete().
To access current list items's index, we can use root.ListItem.indexPath property.
ListItemComponent { Container{ id: root layout: DockLayout {} touchPropagationMode: TouchPropagationMode.Full; Container{ id: itemRoot layout: DockLayout {} preferredWidth: 768; preferredHeight: 120 gestureHandlers:[ LongPressHandler { onLongPressed: { //make delete button visible deleteBtn.opacity = 1; itemRoot.opacity = 0.3 } }, TapHandler { onTapped: { if( deleteBtn.opacity == 1 ) { //make delete button hide deleteBtn.opacity = 0; itemRoot.opacity = 1; } else { //fire selected event root.ListItem.view.selected(root.ListItem.indexPath); } } } ] Divider { verticalAlignment: VerticalAlignment.Bottom horizontalAlignment: HorizontalAlignment.Center } Container { verticalAlignment: VerticalAlignment.Center layout:StackLayout { orientation: LayoutOrientation.LeftToRight; } ImageView{ id: image preferredHeight: 110; preferredWidth: 110 imageSource: ListItemData.image; verticalAlignment: VerticalAlignment.Center } Container { layout:StackLayout {} bottomPadding: 10 Label{ text: ListItemData.title; } Label{ text: ListItemData.subTitle; } } } } Container { id: deleteBtn opacity: 0; preferredWidth: 150; preferredHeight: 100 verticalAlignment: VerticalAlignment.Center horizontalAlignment: HorizontalAlignment.Right ImageView{ imageSource: "delete.png"; verticalAlignment: VerticalAlignment.Center horizontalAlignment: HorizontalAlignment.Center } gestureHandlers:[ TapHandler { onTapped: { if( deleteBtn.opacity == 1 ) { deleteBtn.opacity = 0; itemRoot.opacity = 1; root.ListItem.view.delete(root.ListItem.indexPath); } } } ] } } }Thought it seems lot of code, its quite easy to create custom list item. Now lets see how we can provide data to ListView. For my case I created Data model in C++ and exported it to QML. You can export c++ Data model to QML using setContextProperty. listModel is instance of class derived from QObject.
QmlDocument *qml = QmlDocument::create("asset:///main.qml").parent(&app); ListModel listModel; qml->setContextProperty("listModel", &listModel);We are setting model to list view like dataModel: listModel.model, it means that listModel has property called model. Following is code from the same.
class ListModel : public QObject { Q_OBJECT Q_PROPERTY(bb::cascades::DataModel* model READ model); public: void loadData(); bb::cascades::DataModel* model(return mListModel;); private: QMapListDataModel* mListModel; };And I am populating the ListModel like below. I am packaging my List ItemData inside QVariantMap, so that ListItem can easily access the data.
void ListModel::loadData() { mListModel->clear(); foreach( ... ) { QVariantMap map; map["image"] = "image path"; map["title"] = "title"; map["subtitle"] = "subtitle"; (*mListModel) << map; } }If you see ListItem's code, We can access the list item data like below.
ImageView{ imageSource: ListItemData.image; } Container { Label{ text: ListItemData.title; } Label{ text: ListItemData.subTitle; } }Now finally its done. I hope it will be helpful to someone.
Hi,
ReplyDeleteI am developing an image viewer and went your way too. QMapListModel and so on with a custom delegate for my ListView.
Issue is, the ListView shows all the items, but only loads the first image I add and shows the rest as black squares.
Any idea why ?
The code doesn't seem to be different from yours. Have you encountered this issue before?
I think I faced some data model related issue when i was setting type attribute in wrong way. I choose to remove type attribute and then it was working fine. But else that this I did not faced any issue, for me image loads fine for each list item.
DeleteListItemComponent {
type: "header"
...
}
I see there is an image in your list view, how do I make a context menu to change that image. Thanks
DeleteI guess you are talking about thumbnail image in list delegate. You will have to change data model to change the thumbnail image in list delegate.
DeleteHi Kunal, I need to implement a dynamic listview. Like, I ll get a list from server as json or XMLsoap . I need to parse it and put it in the list and show it. I am looking for some help on this.
ReplyDeletemay be this link can help you,
DeleteIts example where it uses the JSON data file as model
http://blackberry.github.io/Cascades-Samples/rssnews.html