Qt Reference Documentation

Orientation Specific Layouts

Overview

Application top level page definitions, and reusable component definitions, should use one QML layout definition for the layout structure. This single definition should include the layout design for separate Device Orientations and container Aspect Ratios. The reason for this is that performance during an orientation switch is critical, and it is therefore a good idea to ensure that all of the components needed by both orientations are loaded when the containing Page is loaded.

On the contrary, you should perform thorough tests if you choose to use a Loader to load additional QML that is needed in separate orientations, as this will affect the performance of the orientation change.

Examples

In order to enable layout animations between the orientations, the anchor definitions must reside within the same containing component. Therefore the structure of an page or a component should consist of a common set of child components, and a common set of anchor definitions (defined in a StateGroup):

 StateGroup {
     states: [
         State {
             when: (true)
             AnchorChanges { target: infoArea;  anchors.left: helper.left; anchors.top: helper.top }
             AnchorChanges { target: controlArea; anchors.right: helper.right }
 }
 ]
 }

and a collection of states (defined in another StateGroup) representing the different aspect ratios supported by the component.

 StateGroup {
     states: [
         State {
             when: (container.width / container.height) > 3.00
             AnchorChanges { target: infoArea; anchors.right: undefined; anchors.bottom: helper.bottom }
             AnchorChanges { target: frequencyArea; anchors.left: infoArea.right; anchors.right: helper.right; anchors.top: undefined; anchors.bottom: parent.bottom }
             AnchorChanges { target: controlArea; anchors.left: infoArea.right; anchors.top: helper.top; anchors.bottom: frequencyArea.top }
 },
 State {
 when: (container.width / container.height) > 1.50
 AnchorChanges { target: infoArea; anchors.right: undefined; anchors.bottom: frequencyArea.top }
 AnchorChanges { target: frequencyArea; anchors.left: helper.left; anchors.right: infoArea.right; anchors.top: undefined; anchors.bottom: parent.bottom }
 AnchorChanges { target: controlArea; anchors.left: infoArea.right; anchors.top: helper.top; anchors.bottom: helper.bottom }
 },
 State {
 when: (container.width / container.height) > 1.00
 AnchorChanges { target: infoArea; anchors.right: undefined; anchors.bottom: frequencyArea.top }
 AnchorChanges { target: frequencyArea; anchors.left: helper.left; anchors.right: helper.right; anchors.top: undefined; anchors.bottom: parent.bottom }
 AnchorChanges { target: controlArea; anchors.left: infoArea.right; anchors.top: helper.top; anchors.bottom: frequencyArea.top }
 },
 State {
 when: (true)
 AnchorChanges { target: infoArea;  anchors.right: helper.right; anchors.bottom: undefined }
 AnchorChanges { target: frequencyArea; anchors.left: helper.left; anchors.right: helper.right; anchors.top: infoArea.bottom; anchors.bottom: undefined }
 AnchorChanges { target: controlArea; anchors.left: helper.left; anchors.top: frequencyArea.bottom; anchors.bottom: helper.bottom }
 }
 ]
 }

One advantage of defining the layouts within states, rather than within the component definitions, is that it helps with maintainability, particularly when layout animations are also defined in the corresponding state transitions. (However note that orientation change animations are not possible on Symbian due to compatibility support for S60 applications).

Design Patterns

If a component, contained within a Page element, needs to be hosted in numerous different form factor definitions, then the layout states of the view should depend on the aspect ratio of the page (its immediate container). Similarly, different instances of a component might be situated within numerous different containers in a UI, and so its layout states should be determined by the aspect ratio of its parent. The conclusion is that layout states should in general follow the aspect ratio of the direct container (not the current Device Orientation). The only exception to this is of course the top level Window definition, which is handled by the framework, or some parameters that happen to depend specifically on the Device Orientation.

Within each layout State, you should define the relationships between items using native QML layout definitions. See below for more information. During transitions between the states (triggered by the top level orientation change), in the case of anchor layouts, AnchorAnimation elements can be used to control the transitions. In some cases, you can also use a NumberAnimation on e.g. the width of an item. Remember to avoid complex javascript calculations during each frame of animation. Using simple anchor definitions and anchor animations can help with this in the majority of cases.

There are a few additional cases to consider:

  • What if you have a single page that looks completely different between landscape and portrait, i.e. all of the child items are different? For each Page, have two child components, with separate layout definitions, and make one or other of the items have zero opacity in each state. You can use a cross-fade animation by simply applying a NumberAnimation transition to the opacity.
  • What if you have a single page that shares 30% or more of the same layout contents between portrait and landscape? In that case, consider having one component with landscape and portrait states, and a collection of separate child items whose opacity (or position) depends on the orientation state. This will enable you to use layout animations for the items that are shared between the orientations, whilst the other items are either faded in/out, or animated on/off screen.
  • What if you have two pages on a handheld device that need to be on screen at the same time, for example on a larger form factor device? In this case, notice that your view component will no longer be occupying the full screen. Therefore it's important to remember in all components (in particular, list delegate items) should depend on the size of the containing component width, not on the screen width. It may be necessary to set the width in a Component.onCompleted() handler in this case, to ensure that the list item delegate has been constructed before the value is set.
  • What if the two orientations take up too much memory to have them both in memory at once? Use a Loader if necessary, if you cannot keep both versions of the view in memory at once, but beware performance on the cross-fade animation during layout switch. One solution could be to have two "splash screen" items that are children of the Page, then you cross fade between those during rotation. Then you can use a Loader to load another child component that loads the actual model data to another child Item, and cross-fade to that when the Loader has completed.