QML Paging Using ListView

I originally published this How-To at Forum.Nokia and is an updated version of my entry into the QtQuick competiton.

There are many uses for paging in mobile apps. One use is to create something like a desktop view – Symbian and Maemo both have multiple desktops which slide across one at a time when you swipe. Another is for something like a full screen view in an image gallery.

After I Started investigating this I have found that there is an example of this in the QML SDK in the Qt Documentation at VisualItemModel example. There is also another Wiki describing how to create paging using a PathView at How to create a PageControl component in QML. However, I have focused on the ListView system and have taken that and expanded it further, but the principle is still the same. This method provides a simple framework for adding the List content dynamically.

In this example, we create an ImageViewer. The QML doesn’t define any images explicitly, instead, the images are added dynamically via JavaScript.

Mid flick on Symbian 3 simulator(Gaps between images due to image capture on N900 which has a different image size ratio to the N8 and should disappear with images taken on the device running the Image viewer)

Creating the shell

We begin with 3 items: In a file called ImageViewer.qml, we have created A Rectangle, A Visual Data Model and a List View. For an image viewer, we may want it to fill the screen so we set the view to be maximized and the ListView to fill it’s parent. We also set the model property on the ListView to the VisualDataModel:

import QtQuick 1.0

Rectangle {
 width: view.width
 height: view.height

 VisualDataModel {
 id: dataModel
 }    

 ListView {
 id: view
 anchors.fill: parent;
 model: dataModel
 orientation: ListView.Horizontal
 }
}

Flickable behavior

We set up the scrolling on the ListView - we only want it to scroll / flick one image at a time so we set the snapMode to SnapOneItem and have a low flickDecelertation. The Higher the flickDecelertaion, the longer it will take to stop so having a low number reduces the chance of skipping images. The cacheBuffer is the number of pixels before and after the displayed item that the delegate will be retained. Setting this to it's width ensures that at lease the image delegate either side of the current one is retained. 
ListView {
 id: view
 anchors.fill: parent;
 model: dataModel
 orientation: ListView.Horizontal
 snapMode: ListView.SnapOneItem;
 flickDeceleration: 500
 cacheBuffer: width;
 }

Creating the dynamic data model

In the VisualDataModel, we create a Delegate and assign it’s properties. No source property is set at this time, just width, height and fillMode -which means the image will fill the maximum area it can whist retaining it’s aspect ratio (The Image.PreserveAspectFit fillMode setting below is the reason for the gaps in the Simulator screenshot above). We also create assign the model property to a new ListModel and give it an id:

VisualDataModel {
 id: dataModel
 model: ListModel{
   id:innerModel
 }

 delegate: Image {
   width: view.width;
   height: view.height;
   fillMode: Image.PreserveAspectFit
   }
 }

New we add some JavaScript. In a new JavaScript file we can create 2 methods. createImages() and createImage(url). createImages is called when the ImageViewer.qml file is executed and creates an array of image sources. for each image in the array we call createImage(url) to add it to the ListView. I have used a fixed array here, but you can create your array however you want.

function createImages()
{
 var files = ["waterfall.jpg","lake.jpg",
 "river.jpg","boats.jpg"];
 var i = 0;
 for (i=0; i<files.length; i++){
 createImage(files[i]);
 }
}

function createImage(name) {
 innerModel.append({"url": name});
}

The reason for the ListModel inside the VisualDataModel is that the VisualDataModel doesn’t provide an method to append data to it, where as the ListModel does. It is here where we create the url property for the Images to bind to. Again, if you are not doing images, add whatever properties are relevant for your object. Don’t Forget: You need to initialize your index in your loop before the for loop declaration when using JavaScript with QML.

We have three tasks remaining. So go back to the QML file and in the Image delegate add the source property, and a import statement to include the JavaScript file: Lastly add the code to call createImages() when the application starts.

import QtQuick 1.0
import "ImageLoader.js" as ImageLoader

Rectangle {
 width: view.width
 height: view.height

 Component.onCompleted: ImageLoader.createImages()

 .....

 delegate: Image {
 width: view.width;
 height: view.height;
 source: url
 }

Working with devices without a touch-screen

This means adding key handling to the scrolling can be done using the keyboard. To do that we need to keep track of the current index of the ListView and change it when keys are pressed. To track the current index in the ListView for either keyboard use or swiping, you need to add a couple of lines to the ListView object to do that:

ListView {
 id: view
 anchors.fill: parent;
 model: dataModel
 orientation: ListView.Horizontal
 snapMode: ListView.SnapOneItem;
 flickDeceleration: 500
 cacheBuffer: width;
 preferredHighlightBegin: 0; preferredHighlightEnd: 0  //this line means that the currently highlighted item will be central in the view
 highlightRangeMode: ListView.StrictlyEnforceRange  //this means that the currentlyHighlightedItem will not be allowed to leave the view
 highlightFollowsCurrentItem: true  //updates the current index property to match the currently highlighted item
}

The next part is to add keyboard handling code that uses the index to move the image when certain keys are pressed. The following code will hook up the Left and Right d-pad buttons and should be added to the ListView below the code above:

focus: true
 Keys.onLeftPressed: {
 if (currentIndex > 0 )
 currentIndex = currentIndex-1;}
 Keys.onRightPressed: {
 if (currentIndex < count)
 currentIndex = currentIndex+1;}

The focus line ensure the ListView has keyboard focus. OnLeftPressed and onRightPressed capture the keyboard button, and then by setting the currentIndex, because we have the highlight properties set in the previous code segment, the ListView will move the new selection to the centre of the screen. Before changing the index, we check to make sure it isn’t already at the beginning or end of the list, so we add the if statement before we adjust the index.

Using D-Pad to switch images

Extending this system

This can be used as a component and added inside other objects, or used as an application on it’s own. Now we are tracking the index, you could, displaying a number showing the page for a book application perhaps, or by creating a system similar to the Symbian desktop switching button. An example of the latter is shown in the VisualItemModel example.

Download this code

You can download the code for this from the link below. There are no images in this zip file so please add any images you want to the folder you extract it to, and edit ImageLoader.js to include the correct file names.

File:PagingWithListViewDemo.zip

This file contains:
ImageViewer.qmlproject – project file
ImageViewer.qml – QML code
ImageLoader.js – JavaScript file to get the image list
readme.txt – reminder to swap out the image file names in ImageLoader.js

Advertisements

About bluechrism

I am a software developer with most professional experience in the Windows .Net realm and I'm currently a WPF developer with Starkey Labs. However, I have wanted for some time to start the mobile developer journey properly and being an N900 owner, this was to be in the realm of QT. Job hunting, moving to Minnesota and changing jobs put my plans on hold 6-12 months but things are starting to settle now, just as I'm getting sorted to start some things, Microsoft and Nokia merge. This blog is about my novice mobile development experiences and hopefully will end up complete with links to download some apps on various platforms, but obviously by the name, Sybian, Maemo/Meego and Windows Mobile. In other stuff, I am English, I support Everton FC, I have visited Glastonbury music festival 5 times and recommend it to anyone. I am married and my wife and i have a dog called Friday.
This entry was posted in How To, Nokia, Qt. Bookmark the permalink.

3 Responses to QML Paging Using ListView

  1. It’s laborious to seek out educated individuals on this matter, however you sound like you understand what you’re talking about! Thanks

  2. g says:

    do you know how to use the VisualDataModel to show the file system as a tree?

  3. Nice tutorials.
    Request: If can correct, QT as Qt.

    Thanks.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s