October 15th 2014, Photo Gallery

October 2nd 2014 Summary of UI changes | | October 15th 2014 Android Storage

So far the QML camera for Android just has a “View” button which pops up the last captured photo. How about a photo gallery?

Showing a collection of items is pretty easy with Qt and QML in particular. Qt follows the so called Model/View concept. The model specifies the actual items and their type, whereas the view defines the layout of the items and the user interactions. The actual look and feel of a single item is not defined by the view but by a delegate. Exchanging or adding delegates easily adapts and extends the view by new item types. Exchanging the view easily rearranges the layout without recoding the model.

Qt provides a couple of standard view layouts, in particular a ListView and a GridView. As the name suggests the layout is a linear (horizontal or vertical) one for the ListView and a matrix for the GridView. Interestingly, all the layouts have the nice poperty that they can contain an arbitrarily large number of items, because invisible items are not loaded into memory. The standard views are derived from Flickable, so that kinetic scrolling is available, if the contents of the view does not fit the containing item.

With the Model/View concept of QML a basic photo gallery is quickly set up by using a ListView. What we need is a model to populate the view with items that are the files of the image directory. Qt.labs provides a model for that: FolderListModel. Supposed we already have setup the model folderModel and a delegate imageDelegate, the photo gallery’s ListView is coded as follows:

ListView {
    id: view
    anchors.fill: parent
    orientation: ListView.Horizontal
    spacing: 10

    model: folderModel
    delegate: imageDelegate

To populate the view we use the following instance of FolderListModel:

FolderListModel {
    id: folderModel
    nameFilters: ["*.jpg", "*.jpeg", "*.png", "*.gif", "*.tif", "*.tiff"]
    sortField: FolderListModel.Time
    sortReversed: true
    showDirs: false

For each item we use a delegate that loads the image from the model’s file name. For performance reasons loading is done asynchronously. To prevent the images to consume a whole lot of memory, we define a maximum image size of 1024.

Component {
    id: imageDelegate

    Rectangle {
        width: 256;
        height: parent.height;

        Image {
            id: imageItem
            source: fileURL
            asynchronous: true
            width: parent.width
            height: parent.height
            sourceSize.width: 1024
            sourceSize.height: 1024
            fillMode: Image.PreserveAspectFit
            smooth: true

An non-standard QML feature of delegates is that they have direct access to the models properties. For example, the imageDelegate gets its fileURL from the corresponding property of the folderModel.

Now how do we get the above view to show the contents of a particular folder? We need to pass the canonical path of the folder as URL to the folderModel:

FolderListModel {
    id: folderModel
    folder: "file:///path/to/gallery/folder"

But what is the actual path of the images captured by the Android camera? Unfortunately, the answer is: we do not know, period! Each Android phone manufactor may use different paths. What is more, there is also no standard way to mount external SD cards. Might be /storage/external_SD but could be anything else like /storage/extSdCard or /mnt/extSdCard. If the camera is set to capture to the external card, then the path is absolutely arbitrary.

The only way to get to know where the images actually end up being stored, is to capture an image and have a look at the camera.imageCapture.capturedPath:

property string path: {
    var p = camera.imageCapture.capturedImagePath
    p = p.substring(0, p.lastIndexOf("/"))
    if (p == "") p = "/storage/sdcard0/DCIM"

folder: "file://" + path

Full source code of the photo gallery is available here:

October 2nd 2014 Summary of UI changes | | October 15th 2014 Android Storage