Sample

你在这里

你在这里

Page Example

Layout transitions Sample Overview

The Layout Transitions sample application demonstrates how you can implement an animated transition between different application views. The application implements 3 different screens: A, B, and C. The animation implementation is based on 3 EFL mechanisms: elm_transitions, Ecore_Animator, and Edje.

The following figure illustrates the application views.

Figure: Layout Transitions screens

The application is divided into 3 modules:

Main module is generated by the Tizen SDK and contains all callbacks from the app_control library necessary to run the application.

View module is responsible for user interactions and UI creation.

Animator module is responsible for animations between different views.

Implementation

Main Module

The main module contains the code automatically generated by the Tizen SDK when you create a new native project with EDJE files. The module initializes an application instance and handles app control event callbacks.

The following updates have been introduced to the main module:

The main data structure is modified. All pointers to elementary components are placed in an internal s_view_data structure. The module does not access the UI components directly.

typedef struct
appdata
{
s_view_data view;
} appdata_s;

All functions connected to the UI element creation are placed in the view_init() function from the view module:

View Module

The view module implements all UI functionality. The following figure illustrates the application layout structure and components tree.

Figure: Layout structure and component tree

To create the view module:

The view is initialized in the view_init() function. Each component is created in a separate function:

bool
view_init(s_view_data *view)
{
// Create the main window of the application and set its properties
// This is the standard implementation from the UI sample, so it is
// not described in detail
if (!_create_win(view))
{
// Do something
}
// Create the conformant component
// It is also standard code generated from the UI sample
if (!_create_conformant(view))
{
// Do something
}
// Create the elm_layout component. This layout loads an EDJE file,
// which contains proper placeholders for the toolbar component and
// the A, B, and C screens
if (!_create_layout(view))
{
// Do something
}
// Create the toolbar component that is used for switching between the
// different application views
if (!_create_toolbar(view))
{
// Do something
}
}

The default page view consists of labels and 2 sets of elm_radio buttons. The elm_radio selectors are responsible for setting the animation type for the screens A and C. They are created in the _create_animation_selectors(), _fill_animation_selectors(), and _create_radio_button() functions:

static bool
_create_animation_selectors(s_view_data *view)
{
// Create a container for the first group of the selectors
view->group_1_box = elm_box_add(view->b_page);
// Insert the box in the proper swallow parts and set its parameters
elm_object_part_content_set(view->b_page, PART_PAGE_DEFAULT_ANIM_TYPE_A, view->group_1_box);
evas_object_size_hint_weight_set(view->group_1_box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_show(view->group_1_box);
elm_box_align_set(view->group_1_box, 0.5, 0.1);
// Create a container for the second group of the selectors
view->group_2_box = elm_box_add(view->b_page);
// Add items to the created containers
if (!_fill_animation_selectors(view))
{
}
static bool
_fill_animation_selectors(s_view_data *view)
{
// Declare helper variables for handling 2 different groups of radio buttons
int r_id_1 = 0;
int r_id_2 = 0;
int i = 0;
// Create the elm_radio component and define the first group of the buttons
view->radio_gr_1 = __create_radio_button(view, view->group_1_box, radio_names[0], r_id_1++);
if (!view->radio_gr_1)
return false;
// Create the elm_radio component and define the second group of the buttons
view->radio_gr_2 = __create_radio_button(view, view->group_2_box, radio_names[0], r_id_2++);
if (!view->radio_gr_2)
return false;
// Create radio buttons for each type of animation and attach the created components to the group
for (i = 1; i < ANIM_TYPES_CNT; i++)
{
radio = __create_radio_button(view, view->group_1_box, radio_names[i], r_id_1++);
if (!radio)
return false;
elm_radio_group_add(radio, view->radio_gr_1);
radio = __create_radio_button(view, view->group_2_box, radio_names[i], r_id_2++);
if (!radio)
return false;
elm_radio_group_add(radio, view->radio_gr_2);
}
// Variable indicating which radio button is checked
elm_radio_value_pointer_set(view->radio_gr_1, &(view->actual_indice_scr_a));
elm_radio_value_pointer_set(view->radio_gr_2, &(view->actual_indice_scr_c));
}

The view animations are invoked in callbacks attached to the elm_toolbar component. Each "activate" function calls an external function from the animator module with proper parameters.

The functions for switching the current view between B and C screens are quite similar.

static void
_screen_a_activate(void *data, Evas_Object *obj, void *event_info)
{
Evas_Object *current_page = NULL;
animator_type_t tmp = ANIMATION_TYPE_NONE;
s_view_data *view = (s_view_data*) data;
// Check the active view type
// If the user clicks the same tab in the toolbar twice, this function must be stopped
if (view->active_view == ACTIVE_VIEW_TYPE_SCR_A)
{
return;
}
// When a specific view is displayed for the first time, a new page must be created
// Implementation is described later
if (!view->a_page)
{
if (!_create_view_page(view, PAGE_TYPE_A))
{
}
}
// Unset the current view of the main layout and set a new one
current_page = elm_object_part_content_unset(view->layout, PART_PAGE);
elm_object_part_content_set(view->layout, PART_PAGE, view->a_page);
evas_object_show(view->a_page);
// Check the selected animation type (implementation is described below)
__set_animations_type(view);
// Invoke the animator function for switching the view
if (view->animator_scr_a != ANIMATION_TYPE_NONE)
{
tmp = view->animator_scr_c;
view->animator_scr_c = ANIMATION_TYPE_NONE;
animator_page_switch(current_page, view->a_page,
view->animator_scr_a, view->animator_scr_c);
view->animator_scr_c = tmp;
}
// If no animation is selected, hide the visible page to show another screen
else
{
evas_object_hide(current_page);
}
// Set the active view type
view->active_view = ACTIVE_VIEW_TYPE_SCR_A;
}

The __set_animations_type() function checks which of the radio buttons is selected and sets the valid animation type:

Animator Module

The animator module is responsible for smooth transitions between the screens. The 3 different methods provided by the EFL and Elementary APIs are used to demonstrate how the view can be changed:

elm_transitions

Ecore_Animator

Animations in the EDJE file

Using the elm_transition

The Elementary transition mechanism is used to prepare a wipe animation. Only the current page pointer is needed to create this animation. The Elm_Transit_Effect_Wipe_Dir parameter is used to select the wipe direction. If the next page is screen A, the wipe direction is set to ELM_TRANSIT_EFFECT_WIPE_DIR_LEFT, otherwise the dir value is equal to ELM_TRANSIT_EFFECT_WIPE_DIR_RIGHT.

Using the Ecore_Animator

The Ecore_Animator is used for resize and fade animations. The following example shows the resize animation. The fade animation is similar, and its implementation is omitted. The Ecore_Animator mechanism requires a callback, which is invoked when a new animator object is created.

To create a resize animation:

The _start_resize_animation() function creates a new object and sets parameters for the callback which changes the object properties.

The implementation for the callback which animates the transit between the pages takes 2 parameters: the data pointer which is passed when the animator object is created and the position value. It is a double value range from 0.0 to 1.0, and it is used to acquire the progress of the current animation.

static Eina_Bool
_resize_animator_timeline_cb(void *data, double pos)
{
// Resize the animation: maximize the next page and minimize the current one
// Second parameter is needed with the value opposite to the pos value
double frame = 1.0 - pos;
animation_data_t *anim_data = (animation_data_t*) data;
// Set new size and position values for the current page
new_c_w = anim_data->w * frame;
new_c_h = anim_data->h * frame;
new_c_x = (anim_data->x + (double)(anim_data->w - new_c_w)/2);
new_c_y = (anim_data->y + (double)(anim_data->h - new_c_h)/2);
// Set new size and position values for the next page
new_n_w = anim_data->w * pos;
new_n_h = anim_data->h * pos;
new_n_x = (anim_data->x + (double)(anim_data->w - new_n_w)/2);
new_n_y = (anim_data->y + (double)(anim_data->h - new_n_h)/2);
// Use new values to change the parameters of the Evas_Object
evas_object_resize(anim_data->c_obj, new_c_w, new_c_h);
// Animation must resize the object from the center of the screen, so it
// must be moved after it is resized
evas_object_move(anim_data->c_obj, new_c_x, new_c_y);
// Make a couple of additional changes to smoothen the disappearing effect
evas_object_color_set(anim_data->c_obj, 255, 255, 255, 255 * frame);
// Use the same functions for the next page animation
evas_object_resize(anim_data->n_obj, new_n_w, new_n_h);
evas_object_move(anim_data->n_obj, new_n_x, new_n_y);
evas_object_color_set(anim_data->n_obj, 255, 255, 255, 255 * pos);
// If the pos parameter equals 1.0, it is the last animation tick, so the
// animation data is freed and the last properties for the next page are set
if (pos == 1.0)
{
evas_object_hide(anim_data->c_obj);
evas_object_color_set(anim_data->c_obj, 255, 255, 255, 255);
free(anim_data);
return ECORE_CALLBACK_CANCEL;
}
}

Using EDJE for Animations

If the Edje API is used for the animation, all logic responsible for changing the state of the view is implemented in the .edc script file. To start the animation, the elm_object_signal_emit() function is used. If the application must be informed of the end of the animation, an event listener must be set for a proper signal.

The _split_anim_done_cb() callback function is used only to hide the current page when the animation is finished.

The implementation of the animation in the EDJE layout file is very simple. If the EDJE file consists of parts, you only need to define the custom states for them and programs that react for proper events.

The split animation splits the page horizontally and moves the parts from the top of the screen up and parts from the bottom of the screen down. The EDJE file for the screen B consists of:

Part for the main title at the top of the page

Labels for the "Screen-A" and "Screen-C" animation types

Swallow parts for the elm_hoversel components

The following example describes the animation implementation for the main title. The implementation for other parts is similar.

To change the state of the part and animate its position change, a program is used in the EDJE file:

program
{
name: "split_anim_start";
// Program is executed when the EDJE file receives the SIGNAL_SPLIT_HORIZONTAL
// signal from the SIGNAL_SOURCE
signal: SIGNAL_SPLIT_HORIZONTAL;
source: SIGNAL_SOURCE;
// Define the program action. In this case, STATE_SET means that the state of the
// part is changed to "split_animation" 0.0
action: STATE_SET "split_animation" 0.0;
// Define the program target
target: PART_PAGE_TITLE;
// Define the program to be called after the "split_anim_start" program
after: "animation_done";
// Set the animation type to DECELERATE and its duration time to 0.5 seconds
transition: DECELERATE 0.5;
}

Each program defined in the EDJE file can perform only 1 action. If 1 program must be connected to more than 1 action property, the after element must be used to define the next action. In this sample, after invokes the animation_done program:

program
{
name: "animation_done";
// This action emits a signal to inform that the animation handled in the animator module is finished
action: SIGNAL_EMIT SIGNAL_SPLIT_ANIM_DONE SIGNAL_SOURCE;
}