Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d047c0e0 authored by Ricardo Cervera's avatar Ricardo Cervera Committed by Android Git Automerger
Browse files

am c2b35efb: am 0e61e44d: am 2a02b881: Merge "docs: Training class for the...

am c2b35efb: am 0e61e44d: am 2a02b881: Merge "docs: Training class for the Wearable UI Library" into klp-modular-docs

* commit 'c2b35efb':
  docs: Training class for the Wearable UI Library
parents 15174523 c2b35efb
Loading
Loading
Loading
Loading
+28 −0
Original line number Diff line number Diff line
@@ -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"
+181 −0
Original line number Diff line number Diff line
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>
+169 −0
Original line number Diff line number Diff line
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>
+166 −0
Original line number Diff line number Diff line
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>
+110 −0
Original line number Diff line number Diff line
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>
 No newline at end of file
Loading