Model/View With BlackBerry 10 Cascades
In this post we'll look briefly at the Model View support from QML in BlackBerry 10's Cascades Framework, including an example program.
The Cascades framework lets you create UIs for the BlackBerry Application Platform with relative ease. This Qt based framework can be used to develop native applications for the BlackBerry 10 mobile platform. It supports development in C++ and/or QML.
Recently, I have been using Cascades for development of some mobile applications and I wanted to show an example of the support it offers for data management using Model/View from QML.
Providing a powerful construct for data management under the Cascades UI framework, ListView follows a Model/View architecture to provide a scrollable container with list items. Support for accessing data from external data sources, such as XML, JSON and SQL, is provided by the classes JsonDataAccess, XmlDataAccess and SqlDataAccess. These classes convert data into Qt C++ objects that can be added to a data model and displayed using the Cascades User Interface.
Let's look at an example of loading the data from the above data sources and displaying it using the ListView QML component of the Cascades UI.
Our example model data can be contained in a JSON or XML file or a database file as follows:
The file grocerylists.json, below, provides a list of name/value pairs that represent an object or record. For more detailed information about JSON and its structure, see www.json.org.
[ {"item":"Banana","grouping":"Fruits","image":"asset:///images/banana.png"}, {"item":"Orange","grouping":"Fruits","image":"asset:///images/orange.png"}, {"item":"Peach","grouping":"Fruits","image":"asset:///images/peach.png"}, ... {"item":"Red Wine","grouping":"Alcohol","image":"asset:///images/redwine.png"}, {"item":"White Chocolate","grouping":"Chocolate Factory","image":"asset:///images/whitechocolate.png"}, {"item":"Dark Chocolate","grouping":"Chocolate Factory","image":"asset:///images/darkchocolate.png"}, {"item":"Cookies","grouping":"Chocolate Factory","image":"asset:///images/cookie.png"} ]
File grocerylists.xml, below, provides a tree structure that starts at the root and has branches to provide the data storage in XML format. For information about XML and its structure, see http://www.w3.org/XML
<root> <header category="Fruits"> <item icon="images/banana.png" title="Banana" /> <item icon="images/orange.png" title="Orange" /> <item icon="images/peach.png" title="Peach" /> <item icon="images/pear.png" title="Pear" /> <item icon="images/strawberries.png" title="Strawberries" /> </header> <header category="Vegetables"> <item icon="images/broccoli.png" title="Broccoli" /> <item icon="images/yellowpepper.png" title="Yellow Pepper" /> ... <item icon="images/whitewine.png" title="White Wine" /> <item icon="images/redwine.png" title="Red Wine" /> </header> <header category="Chocolate Factory"> <item icon="images/whitechocolate.png" title="White Chocolate" /> <item icon="images/darkchocolate.png" title="Dark Chocolate" /> <item icon="images/cookie.png" title="Cookies" /> </header> </root>Query
In addition to the above simple data storage, the Cascades framework can use an SQL database to store data. The code below creates a SQLite database called grocerycatalog.db and inserts sample grocery items into the database.
// Create an instance of QSqlDataBase with the driver QSQLITE database QSqlDatabase database = QSqlDatabase::addDatabase("QSQLITE"); // Define the path where database would be located QDir home = QDir::home(); QString filePath = home.absoluteFilePath("grocerycatalog.db"); // Delete file to recreate the database QFile::remove(filePath); QFile file(filePath); // Set the database path name for reading and writing data database.setDatabaseName(filePath); if (database.open()) { if (file.open(QIODevice::ReadWrite)) { SqlDataAccess sda(home.absoluteFilePath("grocerycatalog.db")); sda.execute("CREATE TABLE GroceryCatalog( item VARCHAR(50), category VARCHAR(100), image VARCHAR(100) );"); sda.execute("INSERT INTO GroceryCatalog(item, category, image) VALUES(\"Banana\", \"Fruits\", \"asset:///images/banana.png\");"); sda.execute("INSERT INTO GroceryCatalog(item, category, image) VALUES(\"Orange\", \"Fruits\", \"asset:///images/orange.png\");"); sda.execute("INSERT INTO GroceryCatalog(item, category, image) VALUES(\"Peach\", \"Fruits\", \"asset:///images/peach.png\");"); sda.execute("INSERT INTO GroceryCatalog(item, category, image) VALUES(\"Pear\", \"Fruits\", \"asset:///images/pear.png\");"); ...
Having listed out the various data structure provided by Cascades, here is the QML code that presents the data in graphical view for the JSON data storage.
import bb.cascades 1.0 import bb.data 1.0 Page { content: Container { ListView { id: listView objectName: "listView" dataModel: MyListModel { id: myListModel } listItemComponents: [ // Define delegates for different item types here ListItemComponent { // StandardListItem is a convenience component for lists with default Cascades look and feel StandardListItem { title: ListItemData.text description: ListItemData.description imageSource: ListItemData.image } } ] // More code as needed... } } onCreationCompleted: { // Populate list view model with the sample data myListModel.load("mydata.json") } }
ListView visuals are managed by the listItemComponents property. It defines the delegates for different item types and ListItemComponents are used to provide the item visuals. Standard list items consist of sets of common properties to be displayed, such as an image, bold title text, description text and status text. Each of the properties is optional.
The data model myListModel will load/append the data using the JsonDataAccess class, which provides an array of QVariantList. The root element of the JSON data should be either an array or an object, so the corresponding Qt value types are QVariantList and QVariantMap.
The relevant code is shown below:
void MyListModel::load(const QString& file_name) { bb::data::JsonDataAccess jda; QVariantList lst = jda.load(file_name).value(); if (jda.hasError()) { bb::data::DataAccessError error = jda.error(); qDebug() << file_name << "JSON loading error: " << error.errorType() << ": " << error.errorMessage(); } else { qDebug() << file_name << "JSON data loaded okay."; append(lst); } }
XmlDataModel reads the data from the XML file and creates the data model. The list item component's delegate presents the data in a grid view creating list items containing an image and the title. The Header item component is the category section of the various list items, where each element in the XML file is shown as an item in the ListView.
Container { ListView { id: foodList objectName: "foodList" layout: GridListLayout { columnCount: 2 headerMode: ListHeaderMode.Standard cellAspectRatio: 1.1 spacingAfterHeader: 40 verticalCellSpacing: 10 } dataModel: XmlDataModel { source: "models/fooditem.xml" } listItemComponents: [ // Define delegates for different item types here ListItemComponent { type: "header" Header { title: { ListItemData.category } } }, ListItemComponent { // Custom list item type: "item" FoodItem { } } ] // listItemComponents } // ListView } // Container
Similarly, we can use an SQL database with GroupDataModel to represent the data in a list view. It is sorted with the grouping item category, compared to the list view that is used for the XML data storage.
... attachedObjects: [ GroupDataModel { id: dataModel sortingKeys: [ "category"] grouping: ItemGrouping.ByFullValue }, MySqlDataSource { id: dataSource // Load the data from an SQL database based on a specific query source: "producecatalog.db" query: "select * from ProduceCatalog" onDataLoaded: { // After the data is loaded, insert it into the data model dataModel.insertList(data); } } // end of DataSource ] // attachedObjects onCreationCompleted: { // When the list view has been created load the data. dataSource.loadData(); } } // ListView } // Container } // Page
Screen shots of the example application using each of the three data sources are shown below.
I hope this small example has illustrated some of the QML features supported by BlackBerry 10 Cascades. You can download the complete source code for the example from here. You will need to have the BlackBerry 10 Cascades SDK installed to run it.