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" p } folder: "file://" + path
Full source code of the photo gallery is available here:
- http://code.google.com/p/libpong/source/browse/libpong/pong/declarative-camera/Gallery.qml
- http://code.google.com/p/libpong/source/browse/libpong/pong/declarative-camera/PhotoGallery.qml
← October 2nd 2014 Summary of UI changes | ● | October 15th 2014 Android Storage →