Home | History | Annotate | Download | only in custom-views
      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 
     64 <p>To allow Android Studio to interact with your view, at a minimum you must provide a constructor that takes a
     65 {@link android.content.Context} and an {@link android.util.AttributeSet} object as parameters.
     66 This constructor allows the layout editor to create and edit an instance of your view.</p>
     67 
     68 <pre class="prettyprint">
     69 class PieChart extends View {
     70     public PieChart(Context context, AttributeSet attrs) {
     71         super(context, attrs);
     72     }
     73 }
     74 </pre>
     75 
     76 <h2 id="customattr">Define Custom Attributes</h2>
     77 
     78 <p>To add a built-in {@link android.view.View View} to your user interface, you specify it in an XML element and
     79 control its
     80 appearance and behavior with element attributes. Well-written custom views can also be added and
     81 styled via XML. To
     82 enable this behavior in your custom view, you must:
     83 
     84 <ul>
     85     <li>Define custom attributes for your view in a {@code
     86         <declare-styleable>
     87         } resource element
     88     </li>
     89     <li>Specify values for the attributes in your XML layout</li>
     90     <li>Retrieve attribute values at runtime</li>
     91     <li>Apply the retrieved attribute values to your view</li>
     92 </ul>
     93 
     94 <p>This section discusses how to define custom attributes and specify their values.
     95   The next section deals with
     96     retrieving and applying the values at runtime.</p>
     97 
     98 <p>To define custom attributes, add {@code
     99     <declare-styleable>
    100     } resources to your project. It's customary to put these resources into a {@code
    101     res/values/attrs.xml} file. Here's
    102     an example of an {@code attrs.xml} file:
    103 </p>
    104 
    105 <pre>
    106 &lt;resources>
    107    &lt;declare-styleable name="PieChart">
    108        &lt;attr name="showText" format="boolean" />
    109        &lt;attr name="labelPosition" format="enum">
    110            &lt;enum name="left" value="0"/>
    111            &lt;enum name="right" value="1"/>
    112        &lt;/attr>
    113    &lt;/declare-styleable>
    114 &lt;/resources>
    115 </pre>
    116 
    117 <p>This code declares two custom attributes, {@code showText} and {@code labelPosition}, that belong
    118     to a styleable
    119     entity named {@code PieChart}. The name of the styleable entity is, by convention, the same name as the
    120     name of the class
    121     that defines the custom view. Although it's not strictly necessary to follow this convention,
    122     many popular code
    123     editors depend on this naming convention to provide statement completion.</p>
    124 
    125 <p>Once you define the custom attributes, you can use them in layout XML files just like built-in
    126     attributes. The only
    127     difference is that your custom attributes belong to a different namespace. Instead of belonging
    128     to the {@code
    129     http://schemas.android.com/apk/res/android} namespace, they belong to {@code
    130     http://schemas.android.com/apk/res/[your package name]}. For example, here's how to use the
    131     attributes defined for
    132     {@code PieChart}:
    133     <p>
    134 
    135 <pre>
    136 &lt;?xml version="1.0" encoding="utf-8"?>
    137 &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    138    xmlns:custom="http://schemas.android.com/apk/res/com.example.customviews">
    139  &lt;com.example.customviews.charting.PieChart
    140      custom:showText="true"
    141      custom:labelPosition="left" />
    142 &lt;/LinearLayout>
    143 </pre>
    144 
    145         <p>In order to avoid having to repeat the long namespace URI, the sample uses an {@code
    146             xmlns} directive. This
    147             directive assigns the alias {@code custom} to the namespace {@code
    148             http://schemas.android.com/apk/res/com.example.customviews}. You can choose any alias
    149             you want for your
    150             namespace.</p>
    151 
    152         <p>Notice the name of the XML tag that adds the custom view to the layout. It is the fully
    153             qualified name of the
    154             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.
    155             further. For instance, the
    156             {@code PieChart} class has an inner class called {@code PieView}. To use the custom attributes from this class, you would
    157             use the tag {@code com.example.customviews.charting.PieChart$PieView}.</p>
    158 
    159         <h2 id="applyattr">Apply Custom Attributes</h2>
    160 
    161         <p>When a view is created from an XML layout, all of the attributes in the XML tag are read
    162             from the resource
    163             bundle and passed into the view's constructor as an {@link android.util.AttributeSet}.
    164             Although it's
    165             possible to read values from the {@link android.util.AttributeSet} directly, doing so
    166             has some disadvantages:</p>
    167 
    168         <ul>
    169             <li>Resource references within attribute values are not resolved</li>
    170             <li>Styles are not applied</li>
    171         </ul>
    172 
    173         <p>Instead, pass the {@link android.util.AttributeSet} to {@link
    174             android.content.res.Resources.Theme#obtainStyledAttributes obtainStyledAttributes()}.
    175             This method passes back a {@link android.content.res.TypedArray TypedArray} array of
    176             values that have
    177             already been dereferenced and styled.</p>
    178 
    179         <p>The Android resource compiler does a lot of work for you to make calling {@link
    180             android.content.res.Resources.Theme#obtainStyledAttributes obtainStyledAttributes()}
    181             easier. For each {@code <declare-styleable>}
    182             resource in the res directory, the generated R.java defines both an array of attribute
    183             ids and a set of
    184             constants that define the index for each attribute in the array. You use the predefined
    185             constants to read
    186             the attributes from the {@link android.content.res.TypedArray TypedArray}. Here's how
    187             the {@code PieChart} class
    188             reads its attributes:</p>
    189 
    190 <pre>
    191 public PieChart(Context context, AttributeSet attrs) {
    192    super(context, attrs);
    193    TypedArray a = context.getTheme().obtainStyledAttributes(
    194         attrs,
    195         R.styleable.PieChart,
    196         0, 0);
    197 
    198    try {
    199        mShowText = a.getBoolean(R.styleable.PieChart_showText, false);
    200        mTextPos = a.getInteger(R.styleable.PieChart_labelPosition, 0);
    201    } finally {
    202        a.recycle();
    203    }
    204 }
    205 </pre>
    206 
    207         <p>Note that {@link android.content.res.TypedArray TypedArray} objects
    208           are a shared resource
    209             and must be recycled after use.</p>
    210 
    211         <h2 id="addprop">Add Properties and Events</h2>
    212 
    213         <p>Attributes are a powerful way of controlling the behavior and appearance of views, but
    214             they can only be read
    215             when the view is initialized. To provide dynamic behavior, expose a property getter and
    216             setter pair for each
    217             custom attribute. The following snippet shows how {@code PieChart} exposes a property
    218             called {@code
    219             showText}:</p>
    220 
    221 <pre>
    222 public boolean isShowText() {
    223    return mShowText;
    224 }
    225 
    226 public void setShowText(boolean showText) {
    227    mShowText = showText;
    228    invalidate();
    229    requestLayout();
    230 }
    231 </pre>
    232 
    233         <p>Notice that {@code setShowText} calls {@link android.view.View#invalidate invalidate()}
    234             and {@link android.view.View#requestLayout requestLayout()}. These calls are crucial
    235             to ensure that the view behaves reliably. You have
    236             to invalidate the view after any change to its properties that might change its
    237             appearance, so that the
    238             system knows that it needs to be redrawn. Likewise, you need to request a new layout if
    239             a property changes
    240             that might affect the size or shape of the view. Forgetting these method calls can cause
    241             hard-to-find
    242             bugs.</p>
    243 
    244         <p>Custom views should also support event listeners to communicate important events. For
    245             instance, {@code PieChart}
    246             exposes a custom event called {@code OnCurrentItemChanged} to notify listeners that the
    247             user has rotated the
    248             pie chart to focus on a new pie slice.</p>
    249 
    250         <p>It's easy to forget to expose properties and events, especially when you're the only user
    251             of the custom view.
    252             Taking some time to carefully define your view's interface reduces future maintenance
    253             costs.
    254             A good rule to follow is to always expose any property that affects the visible
    255             appearance or behavior of
    256             your custom view.
    257 
    258             <h2 id="accessibility">Design For Accessibility</h2>
    259 
    260             <p>Your custom view should support the widest range of users. This includes users with
    261                 disabilities that
    262                 prevent them from seeing or using a touchscreen. To support users with disabilities,
    263                 you should:</p>
    264 
    265             <ul>
    266               <li>Label your input fields using the {@code android:contentDescription} attribute
    267               </li>
    268               <li>Send accessibility events by calling {@link
    269                 android.view.accessibility.AccessibilityEventSource#sendAccessibilityEvent
    270                 sendAccessibilityEvent()} when
    271                 appropriate.
    272               </li>
    273                 <li>
    274                 Support alternate controllers, such as D-pad and trackball</li>
    275             </ul>
    276 
    277             <p>For more information on creating accessible views, see
    278                 <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#custom-views">
    279               Making Applications Accessible</a> in the Android Developers Guide.
    280         </p>
    281