Orientation

Responding to orientation changes is necessary for most applications, and it's straightforward to implement. After you've configured your app to support orientation changes, you just need to listen to change events and respond to them.

It's also important to consider how the UI looks in different views and handle orientation changes appropriately. Often, it isn't enough to simply resize your UI components to reflect the new screen dimensions, because the layouts in these views can be very different. For example, if you have an application with a list, it probably doesn't make sense to have the same UI for both portrait and landscape orientations, as you can see in the following images:

Screen showing different orientations

The number of items displayed and the screen space used are different for each orientation. When you design an application that works for both portrait and landscape orientations, it's important to think about how the different views look and what their strengths are. For example, in a photo browser app, it might make sense to restrict the folders list to portrait orientation only, but allow both portrait and landscape orientations when viewing individual photos.

Orientation changes don't apply to devices with a 1:1 aspect ratio (for example, the BlackBerry Q10 smartphone). For more information on how this affects your app, see Handling square displays.

Launching an application in landscape orientation

The default orientation when launching applications on BlackBerry 10 devices is portrait. To start your application in landscape orientation:

  1. In the Project Explorer view, double-click the bar-descriptor.xml file.
  2. On the Application tab, in the Orientation drop-down list, select Landscape.
Screen showing bar-descriptor.xml file

Now when you run your application, it will start in landscape orientation. Other valid orientation values are:
  • Default: The screen is in portrait orientation.
  • Auto-orient: The orientation of the screen matches the orientation of the device.
  • Portrait: The screen is in portrait orientation.

You can also set the orientation programmatically, as described in the next section.

Setting the orientation programmatically

To specify the orientations that your app supports, to force orientation changes, and to respond to device orientation changes, you use the OrientationSupport class. This class includes three properties that determine how orientation is handled in your app:

You set the display orientations that your app supports by using the supportedDisplayOrientation property. This property can be set to one of the following values:

Setting the property looks like this:

OrientationSupport.supportedDisplayOrientation = 
    SupportedDisplayOrientation.All;

If you change the supportedDisplayOrientation property programmatically and the user isn't physically rotating the device, the supportedDisplayOrientationChanged() signal is emitted.

If the Orientation value in the bar-descriptor.xml file is anything other than Auto-orient, setting supportedDisplayOrientation to SupportedDisplayOrientation::All results in the application being notified of 180 degree rotations only. Rotations from portrait to landscape orientation, or vice versa, do not result in application notifications. If the application needs to respond to all device rotations, make sure to set the Orientation value in the bar-descriptor.xml file to Auto-orient.

You can also use this property to force your app into a specific orientation. Here is an example of how to set your app to portrait orientation after a Page is created:

onCreationCompleted: { 
    OrientationSupport.supportedDisplayOrientation = 
        SupportedDisplayOrientation.DisplayPortrait;  
    }

These properties specify which orientations are allowed and which orientations aren't allowed for your app. It's still up to you to define how the app behaves when there is an orientation change. This is covered in the next sections.

Responding to orientation changes

Your application orients itself automatically if it's set to handle orientation changes through the bar-descriptor.xml file (Auto-orient) or programatically (SupportedDisplayOrientation::All). To ensure the UI looks great for all orientations, the position, size, and number of items displayed may need to be adjusted to fit the width and height of the current orientation. During an orientation change, a sequence of property changes and emitted signals occurs that you can use to adjust the UI or control the behavior of your app. This sequence and your options are:

1. The user starts rotating the device and the displayDirection property is about to change.

  • The displayDirectionAboutToChange() signal is emitted, even if the rotation is 180 degrees (meaning there is no change to the orientation property).
  • No UI rotation occurs yet so you can update the UI in response to the displayDirectionAboutToChange() signal.
  • You can stop the orientation change by setting the supportedDisplayOrientation property to a value that's incompatible with the upcoming orientation, such as SupportedDisplayOrientation::CurrentLocked.

2. Rotation of the device continues and the displayDirection property changes

  • The displayDirectionChanged() signal is emitted.
  • After this point, updates to the UI controls on the screen are visible by the user only after the rotation occurs. For example, if you change the color of a control after this point, the user won't see the change until after the rotation occurs.

3. An actual orientation change of the UI is about to happen (a rotation that is not 180 degrees)

  • The orientationAboutToChange() signal is emitted.
  • The app can update the UI to fit the new orientation (for more information on how to do this, see the code samples below).

4. The orientation change occurs

  • After handling the orientationAboutToChange() signal, the visual rotation effect occurs and the updated scene is displayed.
  • The orientation property updates to reflect the new orientation.
  • The orientationChanged() signal is emitted.
  • The app can make more significant or time-consuming changes to the scene in the new orientation.

5. The rotation or orientation change completes

  • The rotationCompleted() signal is emitted.
  • The app can perform any activities needed after either the rotation or orientation change is finished.

You can use the OrientationHandler class to handle any orientation changes and adjust the UI. To use this class, add it to a Page in your app using the  attachedObjects property.

attachedObjects: [
    OrientationHandler {
     
    }
]

In OrientationHandler, you add code to handle the orientationAboutToChange() and orientationChanged() signals. Here is an example of how to handle the orientationAboutToChange() signal to modify UI elements in response to an orientation change:

attachedObjects: [
    OrientationHandler {
        onOrientationAboutToChange: {
            if (orientation == UIOrientation.Landscape) {
                // Changing the text and the paddings
                orientationtext.text = "landscape"
                rootContainer.leftPadding = 300 
                rootContainer.rightPadding = 300
                rootContainer.topPadding = 230
            } else {
                // Changing the text and the paddings
                orientationtext.text = "portrait"
                rootContainer.leftPadding = 100 
                rootContainer.rightPadding = 100
                rootContainer.topPadding = 300
            }
        }
    } // end of OrientationHandler
] // end of attachedObjects

You can also do the same thing in C++, using the OrientationSupport class. First, the orientationAboutToChange() signal is connected to a slot called onOrientationAboutToChange:

OrientationSupport::instance()
     .setSupportedDisplayOrientation(SupportedDisplayOrientation::All);

bool res = connect(
    &OrientationSupport::instance(),
    SIGNAL(orientationAboutToChange(
        bb::cascades::UIOrientation::Type)),
    this,
    SLOT(onOrientationAboutToChange(
        bb::cascades::UIOrientation::Type)));

Then the slot function specifies the actions to take during an orientation change:

void App::onOrientationAboutToChange(bb::cascades::UIOrientation::Type
                                     uiOrientation)
{
    // Change the text and padding to reflect a landscape orientation
    if(uiOrientation == UIOrientation::Landscape)
    {
        m_label->setText("Landscape");
        rootContainer->setLeftPadding(300);
        rootContainer->setRightPadding(300);
        rootContainer->setTopPadding(230);
    }
     
    // Change the text and padding to reflect a portrait orientation
    else
    {
        m_label->setText("Portrait");
        rootContainer->setLeftPadding(100);
        rootContainer->setRightPadding(100);
        rootContainer->setTopPadding(300);
    }
}

For a full code sample demonstrating how to respond to orientation changes, see Tutorial: Orientation changes.

Simplifying orientation handling

You can use a technique to make orientation handling simpler by binding various UI components to the orientationAboutToChange() signal. You do this by creating a custom property (for example, newOrientation) and a function that updates this property with the current orientation when it's called. You can use this property as a condition to select or configure UI components.

To ensure the UI is updated before the orientation change completes, the update function is called whenever the orientationAboutToChange() signal is emitted. The function looks like this:

function updateNewOrientation(orientation) {
    newOrientation = 
        (orientation == UIOrientation.Landscape ?
            "landscape" : "portrait");
}

Here is an example of how this technique is used to change the background color of a Container and the image contained within it:

import bb.cascades 1.2

Page {
    property string newOrientation;

    // Declare the update function
    function updateNewOrientation(orientation) {
        newOrientation = 
            (orientation == UIOrientation.Landscape ?
                "landscape" : "portrait");
    }

    Container {
        // Change background color based on orientation
        background: newOrientation == "landscape" ?
            Color.Green : Color.Blue
        
        ImageView {
            // Load different image resources based on orientation
            imageSource: "asset:///image_" + newOrientation + ".png"
        }
    }
    attachedObjects: [
        OrientationHandler {
              id: handler
            // Call update function to set new orientation
            onOrientationAboutToChange:
                updateNewOrientation(orientation)
        }
    ]
    // Call update function to set initial orientation
    onCreationCompleted: updateNewOrientation(handler.orientation)
}

The image selection here assumes the app has two assets loaded: image_landscape.png and image_portrait.png.

Supporting different orientation settings per page

In your application, you might want to support orientation changes on some pages but not on others. For example, you might have an application that you want to start in portrait orientation, but can also support both portrait and landscape orientations on other pages (such as the photo browser app that was mentioned earlier).

On the first page that your application displays, you use the onCreationCompleted signal handler to configure the application to support portrait orientation only:

onCreationCompleted: { 
    OrientationSupport.supportedDisplayOrientation = 
        SupportedDisplayOrientation.DisplayPortrait;  
    }

On pages that support both landscape and portrait orientation, you set the supportedDisplayOrientation to SupportedDisplayOrientation.All:

onCreationCompleted: {
    OrientationSupport.supportedDisplayOrientation = 
        SupportedDisplayOrientation.All;
    }

If you're using a NavigationPane to navigate between pages, you can use the onTopChanged signal handler to change the orientation settings when the user navigates back to the first page. We can't use the onCreationCompleted signal handler anymore (because the page is already created), so we need to listen to the topChanged() signal that occurs when the user navigates to or from pages in the NavigationPane. In the onTopChanged signal handler, we set our orientation preferences to support portrait orientation only.

onTopChanged: {
    OrientationSupport.supportedDisplayOrientation = 
        SupportedDisplayOrientation.DisplayPortrait;
    }

You can support orientation settings for other types of navigation (such as TabbedPane) and the same approach applies.

Screen showing different orientations

The following code samples demonstrate how to support different orientation settings in the same application. This code represents an app that contains a screen with a single image. When the image is tapped, the app displays the same image on a new page that supports orientation changes (SupportedDisplayOrientation.All). If you navigate back to the first page, the app switches to portrait orientation automatically (SupportedDisplayOrientation.DisplayPortrait) and the page doesn't support orientation changes.

Here, a NavigationPane and Page are defined to set portrait orientation in both the onCreationCompleted and onTopChanged signal handlers:

import bb.cascades 1.0
 
NavigationPane {
    id: navigationPane
    Page {
        id: mainpage
        content: Container {
            layout: DockLayout {
            }
            ImageView {
                imageSource: "niagara.png"
                preferredWidth: 500
                preferredHeight: 199
 
                verticalAlignment: VerticalAlignment.Center
                horizontalAlignment: HorizontalAlignment.Center
 
                gestureHandlers: [
                    TapHandler {
                        onTap: {
                            var newPage =
                                pageDefinition.createObject();
                            navigationPane.push(newPage);
                        }
                    }
                ]
            } // end of ImageView
        } // end of Container
        onCreationCompleted: {
            OrientationSupport.supportedDisplayOrientation = 
                SupportedDisplayOrientation.DisplayPortrait;
        }
         
        attachedObjects: [
            ComponentDefinition {
                id: pageDefinition
                source: "img.qml"
            }
        ]
    }
    onTopChanged: {
        if (page == mainpage) {
            OrientationSupport.supportedDisplayOrientation = 
                SupportedDisplayOrientation.DisplayPortrait;
        }
    } // end of Page 
} // end of NavigationPane 

In a separate QML file, a second page is defined that supports all orientations:

import bb.cascades 1.0
 
Page {
    onCreationCompleted: {
        OrientationSupport.supportedDisplayOrientation = 
            SupportedDisplayOrientation.All;
    }
    content: Container {
        layout: DockLayout {
        }
        ImageView {
            verticalAlignment: VerticalAlignment.Center
            horizontalAlignment: HorizontalAlignment.Center
 
            scalingMethod: ScalingMethod.AspectFit
            id: imagecontainer
            imageSource: "niagara.png"
        } // end of ImageView
    } // end of Container
} // end of Page

Handling square displays

Some devices, such as the BlackBerry Q10 smartphone, have a display where the width and height are equal (that is, the display is square and the aspect ratio is 1:1). For these types of devices, the supported orientation and the current orientation are fixed and do not change during the lifetime of the app. The relationships between SupportedDisplayOrientation values and UIOrientation values are:

Because the orientation doesn't change for devices with square displays, you should ensure your UI looks appropriate for the orientation specified when the app starts. You should also ensure that any UI elements needed by the user aren't hidden or misaligned when the UIOrientation value is fixed. For example, if the app sets SupportedDisplayOrientation to DisplayPortrait, UIOrientation becomes Portrait and required UI elements should work. If you create or adjust these elements when handling Landscape orientation only, they may not appear correctly.

Handling cards

If your app exports cards for use in other apps, the orientation behavior is slightly different. The parent app is always in charge of how orientation changes are handled, so the supportedDisplayOrientation property is read-only for your card and defaults to SupportedDisplayOrientation::All. All signals related to orientation are still emitted, just as they are for non-card apps, and your card must still respond to orientation changes.

For more information on handling orientation changes for your card, see Creating your own cards.

Related resources

 
 

Last modified: 2013-12-21

comments powered by Disqus