page.title=Creating a View Class parent.title=Creating Custom Views parent.link=index.html trainingnavtop=true next.title=Custom Drawing next.link=custom-drawing.html @jd:body
CustomView.zip
A well-designed custom view is much like any other well-designed class. It encapsulates a specific set of functionality with an easy to use interface, it uses CPU and memory efficiently, and so forth. In addition to being a well-designed class, though, a custom view should:
The Android framework provides a set of base classes and XML tags to help you create a view that meets all of these requirements. This lesson discusses how to use the Android framework to create the core functionality of a view class.
All of the view classes defined in the Android framework extend {@link android.view.View}. Your custom view can also extend {@link android.view.View View} directly, or you can save time by extending one of the existing view subclasses, such as {@link android.widget.Button}.
To allow the Android Developer Tools to interact with your view, at a minimum you must provide a constructor that takes a {@link android.content.Context} and an {@link android.util.AttributeSet} object as parameters. This constructor allows the layout editor to create and edit an instance of your view.
class PieChart extends View { public PieChart(Context context, AttributeSet attrs) { super(context, attrs); } }
To add a built-in {@link android.view.View View} to your user interface, you specify it in an XML element and control its appearance and behavior with element attributes. Well-written custom views can also be added and styled via XML. To enable this behavior in your custom view, you must:
This section discusses how to define custom attributes and specify their values. The next section deals with retrieving and applying the values at runtime.
To define custom attributes, add {@code <declare-styleable> } resources to your project. It's customary to put these resources into a {@code res/values/attrs.xml} file. Here's an example of an {@code attrs.xml} file:
<resources> <declare-styleable name="PieChart"> <attr name="showText" format="boolean" /> <attr name="labelPosition" format="enum"> <enum name="left" value="0"/> <enum name="right" value="1"/> </attr> </declare-styleable> </resources>
This code declares two custom attributes, {@code showText} and {@code labelPosition}, that belong to a styleable entity named {@code PieChart}. The name of the styleable entity is, by convention, the same name as the name of the class that defines the custom view. Although it's not strictly necessary to follow this convention, many popular code editors depend on this naming convention to provide statement completion.
Once you define the custom attributes, you can use them in layout XML files just like built-in attributes. The only difference is that your custom attributes belong to a different namespace. Instead of belonging to the {@code http://schemas.android.com/apk/res/android} namespace, they belong to {@code http://schemas.android.com/apk/res/[your package name]}. For example, here's how to use the attributes defined for {@code PieChart}:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:custom="http://schemas.android.com/apk/res/com.example.customviews"> <com.example.customviews.charting.PieChart custom:showText="true" custom:labelPosition="left" /> </LinearLayout>
In order to avoid having to repeat the long namespace URI, the sample uses an {@code xmlns} directive. This directive assigns the alias {@code custom} to the namespace {@code http://schemas.android.com/apk/res/com.example.customviews}. You can choose any alias you want for your namespace.
Notice the name of the XML tag that adds the custom view to the layout. It is the fully qualified name of the 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. further. For instance, the {@code PieChart} class has an inner class called {@code PieView}. To use the custom attributes from this class, you would use the tag {@code com.example.customviews.charting.PieChart$PieView}.
When a view is created from an XML layout, all of the attributes in the XML tag are read from the resource bundle and passed into the view's constructor as an {@link android.util.AttributeSet}. Although it's possible to read values from the {@link android.util.AttributeSet} directly, doing so has some disadvantages:
Instead, pass the {@link android.util.AttributeSet} to {@link android.content.res.Resources.Theme#obtainStyledAttributes obtainStyledAttributes()}. This method passes back a {@link android.content.res.TypedArray TypedArray} array of values that have already been dereferenced and styled.
The Android resource compiler does a lot of work for you to make calling {@link android.content.res.Resources.Theme#obtainStyledAttributes obtainStyledAttributes()} easier. For each {@code <declare-styleable>} resource in the res directory, the generated R.java defines both an array of attribute ids and a set of constants that define the index for each attribute in the array. You use the predefined constants to read the attributes from the {@link android.content.res.TypedArray TypedArray}. Here's how the {@code PieChart} class reads its attributes:
public PieChart(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.getTheme().obtainStyledAttributes( attrs, R.styleable.PieChart, 0, 0); try { mShowText = a.getBoolean(R.styleable.PieChart_showText, false); mTextPos = a.getInteger(R.styleable.PieChart_labelPosition, 0); } finally { a.recycle(); } }
Note that {@link android.content.res.TypedArray TypedArray} objects are a shared resource and must be recycled after use.
Attributes are a powerful way of controlling the behavior and appearance of views, but they can only be read when the view is initialized. To provide dynamic behavior, expose a property getter and setter pair for each custom attribute. The following snippet shows how {@code PieChart} exposes a property called {@code showText}:
public boolean isShowText() { return mShowText; } public void setShowText(boolean showText) { mShowText = showText; invalidate(); requestLayout(); }
Notice that {@code setShowText} calls {@link android.view.View#invalidate invalidate()} and {@link android.view.View#requestLayout requestLayout()}. These calls are crucial to ensure that the view behaves reliably. You have to invalidate the view after any change to its properties that might change its appearance, so that the system knows that it needs to be redrawn. Likewise, you need to request a new layout if a property changes that might affect the size or shape of the view. Forgetting these method calls can cause hard-to-find bugs.
Custom views should also support event listeners to communicate important events. For instance, {@code PieChart} exposes a custom event called {@code OnCurrentItemChanged} to notify listeners that the user has rotated the pie chart to focus on a new pie slice.
It's easy to forget to expose properties and events, especially when you're the only user of the custom view. Taking some time to carefully define your view's interface reduces future maintenance costs. A good rule to follow is to always expose any property that affects the visible appearance or behavior of your custom view.
Your custom view should support the widest range of users. This includes users with disabilities that prevent them from seeing or using a touchscreen. To support users with disabilities, you should:
For more information on creating accessible views, see Making Applications Accessible in the Android Developers Guide.