Create a custom component

Now that we've set up our project, let's create a custom component to represent the pieces of text in our app.

An example text note from the sample app.

Create the Note.qml file

The Poem Maker app creates a random, three-part poem each time the button is clicked. Each part of the poem is distinct, but they all share some common properties, such as size, background, and font. In addition, each part uses the same animations; they all appear horizontally from the left side of the screen, and disappear vertically off the top of the screen.

To encapsulate these properties, we can create a custom component in QML called Note to represent each of the three parts of the poem. This custom component includes layout information for each part (which we'll call a note from now on), visual properties such as the background and font, and animations. We can then use the Note component when we create the rest of the UI for the app.

Start by creating a new .qml file called Note.qml in the assets folder of your project (right-click the assets folder and click New > Other, then expand  BlackBerry and select QML File). In this file, we add the appropriate import statement so that we can use Cascades controls, and we create a top-level  Container to hold all of the components of our note. This container uses a dock layout to make it easy to center the text on the note, and we set the size that we'd like the note to be.

import bb.cascades 1.0
 
Container {
    id: noteContainer
    layout: DockLayout {}
    preferredWidth: 317
    preferredHeight: 154

Expose properties

There are two properties of a note that we want to be able to specify when we create the rest of our UI. We need to specify the poem text that should appear on each note. We also need to specify the starting point for the animation that occurs when each new note slides onto the screen (we'll talk more about this later). We create property aliases that are associated with the properties that we want to expose. For more information about property aliases, see Custom QML components.

In the aliases, poem_part is the id property of a  TextArea control that represents the text on the note, and show is the id of the animation that displays a new note on the screen:

property alias poem: poem_part.text
property alias showAnimStartX: show.fromX

Our Note component also includes a custom signal called newNote(). This signal is emitted when the animation that hides the note is finished, letting us know that it's time to set up a new note and show it on the screen. We'll handle this signal in our main.qml file later in the tutorial.

signal newNote; 

Add animations

Let's create the animations for our note now. Each note has two possible animations, both of which are simple translation animations. The first one hides the old note by translating it vertically off the top of the screen. This translation has a short duration of 500 ms, and uses an easing curve that starts animating quickly and then slows down as the animation proceeds. When this animation finishes, we want the newNote() signal to be emitted.

animations: [
    TranslateTransition {
        id: hide
        toY: -400
        duration: 500
        easingCurve: StockCurve.CubicOut
         
        // When the animation ends, emit the newNote() signal
        onEnded: {
            newNote ();
        }
    },

The second animation shows the new note by translating it onto the screen from the left. Earlier, we associated this animation's fromX property with the showAnimStartX property alias, which lets us specify the correct starting position of each note's show animation. This animation also has a short duration of 500 ms and uses a similar easing curve to the one that the hide animation uses:

    TranslateTransition {
        id: show
        fromX: 0
        toX: 0
        duration: 500
        easingCurve: StockCurve.QuarticOut
    }
]

We need one more component to handle animations for our note: an ImplicitAnimationController. You can use this component to enable or disable implicit animations, which are enabled by default. We need to disable implicit animations at certain times for our app to work properly, so we create an ImplicitAnimationController now and use it later in the tutorial. We associate the ImplicitAnimationController with our top-level container by using the attachedObjects list. To learn more about this list, see QML and C++ integration.

attachedObjects: [
    ImplicitAnimationController {
        id: offScreenController
    }
]

Add visual elements

Next, we add the note background to our layout by using an ImageView control:

ImageView {
    id: noteBackground
    imageSource: "asset:///images/line_background.png"
    preferredWidth: 317
    preferredHeight: 154
}

To complete the visual elements of our custom component, we add a container to hold the poem text of the note. This container uses padding on its left and right sides to create some space between the text and the edge of the note. The container is also centered inside the note:

Container {
    layout: DockLayout {}
    rightPadding: 10
    leftPadding: 10
     
    // Use layout properties to center the container
    horizontalAlignment: HorizontalAlignment.Fill
    verticalAlignment: VerticalAlignment.Center

We use a TextArea control for the poem text and specify properties such as maximum width, alignment, and font. We also make sure to give the TextArea an id. We'll use this id to set the text of the note in our main.qml file.

    TextArea {
        id: poem_part
        text: "text"
        maxWidth: noteBackground.preferredWidth - 10
        editable: false
        backgroundVisible: false
         
        // Apply a text style to create centered body text
        textStyle {
            base: SystemDefaults.TextStyles.BodyText
            textAlign: TextAlign.Center
        }
         
        // Use layout properties to center the text area
        horizontalAlignment: HorizontalAlignment.Fill
        verticalAlignment: VerticalAlignment.Center
    } // end of TextArea
} // end of Container for poem text

Create custom JavaScript functions

Our finished app doesn't actually create new Note components to make a new poem. Instead, we simply reuse the same Note components by performing the following actions each time the button is pressed:

  • Hide each note by calling a custom JavaScript function called hideNote(). This function starts the hide animation for the notes.
  • Emit the newNote() signal to indicate that we're ready to set up new notes.
  • Handle the newNote() signal by generating new text for each note and calling a custom JavaScript function called showNote(). This function moves the notes to their proper positions so they can be animated, and starts the show animation for the notes.

Let's create the hideNote() and showNote() functions now. The hideNote() function is straightforward and starts the hide animation if it's not already running:

function hideNote () {
    if (!hide.isPlaying ()) {
        hide.play ();
    }
}

The showNote() function is a bit more complicated. This function is responsible for moving the notes to their correct positions before starting the  show  animation. However, we don't want this movement itself to be animated; we want the notes to appear immediately in their starting positions. Here's where we use the ImplicitAnimationController that we created earlier. We turn off implicit animations before we move the notes, and then turn on implicit animations again after we finish:

    function showNote () {
        if (show.isPlaying ()) {
            show.stop ();
        }
       
        poem_part.minHeight = 0;
         
        // Disable implicit animations
        offScreenController.enabled = false;
         
        // Move the note
        translationX = -400;
        noteContainer.translationY = 0;
         
        // Enable implicit animations
        offScreenController.enabled = true;
         
        show.play ();
    }
} // end of top-level Container

It might seem strange that we use custom JavaScript functions to start the hide and show animations, instead of just starting the animations directly. Because we start the animations from our main.qml file, instead of from this Note.qml file, we can't use the id properties of the animations to refer to them. It's not possible to start animations using their id properties if the animations are located in a separate QML document.

At this point, we've finished creating the custom Note component for our app.

Last modified: 2013-12-21

comments powered by Disqus