Merge "docs: Training class for the Wearable UI Library" into klp-modular-docs

This commit is contained in:
Ricardo Cervera
2014-10-03 23:25:23 +00:00
committed by Android (Google) Code Review
17 changed files with 1276 additions and 0 deletions

View File

@@ -782,6 +782,34 @@ include the action bar on devices running Android 2.1 or higher."
</ul>
</li>
<li class="nav-section">
<div class="nav-section-header">
<a href="<?cs var:toroot ?>training/wearables/ui/index.html"
description="How to create custom user interfaces for wearable apps."
>Creating Custom UIs</a>
</div>
<ul>
<li>
<a href="<?cs var:toroot ?>training/wearables/ui/layouts.html">Defining Layouts</a>
</li>
<li>
<a href="<?cs var:toroot ?>training/wearables/ui/cards.html">Creating Cards</a>
</li>
<li>
<a href="<?cs var:toroot ?>training/wearables/ui/lists.html">Creating Lists</a>
</li>
<li>
<a href="<?cs var:toroot ?>training/wearables/ui/2d-picker.html">Creating a 2D Picker</a>
</li>
<li>
<a href="<?cs var:toroot ?>training/wearables/ui/confirm.html">Showing Confirmations</a>
</li>
<li>
<a href="<?cs var:toroot ?>training/wearables/ui/exit.html">Exiting Full-Screen Activities</a>
</li>
</ul>
</li>
<li class="nav-section">
<div class="nav-section-header">
<a href="<?cs var:toroot ?>training/wearables/data-layer/index.html"

View File

@@ -0,0 +1,181 @@
page.title=Creating a 2D Picker
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#add-page-grid">Add a Page Grid</a></li>
<li><a href="#implement-adapter">Implement a Page Adapter</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}design/wear/index.html">Android Wear Design Principles</a></li>
</ul>
</div>
</div>
<p>The <a href="{@docRoot}design/wear/structure.html#2DPicker">2D Picker</a> pattern in Android
Wear allows users to navigate and choose from a set of items shown as pages. The Wearable UI
Library lets you easily implement this pattern using a page grid, which is a layout manager
that allows users to scroll vertically and horizontally through pages of data.</p>
<p>To implement this pattern, you add a <code>GridViewPager</code> element to the layout
of your activity and implement an adapter that provides a set of pages by extending
the <code>FragmentGridPagerAdapter</code> class.</p>
<p class="note"><strong>Note:</strong> The <em>GridViewPager</em> sample in the Android SDK
demonstrates how to use the <code>GridViewPager</code> layout in your apps. This sample is
located in the <code>android-sdk/samples/android-20/wearable/GridViewPager</code> directory.</p>
<h2 id="add-page-grid">Add a Page Grid</h2>
<p>Add a <code>GridViewPager</code> element to your layout definition as follows:</p>
<pre>
&lt;android.support.wearable.view.GridViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</pre>
<p>You can use any of the techniques described in
<a href="{@docRoot}training/wearables/ui/layouts.html">Defining Layouts</a> to ensure that
your 2D picker works on both round and square devices.</p>
<h2 id="implement-adapter">Implement a Page Adapter</h2>
<p>A page adapter provides a set of pages to populate a <code>GridViewPager</code> component. To
implement this adapter, you extend the <code>FragmentGridPageAdapter</code> class from the
Wearable UI Library</p>
<p>For example, the <em>GridViewPager</em> sample in the Android SDK contains
the following adapter implementation that provides a set of static cards with custom background
images:</p>
<pre>
public class SampleGridPagerAdapter extends FragmentGridPagerAdapter {
private final Context mContext;
public SampleGridPagerAdapter(Context ctx, FragmentManager fm) {
super(fm);
mContext = ctx;
}
static final int[] BG_IMAGES = new int[] {
R.drawable.debug_background_1, ...
R.drawable.debug_background_5
};
// A simple container for static data in each page
private static class Page {
// static resources
int titleRes;
int textRes;
int iconRes;
...
}
// Create a static set of pages in a 2D array
private final Page[][] PAGES = { ... };
// Override methods in FragmentGridPagerAdapter
...
}
</pre>
<p>The picker calls <code>getFragment</code> and <code>getBackground</code> to retrieve the content
to display at each position of the grid:</p>
<pre>
// Obtain the UI fragment at the specified position
&#64;Override
public Fragment getFragment(int row, int col) {
Page page = PAGES[row][col];
String title =
page.titleRes != 0 ? mContext.getString(page.titleRes) : null;
String text =
page.textRes != 0 ? mContext.getString(page.textRes) : null;
CardFragment fragment = CardFragment.create(title, text, page.iconRes);
// Advanced settings (card gravity, card expansion/scrolling)
fragment.setCardGravity(page.cardGravity);
fragment.setExpansionEnabled(page.expansionEnabled);
fragment.setExpansionDirection(page.expansionDirection);
fragment.setExpansionFactor(page.expansionFactor);
return fragment;
}
// Obtain the background image for the page at the specified position
&#64;Override
public ImageReference getBackground(int row, int column) {
return ImageReference.forDrawable(BG_IMAGES[row % BG_IMAGES.length]);
}
</pre>
<p>The <code>getRowCount</code> method tells the picker how many rows of content are
available, and the <code>getColumnCount</code> method tells the picker how many columns
of content are available for each of the rows.</p>
<pre>
// Obtain the number of pages (vertical)
&#64;Override
public int getRowCount() {
return PAGES.length;
}
// Obtain the number of pages (horizontal)
&#64;Override
public int getColumnCount(int rowNum) {
return PAGES[rowNum].length;
}
</pre>
<p>The adapter implementation details depend on your particular set of pages. Each page provided
by the adapter is of type <code>Fragment</code>. In this example, each page is a
<code>CardFragment</code> instance that uses one of the default card layouts. However, you can
combine different types of pages in the same 2D picker, such as cards, action icons, and custom
layouts depending on your use cases.</p>
<div style="float:right;margin-left:25px;width:250px">
<img src="{@docRoot}wear/images/07_uilib.png" width="250" height="250" alt=""/>
<p class="img-caption" style="text-align:center"><strong>Figure 1:</strong>
The <em>GridViewPager</em> sample.</p>
</div>
<p>Not all rows need to have the same number of pages. Notice that in this example the number of
colums is different for each row. You can also use a <code>GridViewPager</code> component to
implement a 1D picker with only one row or only one column.</p>
<p><code>GridViewPager</code> provides support for scrolling in cards whose content does not fit
the device screen. This example configures each card to expand as required, so users can scroll
through the card's content. When users reach the end of a scrollable card, a swipe in the same
direction shows the next page on the grid, if one is available.</p>
<p>You can specify a custom background for each page with the <code>getBackground()</code> method.
When users swipe to navigate across pages, <code>GridViewPager</code> applies parallax
and crossfade effects between different backgrounds automatically.</p>
<h3>Assign an adapter instance to the page grid</h3>
<p>In your activity, assign an instance of your adapter implementation to the
<code>GridViewPager</code> component:</p>
<pre>
public class MainActivity extends Activity {
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
final GridViewPager pager = (GridViewPager) findViewById(R.id.pager);
pager.setAdapter(new SampleGridPagerAdapter(this, getFragmentManager()));
}
}
</pre>

View File

@@ -0,0 +1,169 @@
page.title=Creating Cards
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#card-fragment">Create a Card Fragment</a></li>
<li><a href="#card-layout">Add a Card to Your Layout</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}design/wear/index.html">Android Wear Design Principles</a></li>
</ul>
</div>
</div>
<p>Cards present information to users with a consistent look and feel across different apps.
This lesson shows you how to create cards in your Android Wear apps.</p>
<p>The Wearable UI Library provides implementations of cards specifically designed for wearable
devices. This library contains the <code>CardFrame</code> class, which wraps views inside
a card-styled frame with a white background, rounded corners, and a light-drop shadow.
<code>CardFrame</code> can only contain one direct child, usually a layout manager, to which
you can add other views to customize the content inside the card.</p>
<p>You can add cards to your app in two ways:</p>
<ul>
<li>Use or extend the <code>CardFragment</code> class.</li>
<li>Add a card inside a <code>CardScrollView</code> in your layout.</li>
</ul>
<p class="note"><strong>Note:</strong> This lesson shows you how to add cards to Android Wear
activities. Android notifications on wearable devices are also displayed as cards. For more
information, see <a href="{@docRoot}training/wearables/notifications/index.html">Adding Wearable
Features to Notifications</a>.</p>
<h2 id="card-fragment">Create a Card Fragment</h2>
<p>The <code>CardFragment</code> class provides a default card layout with a title, a
description, and an icon. Use this approach to add cards to your app if the default card layout
shown in figure 1 meets your needs.</p>
<img src="{@docRoot}wear/images/05_uilib.png" width="500" height="245" alt=""/>
<p class="img-caption"><strong>Figure 1.</strong> The default <code>CardFragment</code> layout.</p>
<p>To add a <code>CardFragment</code> to your app:</p>
<ol>
<li>In your layout, assign an ID to the element that contains the card</li>
<li>Create a <code>CardFragment</code> instance in your activity</li>
<li>Use the fragment manager to add the <code>CardFragment</code> instance to its container</li>
</ol>
<p>The following sample code shows the code for the screen display shown in Figure 1:</p>
<pre>
&lt;android.support.wearable.view.BoxInsetLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@drawable/robot_background"
android:layout_height="match_parent"
android:layout_width="match_parent">
&lt;FrameLayout
<strong>android:id="@+id/frame_layout"</strong>
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_box="bottom">
&lt;/FrameLayout>
&lt;/android.support.wearable.view.BoxInsetLayout>
</pre>
<p>The following code adds the <code>CardFragment</code> instance to the activity in Figure 1:</p>
<pre>
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wear_activity2);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
CardFragment cardFragment = CardFragment.create(getString(R.string.cftitle),
getString(R.string.cfdesc),
R.drawable.p);
fragmentTransaction.add(R.id.frame_layout, cardFragment);
fragmentTransaction.commit();
}
</pre>
<p>To create a card with a custom layout using <code>CardFragment</code>, extend this class
and override its <code>onCreateContentView</code> method.</p>
<h2 id="card-layout">Add a CardFrame to Your Layout</h2>
<p>You can also add a card directly to your layout definition, as shown in figure 2. Use this
approach when you want to define a custom layout for the card inside a layout definition file.</p>
<img src="{@docRoot}wear/images/04_uilib.png" width="500" height="248" alt=""/>
<p class="img-caption"><strong>Figure 2.</strong> Adding a <code>CardFrame</code> to your
layout.</p>
<p>The following layout code sample demonstrates a vertical linear layout with two elements. You
can create more complex layouts to fit the needs of your app.</p>
<pre>
&lt;android.support.wearable.view.BoxInsetLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@drawable/robot_background"
android:layout_height="match_parent"
android:layout_width="match_parent">
&lt;<strong>android.support.wearable.view.CardScrollView</strong>
android:id="@+id/card_scroll_view"
android:layout_height="match_parent"
android:layout_width="match_parent"
app:layout_box="bottom">
&lt;<strong>android.support.wearable.view.CardFrame</strong>
android:layout_height="wrap_content"
android:layout_width="fill_parent">
&lt;LinearLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:orientation="vertical"
android:paddingLeft="5dp">
&lt;TextView
android:fontFamily="sans-serif-light"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:text="@string/custom_card"
android:textColor="@color/black"
android:textSize="20sp"/>
&lt;TextView
android:fontFamily="sans-serif-light"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:text="@string/description"
android:textColor="@color/black"
android:textSize="14sp"/>
&lt;/LinearLayout>
&lt;/android.support.wearable.view.CardFrame>
&lt;/android.support.wearable.view.CardScrollView>
&lt;/android.support.wearable.view.BoxInsetLayout>
</pre>
<p>The <code>CardScrollView</code> element in the example layout above lets you assign gravity to
the card when its content is smaller than the container. This example aligns the card to the
bottom of the screen:</p>
<pre>
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wear_activity2);
CardScrollView cardScrollView =
(CardScrollView) findViewById(R.id.card_scroll_view);
cardScrollView.setCardGravity(Gravity.BOTTOM);
}
</pre>

View File

@@ -0,0 +1,166 @@
page.title=Showing Confirmations
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#confirmation-timers">Use Automatic Confirmation Timers</a></li>
<li><a href="#show-confirmation">Show Confirmation Animations</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}design/wear/index.html">Android Wear Design Principles</a></li>
</ul>
</div>
</div>
<p><a href="{@docRoot}design/wear/patterns.html#Countdown">Confirmations</a> in Android Wear apps
use the whole screen or a larger portion of it than those in handheld apps. This ensures that
users can see these confirmations by just glancing at the screen and that they have large enough
touch targets to cancel an action.</p>
<p>The Wearable UI Library helps you show confirmation animations and timers in your
Android Wear apps:</p>
<dl>
<dt><em>Confirmation timers</em></dt>
<dd>Automatic confirmation timers show users an animated timer that lets them cancel an action
they just performed.</dd>
<dt><em>Confirmation animations</em></dt>
<dd>Confirmation animations give users visual feedback when they complete an action.</dd>
</dl>
<p>The following sections show you how to implement these patterns.</p>
<h2 id="confirmation-timers">Use Automatic Confirmation Timers</h2>
<div style="float:right;margin-left:25px;width:230px;margin-top:10px">
<img src="{@docRoot}wear/images/09_uilib.png" width="230" height="230" alt=""/>
<p class="img-caption" style="text-align:center"><strong>Figure 1:</strong>
A confirmation timer.</p>
</div>
<p>Automatic confirmation timers let users cancel an action they just performed. When the user
performs the action, your app shows a button to cancel the action with a timer animation and
starts the timer. The user has the option to cancel the action until the timer finishes. Your app
gets notified if the user cancels the action and when the timer expires.</p>
<p>To show a confirmation timer when users complete an action in your app:</p>
<ol>
<li>Add a <code>DelayedConfirmationView</code> element to your layout.</li>
<li>Implement the <code>DelayedConfirmationListener</code> interface in your activity.</li>
<li>Set the duration of the timer and start it when the user completes an action.</li>
</ol>
<p>Add the <code>DelayedConfirmationView</code> element to your layout as follows:</p>
<pre>
&lt;android.support.wearable.view.DelayedConfirmationView
android:id="@+id/delayed_confirm"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/cancel_circle"
app:circle_border_color="@color/lightblue"
app:circle_border_width="4dp"
app:circle_radius="16dp">
&lt;/android.support.wearable.view.DelayedConfirmationView>
</pre>
<p>You can assign a drawable resource to display inside the circle with the
<code>android:src</code> attribute and configure the parameters of the circle directly on the
layout definition.</p>
<p>To be notified when the timer finishes or when users tap on it, implement the corresponding
listener methods in your activity:</p>
<pre>
public class WearActivity extends Activity implements
DelayedConfirmationView.DelayedConfirmationListener {
private DelayedConfirmationView mDelayedView;
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wear_activity);
mDelayedView =
(DelayedConfirmationView) findViewById(R.id.delayed_confirm);
mDelayedView.setListener(this);
}
&#64;Override
public void onTimerFinished(View view) {
// User didn't cancel, perform the action
}
&#64;Override
public void onTimerSelected(View view) {
// User canceled, abort the action
}
}
</pre>
<p>To start the timer, add the following code to the point in your activity where users select
an action:</p>
<pre>
// Two seconds to cancel the action
mDelayedView.setTotalTimeMs(2000);
// Start the timer
mDelayedView.start();
</pre>
<h2 id="show-confirmation">Show Confirmation Animations</h2>
<div style="float:right;margin-left:25px;width:200px">
<img src="{@docRoot}wear/images/08_uilib.png" width="200" height="200" alt=""/>
<p class="img-caption" style="text-align:center"><strong>Figure 2:</strong>
A confirmation animation.</p>
</div>
<p>To show a confirmation animation when users complete an action in your app, create an intent
that starts <code>ConfirmationActivity</code> from one of your activities. You can specify
one of the these animations with the <code>EXTRA_ANIMATION_TYPE</code> intent extra:</p>
<ul>
<li><code>SUCCESS_ANIMATION</code></li>
<li><code>FAILURE_ANIMATION</code></li>
<li><code>OPEN_ON_PHONE_ANIMATION</code></li>
</ul>
<p>You can also add a message that appears under the confirmation icon.</p>
<p>To use the <code>ConfirmationActivity</code> in your app, first declare this activity in your
manifest file:</p>
<pre>
&lt;manifest>
&lt;application>
...
&lt;activity
android:name="android.support.wearable.activity.ConfirmationActivity">
&lt;/activity>
&lt;/application>
&lt;/manifest>
</pre>
<p>Then determine the result of the user action and start the activity with an intent:</p>
<pre>
Intent intent = new Intent(this, ConfirmationActivity.class);
intent.putExtra(ConfirmationActivity.EXTRA_ANIMATION_TYPE,
ConfirmationActivity.SUCCESS_ANIMATION);
intent.putExtra(ConfirmationActivity.EXTRA_MESSAGE,
getString(R.string.msg_sent));
startActivity(intent);
</pre>
<p>After showing the confirmation animation, <code>ConfirmationActivity</code> finishes and your
activity resumes.</p>

View File

@@ -0,0 +1,110 @@
page.title=Exiting Full-Screen Activities
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#disable-swipe">Disable the Swipe-To-Dismiss Gesture</a></li>
<li><a href="#long-press">Implement the Long Press to Dismiss Pattern</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}design/wear/index.html">Android Wear Design Principles</a></li>
</ul>
</div>
</div>
<p>By default, users exit Android Wear activities by swiping from left to right. If the app
contains horizontally scrollable content, users first have to navigate to the edge of the
content and then swipe again from left to right to exit the app.</p>
<p>For more immersive experiences, like an app that can scroll a map in any direction, you can
disable the swipe to exit gesture in your app. However, if you disable it, you must implement
the long-press-to-dismiss UI pattern to let users exit your app using the
<code>DismissOverlayView</code> class from the Wearable UI Library.
You must also inform your users the first time they run your app that they can exit using
a long press.</p>
<p>For design guidelines on exiting Android Wear activities, see
<a href="{@docRoot}design/wear/structure.html#Custom">Breaking out of the card</a>.</p>
<h2 id="disable-swipe">Disable the Swipe-To-Dismiss Gesture</h2>
<p>If the user interaction model of your app interferes with the swipe-to-dismiss gesture,
you can disable it for your app. To disable the swipe-to-dismiss gesture in your app, extend
the default theme and set the <code>android:windowSwipeToDismiss</code> attribute to
<code>false</code>:</p>
<pre>
&lt;style name="AppTheme" parent="Theme.DeviceDefault">
&lt;item name="android:windowSwipeToDismiss">false&lt;/item>
&lt;/style>
</pre>
<p>If you disable this gesture, you must implement the long-press-to-dismiss UI pattern to let users
exit your app, as described in the next section.</p>
<h2 id="long-press">Implement the Long Press to Dismiss Pattern</h2>
<p>To use the <code>DissmissOverlayView</code> class in your activity, add this element to
your layout definition such that it covers the whole screen and is placed above all other views.
For example:</p>
<pre>
&lt;FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
&lt;!-- other views go here -->
&lt;android.support.wearable.view.DismissOverlayView
android:id="@+id/dismiss_overlay"
android:layout_height="match_parent"
android:layout_width="match_parent"/>
&lt;FrameLayout>
</pre>
<p>In your activity, obtain the <code>DismissOverlayView</code> element and set some introductory
text. This text is shown to users the first time they run your app to inform them that they
can exit the app using a long press gesture. Then use a <code>GestureDetector</code> to detect
a long press:</p>
<pre>
public class WearActivity extends Activity {
private DismissOverlayView mDismissOverlay;
private GestureDetector mDetector;
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
setContentView(R.layout.wear_activity);
// Obtain the DismissOverlayView element
mDismissOverlay = (DismissOverlayView) findViewById(R.id.dismiss_overlay);
mDismissOverlay.setIntroText(R.string.long_press_intro);
mDismissOverlay.showIntroIfNecessary();
// Configure a gesture detector
mDetector = new GestureDetector(this, new SimpleOnGestureListener() {
public void onLongPress(MotionEvent ev) {
mDismissOverlay.show();
}
});
}
// Capture long presses
&#64;Override
public boolean onTouchEvent(MotionEvent ev) {
return mDetector.onTouchEvent(ev) || super.onTouchEvent(ev);
}
}
</pre>
<p>When the system detects a long press gesture, <code>DismissOverlayView</code> shows an
<strong>Exit</strong> button, which terminates your activity if the user presses it.</p>

View File

@@ -0,0 +1,57 @@
page.title=Creating Custom UIs for Wear Devices
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>Dependencies and Prerequisites</h2>
<ul>
<li>Android Studio 0.8 or later and Gradle 0.12 or later</li>
</ul>
</div>
</div>
<p>User interfaces for wearable apps differ significantly from those built for handheld devices.
Apps for wearables should follow the Android Wear <a href="{@docRoot}design/wear/index.html">design
principles</a> and implement the recommended <a href="{@docRoot}design/wear/patterns.html">UI
patterns</a>, which ensure a consistent user experience across apps that is optimized for
wearables.</a>
<p>This class teaches you how to create custom UIs for your
<a href="{@docRoot}training/wearables/apps/creating.html">wearable apps</a> and
<a href="{@docRoot}training/wearables/apps/layouts.html#CustomNotifications">custom
notifications</a> that look good on any Android Wear device by implementing these
UI patterns:</p>
<ul>
<li>Cards</li>
<li>Countdowns and confirmations</li>
<li>Long press to dismiss</li>
<li>2D Pickers</li>
<li>Selection lists</li>
</ul>
<p>The Wearable UI Library, which is part of the Google Repository in the Android SDK,
provides classes that help you implement these patterns and create layouts that work on
both round and square Android Wear devices.</p>
<p class="note"><b>Note:</b> We recommend using Android Studio for Android Wear development
as it provides project setup, library inclusion, and packaging conveniences that aren't available
in ADT. This training assumes you are using Android Studio.</p>
<h2>Lessons</h2>
<dl>
<dt><a href="{@docRoot}training/wearables/ui/layouts.html">Defining Layouts</a></dt>
<dd>Learn how to create layouts that look good on round and square Android Wear devices.</dd>
<dt><a href="{@docRoot}training/wearables/ui/cards.html">Creating Cards</a></dt>
<dd>Learn how to create cards with custom layouts.</dd>
<dt><a href="{@docRoot}training/wearables/ui/lists.html">Creating Lists</a></dt>
<dd>Learn how to create lists that are optimized for wearable devices.</dd>
<dt><a href="{@docRoot}training/wearables/ui/2d-picker.html">Creating a 2D Picker</a></dt>
<dd>Learn how to implement the 2D Picker UI pattern to navigate through pages of data.</dd>
<dt><a href="{@docRoot}training/wearables/ui/confirm.html">Showing Confirmations</a></dt>
<dd>Learn how to display confirmation animations when users complete actions.</dd>
<dt><a href="{@docRoot}training/wearables/ui/exit.html">Exiting Full-Screen Activities</a></dt>
<dd>Learn how to implement the long-press-to-dismiss UI pattern to exit full-screen activities.</dd>
</dl>

View File

@@ -0,0 +1,261 @@
page.title=Defining Layouts
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#add-library">Add the Wearable UI Library</a></li>
<li><a href="#different-layouts">Specify Different Layouts for Square and Round Screens</a></li>
<li><a href="#same-layout">Use a Shape-Aware Layout</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}design/wear/index.html">Android Wear Design Principles</a></li>
</ul>
<h2>Video</h2>
<ul>
<li><a href="https://www.youtube.com/watch?v=naf_WbtFAlY">Full Screen Apps for Android Wear</a></li>
</ul>
</div>
</div>
<p>Wearables use the same layout techniques as handheld Android devices, but need to be designed
with specific constraints. Do not port functionality and the UI from a handheld app and expect a
good experience. For more information on how to design great wearable apps, read the
<a href="{@docRoot}design/wear/index.html">Android Wear Design Guidelines</a>.</p>
<p>When you create layouts for Android Wear apps, you need to account for devices with square
and round screens. Any content placed near the corners of the screen may be cropped on round
Android Wear devices, so layouts designed for square screens do not work well on round devices.
For a demonstration of this type of problem, see the video
<a href="https://www.youtube.com/watch?v=naf_WbtFAlY">Full Screen Apps for Android Wear</a>.</p>
<p>For example, figure 1 shows how the following layout looks on square and round screens:</p>
<img src="{@docRoot}wear/images/01_uilib.png" alt="" width="500" height="261"/>
<p class="img-caption"><strong>Figure 1.</strong> Demonstration of how a layout designed for
square screens does not work well on round screens.</p>
<pre>
&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
&lt;TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_square" /&gt;
&lt;/LinearLayout&gt;
</pre>
<p>The text does not display correctly on devices with round screens.</p>
<p>The Wearable UI Library provides two different approaches to solve this problem:</p>
<ul>
<li>Define different layouts for square and round devices. Your app detects the shape
of the device screen and inflates the correct layout at runtime.</li>
<li>Use a special layout included in the library for both square and round devices. This layout
applies different window insets depending on the shape of the device screen.</li>
</ul>
<p>You typically use the first approach when you want your app to look different depending on
the shape of the device screen. You use the second approach when you want to use a similar layout
on both screen shapes without having views cropped near the edges of round screens.</p>
<h2 id="add-library">Add the Wearable UI Library</h2>
<p>Android Studio includes the Wearable UI Library on your <code>wear</code> module by default
when you use the Project Wizard. To compile your project with this library, ensure that the
<em>Extras</em> &gt; <em>Google Repository</em> package is installed in
the Android SDK manager and that the following dependency is included in the
<code>build.gradle</code> file of your <code>wear</code> module:</p>
<pre>
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
<strong>compile 'com.google.android.support:wearable:+'</strong>
compile 'com.google.android.gms:play-services-wearable:+'
}
</pre>
<p>The <code>'com.google.android.support:wearable'</code> dependency is required to implement
the layout techniques shown in the following sections.</p>
<p><a href="/shareables/training/wearable-support-docs.zip">Download the full API
reference documentation</a> for the Wearable UI Library classes.</p>
<h2 id="different-layouts">Specify Different Layouts for Square and Round Screens</h2>
<p>The <code>WatchViewStub</code> class included in the Wearable UI Library lets you specify
different layout definitions for square and round screens. This class detects the screen shape
at runtime and inflates the corresponding layout.</p>
<p>To use this class for handling different screen shapes in your app:</p>
<ol>
<li>Add <code>WatchViewStub</code> as the main element of your activity's layout.</li>
<li>Specify a layout definition file for square screens with the <code>rectLayout</code>
attribute.</li>
<li>Specify a layout definition file for round screens with the <code>roundLayout</code>
attribute.</li>
</ol>
<p>Define your activity's layout as follows:</p>
<pre>
&lt;android.support.wearable.view.WatchViewStub
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/watch_view_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
<strong>app:rectLayout="@layout/rect_activity_wear"</strong>
<strong>app:roundLayout="@layout/round_activity_wear"</strong>>
&lt;/android.support.wearable.view.WatchViewStub>
</pre>
<p>Inflate this layout in your activity:</p>
<pre>
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wear);
}
</pre>
<p>Then create different layout definition files for square and round screens. In this example,
you need to create the files <code>res/layout/rect_activity_wear.xml</code> and
<code>res/layout/round_activity_wear.xml</code>. You define these layouts in the same way that
you create layouts for handheld apps, but taking into account the constraints of wearable devices.
The system inflates the correct layout at runtime depending on the screen shape.</p>
<h3>Accessing layout views</h3>
<p>The layouts that you specify for square or round screens are not inflated until
<code>WatchViewStub</code> detects the shape of the screen, so your app cannot access their views
immediately. To access these views, set a listener in your activity to be notified when
the shape-specific layout has been inflated:</p>
<pre>
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wear);
WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
&#64;Override public void onLayoutInflated(WatchViewStub stub) {
// Now you can access your views
TextView tv = (TextView) stub.findViewById(R.id.text);
...
}
});
}
</pre>
<h2 id="same-layout">Use a Shape-Aware Layout</h2>
<div style="float:right;margin-left:25px;width:250px">
<img src="{@docRoot}wear/images/02_uilib.png" width="250" height="250" alt=""/>
<p class="img-caption"><strong>Figure 2.</strong> Window insets on a round screen.</p>
</div>
<p>The <code>BoxInsetLayout</code> class included in the Wearable UI Library extends
{@link android.widget.FrameLayout} and lets you define a single layout that works for both square
and round screens. This class applies the required window insets depending on the screen shape
and lets you easily align views on the center or near the edges of the screen.</p>
<p>The gray square in figure 2 shows the area where <code>BoxInsetLayout</code> can automatically
place its child views on round screens after applying the required window insets. To be displayed
inside this area, children views specify the <code>layout_box</code> atribute with these values:
</p>
<ul>
<li>A combination of <code>top</code>, <code>bottom</code>, <code>left</code>, and
<code>right</code>. For example, <code>"left|top"</code> positions the child's left and top
edges inside the gray square in figure 2.</li>
<li>The <code>all</code> value positions all the child's content inside the gray square in
figure 2.</li>
</ul>
<p>On square screens, the window insets are zero and the <code>layout_box</code> attribute is
ignored.</p>
<img src="{@docRoot}wear/images/03_uilib.png" width="500" height="253" alt=""/>
<p class="img-caption"><strong>Figure 3.</strong> A layout definition that works on both
square and round screens.</p>
<p>The layout shown in figure 3 uses <code>BoxInsetLayout</code> and works on square and
round screens:</p>
<pre>
&lt;<strong>android.support.wearable.view.BoxInsetLayout</strong>
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
<strong>android:background="@drawable/robot_background"</strong>
android:layout_height="match_parent"
android:layout_width="match_parent"
<strong>android:padding="15dp"</strong>>
&lt;FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
<strong>android:padding="5dp"</strong>
<strong>app:layout_box="all"</strong>>
&lt;TextView
android:gravity="center"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:text="@string/sometext"
android:textColor="@color/black" />
&lt;ImageButton
android:background="@null"
android:layout_gravity="bottom|left"
android:layout_height="50dp"
android:layout_width="50dp"
android:src="@drawable/ok" />
&lt;ImageButton
android:background="@null"
android:layout_gravity="bottom|right"
android:layout_height="50dp"
android:layout_width="50dp"
android:src="@drawable/cancel" />
&lt;/FrameLayout>
&lt;/android.support.wearable.view.BoxInsetLayout>
</pre>
<p>Notice the parts of the layout marked in bold:</p>
<ul>
<li>
<p><code>android:padding="15dp"</code></p>
<p>This line assigns padding to the <code>BoxInsetLayout</code> element. Because the window
insets on round devices are larger than 15dp, this padding only applies to square screens.</p>
</li>
<li>
<p><code>android:padding="5dp"</code></p>
<p>This line assigns padding to the inner <code>FrameLayout</code> element. This padding applies
to both square and round screens. The total padding between the buttons and the window insets
is 20 dp on square screens (15+5) and 5 dp on round screens.</p>
</li>
<li>
<p><code>app:layout_box="all"</code></p>
<p>This line ensures that the <code>FrameLayout</code> element and its children are boxed inside
the area defined by the window insets on round screens. This line has no effect on square
screens.</p>
</li>
</ul>

View File

@@ -0,0 +1,304 @@
page.title=Creating Lists
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#add-list">Add a List View</a></li>
<li><a href="#layout-impl">Create a Layout Implementation for List Items</a></li>
<li><a href="#layout-def">Create a Layout Definition for Items</a></li>
<li><a href="#adapter">Create an Adapter to Populate the List</a></li>
<li><a href="#adapter-listener">Associate the Adapter and Set a Click Listener</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}design/wear/index.html">Android Wear Design Principles</a></li>
</ul>
</div>
</div>
<p>Lists let users select an item from a set of choices easily on wearable devices. This lesson
shows you how to create lists in your Android Wear apps.</p>
<p>The Wearable UI Library includes the <code>WearableListView</code> class, which is a list
implementation optimized for wearable devices..</p>
<p class="note"><strong>Note:</strong> The <em>Notifications</em> sample in the Android SDK
demonstrates how to use <code>WearableListView</code> in your apps. This sample is located in
the <code>android-sdk/samples/android-20/wearable/Notifications</code> directory.</p>
<p>To create a list in your Android Wear apps:</p>
<ol>
<li>Add a <code>WearableListView</code> element to your activity's layout definition.</li>
<li>Create a custom layout implementation for your list items.</li>
<li>Use this implementation to create a layout definition file for your list items.</li>
<div style="float:right;margin-left:25px;width:215px;margin-top:-20px">
<img src="{@docRoot}wear/images/06_uilib.png" width="200" height="200" alt=""/>
<p class="img-caption" style="text-align:center"><strong>Figure 3:</strong>
A list view on Android Wear.</p>
</div>
<li>Create an adapter to populate the list.</li>
<li>Assign the adapter to the <code>WearableListView</code> element.</li>
</ol>
<p>These steps are described in detail in the following sections.</p>
<h2 id="add-list">Add a List View</h2>
<p>The following layout adds a list view to an activity using a <code>BoxInsetLayout</code>, so
the list is displayed properly on both round and square devices:</p>
<pre>
&lt;android.support.wearable.view.BoxInsetLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@drawable/robot_background"
android:layout_height="match_parent"
android:layout_width="match_parent">
&lt;FrameLayout
android:id="@+id/frame_layout"
android:layout_height="match_parent"
android:layout_width="match_parent"
app:layout_box="left|bottom|right">
&lt;<strong>android.support.wearable.view.WearableListView</strong>
android:id="@+id/wearable_list"
android:layout_height="match_parent"
android:layout_width="match_parent">
&lt;/android.support.wearable.view.WearableListView>
&lt;/FrameLayout>
&lt;/android.support.wearable.view.BoxInsetLayout>
</pre>
<h2 id="layout-impl">Create a Layout Implementation for List Items</h2>
<p>In many cases, each list item consists of an icon and a description. The
<em>Notifications</em> sample from the Android SDK implements a custom layout that extends
{@link android.widget.LinearLayout} to incorporate these two elements inside each list item.
This layout also implements the methods in the <code>WearableListView.Item</code> interface
to animate the item's icon and fade the text in response to events from
<code>WearableListView</code> as the user scrolls through the list.</p>
<pre>
public class WearableListItemLayout extends LinearLayout
implements WearableListView.Item {
private final float mFadedTextAlpha;
private final int mFadedCircleColor;
private final int mChosenCircleColor;
private ImageView mCircle;
private float mScale;
private TextView mName;
public WearableListItemLayout(Context context) {
this(context, null);
}
public WearableListItemLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WearableListItemLayout(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
mFadedTextAlpha = getResources()
.getInteger(R.integer.action_text_faded_alpha) / 100f;
mFadedCircleColor = getResources().getColor(R.color.grey);
mChosenCircleColor = getResources().getColor(R.color.blue);
}
// Get references to the icon and text in the item layout definition
&#64;Override
protected void onFinishInflate() {
super.onFinishInflate();
// These are defined in the layout file for list items
// (see next section)
mCircle = (ImageView) findViewById(R.id.circle);
mName = (TextView) findViewById(R.id.name);
}
// Provide scaling values for WearableListView animations
&#64;Override
public float getProximityMinValue() {
return 1f;
}
&#64;Override
public float getProximityMaxValue() {
return 1.6f;
}
&#64;Override
public float getCurrentProximityValue() {
return mScale;
}
// Scale the icon for WearableListView animations
&#64;Override
public void setScalingAnimatorValue(float scale) {
mScale = scale;
mCircle.setScaleX(scale);
mCircle.setScaleY(scale);
}
// Change color of the icon, remove fading from the text
&#64;Override
public void onScaleUpStart() {
mName.setAlpha(1f);
((GradientDrawable) mCircle.getDrawable()).setColor(mChosenCircleColor);
}
// Change the color of the icon, fade the text
&#64;Override
public void onScaleDownStart() {
((GradientDrawable) mCircle.getDrawable()).setColor(mFadedCircleColor);
mName.setAlpha(mFadedTextAlpha);
}
}
</pre>
<h2 id="layout-def">Create a Layout Definition for Items</h2>
<p>After you implement a custom layout for list items, you provide a layout definition file that
specifies the layout parameters of each of the components inside a list item. The following layout
definition uses the custom layout implementation from the previous section and defines an icon
and a text view whose IDs match those in the layout implementation class:</p>
<pre>
&lt;-- res/layout/list_item.xml -->
&lt;com.example.android.support.wearable.notifications.WearableListItemLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="80dp">
&lt;ImageView
android:id="@+id/circle"
android:layout_height="20dp"
android:layout_margin="16dp"
android:layout_width="20dp"
android:src="@drawable/wl_circle"/>
&lt;TextView
android:id="@+id/name"
android:gravity="center_vertical|left"
android:layout_width="wrap_content"
android:layout_marginRight="16dp"
android:layout_height="match_parent"
android:fontFamily="sans-serif-condensed-light"
android:lineSpacingExtra="-4sp"
android:textColor="@color/text_color"
android:textSize="16sp"/>
&lt;/com.example.android.support.wearable.notifications.WearableListItemLayout>
</pre>
<h2 id="adapter">Create an Adapter to Populate the List</h2>
<p>The adapter populates the <code>WearableListView</code> with content. The following simple
adapter populates the list with elements based on an array of strings:</p>
<pre>
private static final class Adapter extends WearableListView.Adapter {
private String[] mDataset;
private final Context mContext;
private final LayoutInflater mInflater;
// Provide a suitable constructor (depends on the kind of dataset)
public Adapter(Context context, String[] dataset) {
mContext = context;
mInflater = LayoutInflater.from(context);
mDataset = dataset;
}
// Provide a reference to the type of views you're using
public static class ItemViewHolder extends WearableListView.ViewHolder {
private TextView textView;
public ItemViewHolder(View itemView) {
super(itemView);
// find the text view within the custom item's layout
textView = (TextView) itemView.findViewById(R.id.name);
}
}
// Create new views for list items
// (invoked by the WearableListView's layout manager)
&#64;Override
public WearableListView.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
// Inflate our custom layout for list items
return new ItemViewHolder(mInflater.inflate(R.layout.list_item, null));
}
// Replace the contents of a list item
// Instead of creating new views, the list tries to recycle existing ones
// (invoked by the WearableListView's layout manager)
&#64;Override
public void onBindViewHolder(WearableListView.ViewHolder holder,
int position) {
// retrieve the text view
ItemViewHolder itemHolder = (ItemViewHolder) holder;
TextView view = itemHolder.textView;
// replace text contents
view.setText(mDataset[position]);
// replace list item's metadata
holder.itemView.setTag(position);
}
// Return the size of your dataset
// (invoked by the WearableListView's layout manager)
&#64;Override
public int getItemCount() {
return mDataset.length;
}
}
</pre>
<h2 id="adapter-listener">Associate the Adapter and Set a Click Listener</h2>
<p>In your activity, obtain a reference to the <code>WearableListView</code> element from
your layout, assign an instance of the adapter to populate the list, and set a click listener
to complete an action when the user selects a particular list item.</p>
<pre>
public class WearActivity extends Activity
implements WearableListView.ClickListener {
// Sample dataset for the list
String[] elements = { "List Item 1", "List Item 2", ... };
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_list_activity);
// Get the list component from the layout of the activity
WearableListView listView =
(WearableListView) findViewById(R.id.wearable_list);
// Assign an adapter to the list
listView.setAdapter(new Adapter(this, elements));
// Set a click listener
listView.setClickListener(this);
}
// WearableListView click listener
&#64;Override
public void onClick(WearableListView.ViewHolder v) {
Integer tag = (Integer) v.itemView.getTag();
// use this data to complete some action ...
}
&#64;Override
public void onTopEmptyRegionClick() {
}
}
</pre>

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB