External images

There might be cases where you need to load an Image that isn't bundled with your application, but instead is downloaded remotely or generated by the application. 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 application binary at compile time. When an app needs to access images from a location in the device file system, like the shared directory, this 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 application). 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 application has access to its own working directory in the file system. The working directory is where the application is started, and is also known as the current directory and the sandbox. Within the application working directory, there are a number of folders that your application has access to.

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

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.

C++

// 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"));

QML

Since you can't use the functionality in QDir from QML, you must retrieve the path to the device home 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"  
}

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  ImageTracker, which encapsulates the asynchronous parts of the Image.

The following C++ example shows how to load an image and display it when it is successfully loaded:

void App::setup() 
{
    Page *root = new Page();
    Container *container = new Container();
     
    mImageView = new ImageView();
    mImageTracker = new ImageTracker(QUrl("assets/image.png"));
 
    // If any Q_ASSERT statement(s) indicate that the slot failed to 
    // connect to the signal, make sure you know exactly why this has happened.
    // This is not normal, and will cause your app to stop working!!
    bool result = connect(mImageTracker,
                      SIGNAL(stateChanged(bb::cascades::ResourceState::Type)),
                      this,
                      SLOT(onStateChanged(bb::cascades::ResourceState::Type)));
    
    // This is only available in Debug builds.
    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());
    }
}

And here's an example of how to do the same using QML:

ImageView {
    id: myImageView
    attachedObjects: [
        ImageTracker {
            id: tracker
            imageSource: "images/image.png"
              
            onStateChanged: {                    
                if (state == ResourceState.Loaded)
                {
                    myImageView.image = tracker.image
                }
            }
        }
    ]
}

Last modified: 2013-12-21

comments powered by Disqus