| Scott Main | 50e990c | 2012-06-21 17:14:39 -0700 | [diff] [blame] | 1 | page.title=Creating a View Class |
| 2 | parent.title=Creating Custom Views |
| 3 | parent.link=index.html |
| 4 | |
| 5 | trainingnavtop=true |
| 6 | next.title=Custom Drawing |
| 7 | next.link=custom-drawing.html |
| 8 | |
| 9 | @jd:body |
| 10 | |
| 11 | <div id="tb-wrapper"> |
| 12 | <div id="tb"> |
| 13 | |
| 14 | <h2>This lesson teaches you to</h2> |
| 15 | <ol> |
| 16 | <li><a href="#subclassview">Subclass a View</a></li> |
| 17 | <li><a href="#customattr">Define Custom Attributes</a></li> |
| 18 | <li><a href="#applyattr">Apply Custom Attributes to a View</a></li> |
| 19 | <li><a href="#addprop">Add Properties and Events</a></li> |
| 20 | <li><a href="#accessibility">Design For Accessibility</a></li> |
| 21 | </ol> |
| 22 | |
| 23 | <h2>You should also read</h2> |
| 24 | <ul> |
| 25 | <li><a href="{@docRoot}/guide/topics/ui/custom-components.html">Custom Components</a> |
| 26 | </li> |
| 27 | </ul> |
| 28 | <h2>Try it out</h2> |
| 29 | <div class="download-box"> |
| 30 | <a href="{@docRoot}shareables/training/CustomView.zip" |
| 31 | class="button">Download the sample</a> |
| 32 | <p class="filename">CustomView.zip</p> |
| 33 | </div> |
| 34 | </div> |
| 35 | </div> |
| 36 | |
| 37 | <p>A well-designed custom view is much like any other well-designed class. It encapsulates a |
| 38 | specific set of |
| 39 | functionality with an easy to use interface, it uses CPU and memory efficiently, and so forth. In |
| 40 | addition to being a |
| 41 | well-designed class, though, a custom view should: |
| 42 | |
| 43 | <ul> |
| 44 | <li>Conform to Android standards</li> |
| 45 | <li>Provide custom styleable attributes that work with Android XML layouts</li> |
| 46 | <li>Send accessibility events</li> |
| 47 | <li>Be compatible with multiple Android platforms.</li> |
| 48 | </ul> |
| 49 | |
| 50 | <p>The Android framework provides a set of base classes and XML tags to help you create a view that |
| 51 | meets all of these |
| 52 | requirements. This lesson discusses how to use the Android framework to create the core |
| 53 | functionality of a view |
| 54 | class.</p> |
| 55 | |
| 56 | <h2 id="subclassview">Subclass a View</h2> |
| 57 | |
| 58 | <p>All of the view classes defined in the Android framework extend {@link android.view.View}. Your |
| 59 | custom view can also |
| 60 | extend {@link android.view.View View} directly, or you can save time by extending one of the |
| 61 | existing view |
| 62 | subclasses, such as {@link android.widget.Button}.</p> |
| 63 | |
| Scott Main | f284d49 | 2012-07-31 09:46:52 -0700 | [diff] [blame] | 64 | <p>To allow the <a href="{@docRoot}guide/developing/tools/adt.html">Android Developer Tools |
| Scott Main | 50e990c | 2012-06-21 17:14:39 -0700 | [diff] [blame] | 65 | </a> to interact with your view, at a minimum you must provide a constructor that takes a |
| 66 | {@link android.content.Context} and an {@link android.util.AttributeSet} object as parameters. |
| 67 | This constructor allows the layout editor to create and edit an instance of your view.</p> |
| 68 | |
| 69 | <pre class="prettyprint"> |
| 70 | class PieChart extends View { |
| Scott Main | f8daf19 | 2013-01-04 17:41:54 -0800 | [diff] [blame] | 71 | public PieChart(Context context, AttributeSet attrs) { |
| 72 | super(context, attrs); |
| Scott Main | 50e990c | 2012-06-21 17:14:39 -0700 | [diff] [blame] | 73 | } |
| 74 | } |
| 75 | </pre> |
| 76 | |
| 77 | <h2 id="customattr">Define Custom Attributes</h2> |
| 78 | |
| 79 | <p>To add a built-in {@link android.view.View View} to your user interface, you specify it in an XML element and |
| 80 | control its |
| 81 | appearance and behavior with element attributes. Well-written custom views can also be added and |
| 82 | styled via XML. To |
| 83 | enable this behavior in your custom view, you must: |
| 84 | |
| 85 | <ul> |
| 86 | <li>Define custom attributes for your view in a {@code |
| 87 | <declare-styleable> |
| 88 | } resource element |
| 89 | </li> |
| 90 | <li>Specify values for the attributes in your XML layout</li> |
| 91 | <li>Retrieve attribute values at runtime</li> |
| 92 | <li>Apply the retrieved attribute values to your view</li> |
| 93 | </ul> |
| 94 | |
| 95 | <p>This section discusses how to define custom attributes and specify their values. |
| 96 | The next section deals with |
| 97 | retrieving and applying the values at runtime.</p> |
| 98 | |
| 99 | <p>To define custom attributes, add {@code |
| 100 | <declare-styleable> |
| 101 | } resources to your project. It's customary to put these resources into a {@code |
| 102 | res/values/attrs.xml} file. Here's |
| 103 | an example of an {@code attrs.xml} file: |
| 104 | </p> |
| 105 | |
| 106 | <pre> |
| Scott Main | f8daf19 | 2013-01-04 17:41:54 -0800 | [diff] [blame] | 107 | <resources> |
| Scott Main | f284d49 | 2012-07-31 09:46:52 -0700 | [diff] [blame] | 108 | <declare-styleable name="PieChart"> |
| Scott Main | 50e990c | 2012-06-21 17:14:39 -0700 | [diff] [blame] | 109 | <attr name="showText" format="boolean" /> |
| 110 | <attr name="labelPosition" format="enum"> |
| 111 | <enum name="left" value="0"/> |
| 112 | <enum name="right" value="1"/> |
| 113 | </attr> |
| 114 | </declare-styleable> |
| 115 | </resources> |
| 116 | </pre> |
| 117 | |
| 118 | <p>This code declares two custom attributes, {@code showText} and {@code labelPosition}, that belong |
| 119 | to a styleable |
| 120 | entity named {@code PieChart}. The name of the styleable entity is, by convention, the same name as the |
| 121 | name of the class |
| 122 | that defines the custom view. Although it's not strictly necessary to follow this convention, |
| 123 | many popular code |
| 124 | editors depend on this naming convention to provide statement completion.</p> |
| 125 | |
| 126 | <p>Once you define the custom attributes, you can use them in layout XML files just like built-in |
| 127 | attributes. The only |
| 128 | difference is that your custom attributes belong to a different namespace. Instead of belonging |
| 129 | to the {@code |
| 130 | http://schemas.android.com/apk/res/android} namespace, they belong to {@code |
| 131 | http://schemas.android.com/apk/res/[your package name]}. For example, here's how to use the |
| 132 | attributes defined for |
| 133 | {@code PieChart}: |
| 134 | <p> |
| 135 | |
| 136 | <pre> |
| 137 | <?xml version="1.0" encoding="utf-8"?> |
| 138 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| 139 | xmlns:custom="http://schemas.android.com/apk/res/com.example.customviews"> |
| 140 | <com.example.customviews.charting.PieChart |
| 141 | custom:showText="true" |
| 142 | custom:labelPosition="left" /> |
| 143 | </LinearLayout> |
| 144 | </pre> |
| 145 | |
| 146 | <p>In order to avoid having to repeat the long namespace URI, the sample uses an {@code |
| 147 | xmlns} directive. This |
| 148 | directive assigns the alias {@code custom} to the namespace {@code |
| 149 | http://schemas.android.com/apk/res/com.example.customviews}. You can choose any alias |
| 150 | you want for your |
| 151 | namespace.</p> |
| 152 | |
| 153 | <p>Notice the name of the XML tag that adds the custom view to the layout. It is the fully |
| 154 | qualified name of the |
| 155 | custom view class. If your view class is an inner class, you must further qualify it with the name of the view's outer class. |
| 156 | further. For instance, the |
| 157 | {@code PieChart} class has an inner class called {@code PieView}. To use the custom attributes from this class, you would |
| 158 | use the tag {@code com.example.customviews.charting.PieChart$PieView}.</p> |
| 159 | |
| 160 | <h2 id="applyattr">Apply Custom Attributes</h2> |
| 161 | |
| 162 | <p>When a view is created from an XML layout, all of the attributes in the XML tag are read |
| 163 | from the resource |
| 164 | bundle and passed into the view's constructor as an {@link android.util.AttributeSet}. |
| 165 | Although it's |
| 166 | possible to read values from the {@link android.util.AttributeSet} directly, doing so |
| 167 | has some disadvantages:</p> |
| 168 | |
| 169 | <ul> |
| 170 | <li>Resource references within attribute values are not resolved</li> |
| 171 | <li>Styles are not applied</li> |
| 172 | </ul> |
| 173 | |
| 174 | <p>Instead, pass the {@link android.util.AttributeSet} to {@link |
| 175 | android.content.res.Resources.Theme#obtainStyledAttributes obtainStyledAttributes()}. |
| 176 | This method passes back a {@link android.content.res.TypedArray TypedArray} array of |
| 177 | values that have |
| 178 | already been dereferenced and styled.</p> |
| 179 | |
| 180 | <p>The Android resource compiler does a lot of work for you to make calling {@link |
| 181 | android.content.res.Resources.Theme#obtainStyledAttributes obtainStyledAttributes()} |
| 182 | easier. For each {@code <declare-styleable>} |
| 183 | resource in the res directory, the generated R.java defines both an array of attribute |
| 184 | ids and a set of |
| 185 | constants that define the index for each attribute in the array. You use the predefined |
| 186 | constants to read |
| 187 | the attributes from the {@link android.content.res.TypedArray TypedArray}. Here's how |
| 188 | the {@code PieChart} class |
| 189 | reads its attributes:</p> |
| 190 | |
| 191 | <pre> |
| Scott Main | f8daf19 | 2013-01-04 17:41:54 -0800 | [diff] [blame] | 192 | public PieChart(Context context, AttributeSet attrs) { |
| 193 | super(context, attrs); |
| Scott Main | 50e990c | 2012-06-21 17:14:39 -0700 | [diff] [blame] | 194 | TypedArray a = context.getTheme().obtainStyledAttributes( |
| 195 | attrs, |
| 196 | R.styleable.PieChart, |
| 197 | 0, 0); |
| 198 | |
| 199 | try { |
| 200 | mShowText = a.getBoolean(R.styleable.PieChart_showText, false); |
| 201 | mTextPos = a.getInteger(R.styleable.PieChart_labelPosition, 0); |
| 202 | } finally { |
| 203 | a.recycle(); |
| 204 | } |
| 205 | } |
| 206 | </pre> |
| 207 | |
| 208 | <p>Note that {@link android.content.res.TypedArray TypedArray} objects |
| 209 | are a shared resource |
| 210 | and must be recycled after use.</p> |
| 211 | |
| 212 | <h2 id="addprop">Add Properties and Events</h2> |
| 213 | |
| 214 | <p>Attributes are a powerful way of controlling the behavior and appearance of views, but |
| 215 | they can only be read |
| 216 | when the view is initialized. To provide dynamic behavior, expose a property getter and |
| 217 | setter pair for each |
| 218 | custom attribute. The following snippet shows how {@code PieChart} exposes a property |
| 219 | called {@code |
| 220 | showText}:</p> |
| 221 | |
| 222 | <pre> |
| 223 | public boolean isShowText() { |
| 224 | return mShowText; |
| 225 | } |
| 226 | |
| 227 | public void setShowText(boolean showText) { |
| 228 | mShowText = showText; |
| 229 | invalidate(); |
| 230 | requestLayout(); |
| 231 | } |
| 232 | </pre> |
| 233 | |
| 234 | <p>Notice that {@code setShowText} calls {@link android.view.View#invalidate invalidate()} |
| 235 | and {@link android.view.View#requestLayout requestLayout()}. These calls are crucial |
| 236 | to ensure that the view behaves reliably. You have |
| 237 | to invalidate the view after any change to its properties that might change its |
| 238 | appearance, so that the |
| 239 | system knows that it needs to be redrawn. Likewise, you need to request a new layout if |
| 240 | a property changes |
| 241 | that might affect the size or shape of the view. Forgetting these method calls can cause |
| 242 | hard-to-find |
| 243 | bugs.</p> |
| 244 | |
| 245 | <p>Custom views should also support event listeners to communicate important events. For |
| 246 | instance, {@code PieChart} |
| 247 | exposes a custom event called {@code OnCurrentItemChanged} to notify listeners that the |
| 248 | user has rotated the |
| 249 | pie chart to focus on a new pie slice.</p> |
| 250 | |
| 251 | <p>It's easy to forget to expose properties and events, especially when you're the only user |
| 252 | of the custom view. |
| 253 | Taking some time to carefully define your view's interface reduces future maintenance |
| 254 | costs. |
| 255 | A good rule to follow is to always expose any property that affects the visible |
| 256 | appearance or behavior of |
| 257 | your custom view. |
| 258 | |
| 259 | <h2 id="accessibility">Design For Accessibility</h2> |
| 260 | |
| 261 | <p>Your custom view should support the widest range of users. This includes users with |
| 262 | disabilities that |
| 263 | prevent them from seeing or using a touchscreen. To support users with disabilities, |
| 264 | you should:</p> |
| 265 | |
| 266 | <ul> |
| 267 | <li>Label your input fields using the {@code android:contentDescription} attribute |
| 268 | </li> |
| 269 | <li>Send accessibility events by calling {@link |
| 270 | android.view.accessibility.AccessibilityEventSource#sendAccessibilityEvent |
| 271 | sendAccessibilityEvent()} when |
| 272 | appropriate. |
| 273 | </li> |
| 274 | <li> |
| 275 | Support alternate controllers, such as D-pad and trackball</li> |
| 276 | </ul> |
| 277 | |
| 278 | <p>For more information on creating accessible views, see |
| Scott Main | f284d49 | 2012-07-31 09:46:52 -0700 | [diff] [blame] | 279 | <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#custom-views"> |
| Scott Main | 50e990c | 2012-06-21 17:14:39 -0700 | [diff] [blame] | 280 | Making Applications Accessible</a> in the Android Developers Guide. |
| 281 | </p> |