• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1
2page.title=Curved Layout
3meta.tags="wear", "wear-preview", "RecyclerView"
4page.tags="wear"
5
6@jd:body
7
8
9<div id="qv-wrapper">
10<div id="qv">
11
12    <h2>In this document</h2>
13    <ol>
14      <li><a href="#creating">Creating a Curved Layout</a></li>
15      <li><a href="#adding">Adding a Circular Scrolling Gesture</a></li>
16      <li><a href="#aligning">Anchoring Children to the Curve</a></li>
17    </ol>
18
19</div>
20</div>
21
22
23<p>
24  Wear 2.0 introduces the {@code WearableRecyclerView} class for displaying
25  and manipulating a vertical list of items optimized for round displays.
26  {@code WearableRecyclerView} extends the existing
27  <a href="{@docRoot}reference/android/support/v7/widget/RecyclerView.html">{@code RecyclerView}</a>
28  class to provide a curved layout and a circular scrolling gesture in wearable apps.
29</p>
30<img src="https://android-dot-devsite.googleplex.com/wear/preview/images/wrv_new.png"
31      style="float:right;margin:10px 20px 0 0">
32
33<p>
34  You can adapt to this interface in your wearable app by creating a new
35  {@code WearableRecyclerView} container.
36</p>
37
38<p>
39  You should decide whether to use a {@code WearableRecyclerView}, based on
40  the kind of user experience you want to provide. We recommend using the
41  {@code WearableRecyclerView} for a simple, long list of items, such as an
42  application launcher, or a list contacts. Each item might have a short string
43  and an associated icon. Alternatively, each item might have only a string or
44  an icon. We do not recommend using a {@code WearableRecyclerView} for short
45  or complex lists.
46</p>
47
48<p>
49  This document describes how to create a curved layout for your scrollable items
50  and properly align them along the curve.
51</p>
52
53
54<h2 id="creating">Creating a Curved Layout</h2>
55<p>To create a curved layout for scrollable items in your wearable app:
56</p>
57<ul>
58  <li>Use {@code WearableRecyclerView} as your main container in the relevant
59      xml layout.
60  </li>
61
62  <li>By default, {@code WearableRecyclerView} uses the {@code
63    DefaultOffsettingHelper} class to offset items in a curved layout on round
64    devices. If you wish to implement your own offsetting logic, you can extend the
65    abstract {@code WearableRecyclerView.OffsettingHelper} class and attach it to
66    the {@code WearableRecyclerView} using {@code
67    WearableRecyclerView.setOffsettingHelper} method.
68
69    <pre>
70      CircularOffsettingHelper circularHelper = new CircularOffsettingHelper();
71      mRecyclerView.setOffsettingHelper(circularHelper);
72    </pre>
73
74    <pre>
75      public class CircularOffsettingHelper extends OffsettingHelper {
76
77        &#64;Override
78        public void updateChild(View child, WearableRecyclerView parent) {
79          int progress = child.getTop() / parent.getHeight();
80          child.setTranslationX(-child.getHeight() * progress);
81         }
82      }
83    </pre>
84
85  </li>
86
87</ul>
88
89<p class="note">
90  <strong>Note:</strong> {@code DefaultOffsettingHelper} class
91  offsets the child items
92  along a predefined UX curve, but the operation can cut off part of the child
93  view if it is not scaled down accordingly. This is because the default curve
94  attempts to fit 5 items on the screen, regardless of their size.
95  If you do not wish to scale your items, you should consider additional padding.
96</p>
97
98<h3>Examples</h3>
99<p>
100  The following code example demonstrates how to add {@code WearableRecyclerView}
101   to a layout:
102</p>
103<pre>
104&lt;android.support.wearable.view.WearableRecyclerView
105   xmlns:android="http://schemas.android.com/apk/res/android"
106   xmlns:tools="http://schemas.android.com/tools"
107   android:id="&#64;+id/recycler_launcher_view"
108   android:layout_width="match_parent"
109   android:layout_height="match_parent"
110   android:scrollbars="vertical" /&gt;
111 </pre>
112
113
114<p>
115  To customize the appearance of the children while scrolling (for example,
116  scale the icons and text while the items scroll away from the center),  extend
117  the  {@code DefaultOffsettingHelper} and override the {@code updateChild }
118  method. It is important to call the {@code super.updateChild(child, parent)} to
119  offset the children along the curve. However, if for any particular child you do
120  not wish them to follow a curve, you can chose not to call the super method for
121  that particular child.
122</p>
123
124<pre>
125
126public class MyOffsettingHelper extends DefaultOffsettingHelper {
127
128   /** How much should we scale the icon at most. */
129   private static final float MAX_ICON_PROGRESS = 0.65f;
130
131   private float mProgressToCenter;
132
133   public OffsettingHelper() {}
134
135   &#64;Override
136
137   public void updateChild(View child,  WearableRecyclerView parent) {
138       super.updateChild(child, parent);
139
140
141       // Figure out % progress from top to bottom
142       float centerOffset = ((float) child.getHeight() / 2.0f) /  (float) mParentView.getHeight();
143       float yRelativeToCenterOffset = (child.getY() / mParentView.getHeight()) + centerOffset;
144
145       // Normalize for center
146       mProgressToCenter = Math.abs(0.5f - yRelativeToCenterOffset);
147       // Adjust to the maximum scale
148       mProgressToCenter = Math.min(mProgressToCenter, MAX_ICON_PROGRESS);
149
150       child.setScaleX(1 - mProgressToCenter);
151       child.setScaleY(1 - mProgressToCenter);
152   }
153}
154
155
156</pre>
157
158
159<h2 id="adding">Adding a Circular Scrolling Gesture</h2>
160
161<p>
162  By default, circular scrolling is disabled in the {@code
163  WearableRecyclerView}. If you want to enable circular scrolling gesture
164  in your child view, use the  {@code WearavleRecyclerView}’s {@code
165  setCircularScrollingGestureEnabled()} method.  You can also customize the
166  circular scrolling gesture by defining one or both of the following:
167</p>
168
169<ul>
170  <li>How many degrees the user has to rotate by to scroll through one screen height.
171    This effectively influences the speed of the scolling -
172    {@code setScrollDegreesPerScreen} - the default value is set at 180 degrees.
173  </li>
174
175  <li>
176    The width of a virtual ‘bezel’ near the the edge of the screen in which the
177    gesture will be recognized - {@code setBezelWidth} - the default value is set
178    at 1. This is expressed as a fraction of the radius of the view.
179</ul>
180
181
182<p>The following code snippet shows how to set these methods:</p>
183
184<pre>
185  setCircularScrollingGestureEnabled(true);
186  setBezelWidth(0.5f);
187  setScrollDegreesPerScreen(90);
188</pre>
189
190<h2 id="aligning"> Anchoring Children to the Curve </h2>
191
192<p>
193  To ensure that the layout for WearableRecyclerView is adaptable to different
194  types of child views, the WearableRecyclerView class, by default, chooses the
195  middle left edge (X=0, Y=Half the child height) as the anchor coordinates for
196  the child item. Using the default anchor coordinates can result in offsetting
197  the child items from the left edge of the watch face. To customize the anchor
198  coordinates of your child view along the curve, you can overwrite the
199  {@code adjustAnchorOffsetXY()} method. You can calculate the X (horizontal)
200  and Y (vertical) offset of the child item, and set it using the
201  {@code adjustAnchorOffsetXY()} method to properly align items
202  along the curve. The coordinates should be with relation to the child view.
203</p>
204
205<p><img src="{@docRoot}wear/preview/images/alignment.png"/></p>
206<p><b>Figure 1</b>. Imaginary UX curve and anchor points on the curve.</p>
207
208<p>
209  The code snippet below, calculates the X offset for a child item in which the
210  width of the icon is same as the height of the child item. In this case, the
211  anchor coordinates for the child item are at the center of the icon.
212
213</p>
214<img src="{@docRoot}wear/preview/images/center_align.png" style="float:left;margin:10px 20px 0 0"/>
215
216<pre>
217 &#64;Override
218  protected void adjustAnchorOffsetXY(View child, float[] anchorOffsetXY) {
219    anchorOffsetXY[0] = child.getHeight() / 2.0f;
220  }
221</pre>
222
223
224