13 KiB
title | description | guide_group | order | related_docs | |||||||
---|---|---|---|---|---|---|---|---|---|---|---|
Layers | How to use standard Layer components to build an app's UI. | user-interfaces | 3 |
|
The Layer
and associated subclasses (such as TextLayer
and
BitmapLayer
) form the foundation of the UI for every Pebble watchapp or
watchface, and are added to a Window
to construct the UI's design. Each
Layer
type contains at least three basic elements:
-
Frame - contains the position and dimensions of the
Layer
, relative to the parent object. -
Bounds - contains the drawable bounding box within the frame. This allows only a portion of the layer to be visible, and is relative to the
Layer
frame. -
Update procedure - the function that performs the drawing whenever the
Layer
is rendered. The subclasses implement a convenience update procedure with additional data to achieve their specialization.
Layer Heirachy
Every app must consist of at least one Window
in order to successfully
launch. Mutiple Layer
objects are added as children of the Window
, which
itself contains a Layer
known as the 'root layer'. When the Window
is
rendered, each child Layer
is rendered in the order in which they were
added. For example:
static Window *s_main_window;
static BitmapLayer *s_background_layer;
static TextLayer *s_time_layer;
// Get the Window's root layer
Layer *root_layer = window_get_root_layer(s_main_window);
/* set up BitmapLayer and TextLayer */
// Add the background layer first, so that it is drawn behind the time
layer_add_child(root_layer, bitmap_layer_get_layer(s_background_layer));
// Add the time layer second
layer_add_child(root_layer, text_layer_get_layer(s_time_layer));
Once added to a Window
, the ordering of each Layer
cannot be modified,
but one can be placed at the front by removing and re-adding it to the heirachy:
// Bring a layer to the front
layer_remove_from_parent(s_some_layer);
layer_add_child(root_layer, s_some_layer);
Update Procedures
For creating custom drawing implementations, the basic Layer
update
procedure can be reassigned to one created by a developer. This takes the form
of a LayerUpdateProc
, and provides a [GContext
](Graphics Context
)
object which can be used for drawing primitive shapes, paths, text, and images.
Note: See {% guide_link graphics-and-animations %} for more information on drawing with the graphics context.
static void layer_update_proc(Layer *layer, GContext *ctx) {
// Custom drawing happens here
}
This function must then be assigned to the Layer
that will be drawn with it:
// Set this Layer's update procedure
layer_set_update_proc(s_some_layer, layer_update_proc);
The update procedure will be called every time the Layer
must be redrawn.
This is typically when any other Layer
requests a redraw, the Window
is
shown/hidden, the heirarchy changes, or a modal (such as a notification) appears.
The Layer
can also be manually marked as 'dirty', and will be redrawn at the
next opportunity (usually immediately):
// Request a redraw
layer_mark_dirty(s_some_layer);
Layer Subclasses
For convenience, there are multiple subclasses of Layer
included in the
Pebble SDK to allow developers to easily construct their app's UI. Each should
be created when the Window
is loading (using the .load
WindowHandler
)
and destroyed when it is unloading (using .the unload
WindowHandler
).
These are briefly outlined below, alongside a simple usage example split into three code snippets - the element declarations, the setup procedure, and the teardown procedure.
TextLayer
The TextLayer
is the most commonly used subclass of Layer
, and allows
apps to render text using any available font, with built-in behavior to handle
text color, line wrapping, alignment, etc.
static TextLayer *s_text_layer;
// Create a TextLayer
s_text_layer = text_layer_create(bounds);
// Set some properties
text_layer_set_text_color(s_text_layer, GColorWhite);
text_layer_set_background_color(s_text_layer, GColorBlack);
text_layer_set_overflow_mode(s_text_layer, GTextOverflowModeWordWrap);
text_layer_set_alignment(s_text_layer, GTextAlignmentCenter);
// Set the text shown
text_layer_set_text(s_text_layer, "Hello, World!");
// Add to the Window
layer_add_child(root_layer, text_layer_get_layer(s_text_layer));
// Destroy the TextLayer
text_layer_destroy(s_text_layer);
BitmapLayer
The BitmapLayer
provides an easy way to show images loaded into GBitmap
objects from an image resource. Images shown using a BitmapLayer
are
automatically centered within the bounds provided to bitmap_layer_create()
.
Read {% guide_link app-resources/images %} to learn more about using image
resources in apps.
Note: PNG images with transparency should use
bitmap
resource type, and use theGCompOpSet
compositing mode when being displayed, as shown below.
static BitmapLayer *s_bitmap_layer;
static GBitmap *s_bitmap;
// Load the image
s_bitmap = gbitmap_create_with_resource(RESOURCE_ID_EXAMPLE_IMAGE);
// Create a BitmapLayer
s_bitmap_layer = bitmap_layer_create(bounds);
// Set the bitmap and compositing mode
bitmap_layer_set_bitmap(s_bitmap_layer, s_bitmap);
bitmap_layer_set_compositing_mode(s_bitmap_layer, GCompOpSet);
// Add to the Window
layer_add_child(root_layer, bitmap_layer_get_layer(s_bitmap_layer));
// Destroy the BitmapLayer
bitmap_layer_destroy(s_bitmap_layer);
StatusBarLayer
If a user needs to see the current time inside an app (instead of exiting to the
watchface), the StatusBarLayer
component can be used to display this
information at the top of the Window
. Colors and separator display style can
be customized.
static StatusBarLayer *s_status_bar;
// Create the StatusBarLayer
s_status_bar = status_bar_layer_create();
// Set properties
status_bar_layer_set_colors(s_status_bar, GColorBlack, GColorBlueMoon);
status_bar_layer_set_separator_mode(s_status_bar,
StatusBarLayerSeparatorModeDotted);
// Add to Window
layer_add_child(root_layer, status_bar_layer_get_layer(s_status_bar));
// Destroy the StatusBarLayer
status_bar_layer_destroy(s_status_bar);
MenuLayer
The MenuLayer
allows the user to scroll a list of options using the Up and
Down buttons, and select an option to trigger an action using the Select button.
It differs from the other Layer
subclasses in that it makes use of a number
of MenuLayerCallbacks
to allow the developer to fully control how it renders
and behaves. Some minimum example callbacks are shown below:
static MenuLayer *s_menu_layer;
static uint16_t get_num_rows_callback(MenuLayer *menu_layer,
uint16_t section_index, void *context) {
const uint16_t num_rows = 5;
return num_rows;
}
static void draw_row_callback(GContext *ctx, const Layer *cell_layer,
MenuIndex *cell_index, void *context) {
static char s_buff[16];
snprintf(s_buff, sizeof(s_buff), "Row %d", (int)cell_index->row);
// Draw this row's index
menu_cell_basic_draw(ctx, cell_layer, s_buff, NULL, NULL);
}
static int16_t get_cell_height_callback(struct MenuLayer *menu_layer,
MenuIndex *cell_index, void *context) {
const int16_t cell_height = 44;
return cell_height;
}
static void select_callback(struct MenuLayer *menu_layer,
MenuIndex *cell_index, void *context) {
// Do something in response to the button press
}
// Create the MenuLayer
s_menu_layer = menu_layer_create(bounds);
// Let it receive click events
menu_layer_set_click_config_onto_window(s_menu_layer, window);
// Set the callbacks for behavior and rendering
menu_layer_set_callbacks(s_menu_layer, NULL, (MenuLayerCallbacks) {
.get_num_rows = get_num_rows_callback,
.draw_row = draw_row_callback,
.get_cell_height = get_cell_height_callback,
.select_click = select_callback,
});
// Add to the Window
layer_add_child(root_layer, menu_layer_get_layer(s_menu_layer));
// Destroy the MenuLayer
menu_layer_destroy(s_menu_layer);
ScrollLayer
The ScrollLayer
provides an easy way to use the Up and Down buttons to
scroll large content that does not all fit onto the screen at the same time. The
usage of this type differs from the others in that the Layer
objects that
are scrolled are added as children of the ScrollLayer
, which is then in turn
added as a child of the Window
.
The ScrollLayer
frame is the size of the 'viewport', while the content size
determines how far the user can scroll in each direction. The example below
shows a ScrollLayer
scrolling some long text, the total size of which is
calculated with graphics_text_layout_get_content_size()
and used as the
ScrollLayer
content size.
Note: The scrolled
TextLayer
frame is relative to that of its parent, theScrollLayer
.
static TextLayer *s_text_layer;
static ScrollLayer *s_scroll_layer;
GFont font = fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD);
// Find the bounds of the scrolling text
GRect shrinking_rect = GRect(0, 0, bounds.size.w, 2000);
char *text = "Example text that is really really really really really \
really really really really really really long";
GSize text_size = graphics_text_layout_get_content_size(text, font,
shrinking_rect, GTextOverflowModeWordWrap, GTextAlignmentLeft);
GRect text_bounds = bounds;
text_bounds.size.h = text_size.h;
// Create the TextLayer
s_text_layer = text_layer_create(text_bounds);
text_layer_set_overflow_mode(s_text_layer, GTextOverflowModeWordWrap);
text_layer_set_font(s_text_layer, font);
text_layer_set_text(s_text_layer, text);
// Create the ScrollLayer
s_scroll_layer = scroll_layer_create(bounds);
// Set the scrolling content size
scroll_layer_set_content_size(s_scroll_layer, text_size);
// Let the ScrollLayer receive click events
scroll_layer_set_click_config_onto_window(s_scroll_layer, window);
// Add the TextLayer as a child of the ScrollLayer
scroll_layer_add_child(s_scroll_layer, text_layer_get_layer(s_text_layer));
// Add the ScrollLayer as a child of the Window
layer_add_child(root_layer, scroll_layer_get_layer(s_scroll_layer));
// Destroy the ScrollLayer and TextLayer
scroll_layer_destroy(s_scroll_layer);
text_layer_destroy(s_text_layer);
ActionBarLayer
The ActionBarLayer
allows apps to use the familiar black right-hand bar,
featuring icons denoting the action that will occur when each button on the
right hand side is pressed. For example, 'previous track', 'more actions', and
'next track' in the built-in Music app.
For three or fewer actions, the ActionBarLayer
can be more appropriate than
a MenuLayer
for presenting the user with a list of actionable options. Each
action's icon must also be loaded into a GBitmap
object from app resources.
The example below demonstrates show to set up an ActionBarLayer
showing an
up, down, and checkmark icon for each of the buttons.
static ActionBarLayer *s_action_bar;
static GBitmap *s_up_bitmap, *s_down_bitmap, *s_check_bitmap;
// Load icon bitmaps
s_up_bitmap = gbitmap_create_with_resource(RESOURCE_ID_UP_ICON);
s_down_bitmap = gbitmap_create_with_resource(RESOURCE_ID_DOWN_ICON);
s_check_bitmap = gbitmap_create_with_resource(RESOURCE_ID_CHECK_ICON);
// Create ActionBarLayer
s_action_bar = action_bar_layer_create();
action_bar_layer_set_click_config_provider(s_action_bar, click_config_provider);
// Set the icons
action_bar_layer_set_icon(s_action_bar, BUTTON_ID_UP, s_up_bitmap);
action_bar_layer_set_icon(s_action_bar, BUTTON_ID_DOWN, s_down_bitmap);
action_bar_layer_set_icon(s_action_bar, BUTTON_ID_SELECT, s_check_bitmap);
// Add to Window
action_bar_layer_add_to_window(s_action_bar, window);
// Destroy the ActionBarLayer
action_bar_layer_destroy(s_action_bar);
// Destroy the icon GBitmaps
gbitmap_destroy(s_up_bitmap);
gbitmap_destroy(s_down_bitmap);
gbitmap_destroy(s_check_bitmap);