External images

There might be cases where you need to load an Image that isn't bundled with your app, but instead is downloaded remotely or generated by the app. This type of content can include contact profile pictures, camera pictures, or generated content (such as scrapbook images and album covers).

Unlike assets, these images are not stored in the app's assets directory, so they don't get packaged with the app binary at compile time. When an app needs to access images from a location in the device file system, such as the shared directory, this approach can result in longer load times and images might even fail to load (for example, if an image in the shared directory is deleted by another app). Like assets, external images are loaded asynchronously so that they don't block the UI thread.

An image displayed in an image view.

Loading external images

Each app has access to its own working directory in the file system. The working directory is where the app is started and is also known as the current directory and the sandbox. In the app working directory, there are a number of directories that your app has access to.

The data directory contains your app's private data. You can use this directory to store images and any other content that your app uses. Your app also has access to a shared directory that contains content that any app can access. The shared directory contains videos, camera photos, music, and much more. For more information about an app's working directory and the shared directory, see File system access.

To load an external image, you must use the file:/// prefix with an absolute path to the location of the image in the file system. Here are some examples of how to load an image from the shared/camera directory using absolute paths:

Because you can't use the functionality in QDir from QML, you must retrieve the path to the device root directory in C++ and expose it to QML using a context property.

// ApplicationUI.cpp

// Load the QML file
QmlDocument *qml = QmlDocument::create("asset:///main.qml");

// Retrieve the path to the app's working directory
QString workingDir = QDir::currentPath();

// Build the path, add it as a context property, and expose
// it to QML
QDeclarativePropertyMap* dirPaths = new QDeclarativePropertyMap;
dirPaths->insert("camera", QVariant(QString(
        "file://" + workingDir + "/shared/camera/")));
qml->setContextProperty("dirPaths", dirPaths);
// main.qml

// Load the image asynchronously
ImageView {
    imageSource: dirPaths.camera + "camera0001.jpg"  
}
// Retrieve the path to the app's working directory
QString workingDir = QDir::currentPath();

// Load the image asynchronously
Image image = Image(QUrl("file://" + workingDir + 
        "/shared/camera/camera0001.jpg"));

Not applicable

Checking the image state

When working with external images, you should assume that the image might take some time to load, or might not load at all. To account for this, an Image goes through a series of states indicating when the image is loaded and if an error occurred. The state of an image and information about its size are accessible through the ImageTracker class, which encapsulates the asynchronous parts of the Image.

The following code sample shows how to load an image and display it when it is successfully loaded:

ImageView {
    id: myImageView
    attachedObjects: [
        ImageTracker {
            id: tracker
            imageSource: "images/image.png"
              
            onStateChanged: {                    
                if (state == ResourceState.Loaded)
                {
                    myImageView.image = tracker.image
                }
            }
        }
    ]
}
void App::setup() 
{
    Page *root = new Page();
    Container *container = new Container();
     
    mImageView = new ImageView();
    mImageTracker = new ImageTracker(QUrl("assets/image.png"));
 
    bool result = connect(mImageTracker,
                      SIGNAL(stateChanged(bb::cascades::ResourceState::Type)),
                      this,
                      SLOT(onStateChanged(bb::cascades::ResourceState::Type)));
    
    Q_ASSERT(result);
    Q_UNUSED(result);
     
    container->add(mImageView);
    container->add(button);
    root->setContent(container);
 
    Application::setScene(root);
}
   
void App::onStateChanged(ResourceState::Type state)
{
    if (state == ResourceState::Loaded)
    {
        mImageView->setImage(mImageTracker->image());
    }
}

Not applicable

Last modified: 2015-07-24



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus