diff options
Diffstat (limited to 'docs/html/guide/topics/ui/custom-components.jd')
-rw-r--r-- | docs/html/guide/topics/ui/custom-components.jd | 563 |
1 files changed, 563 insertions, 0 deletions
diff --git a/docs/html/guide/topics/ui/custom-components.jd b/docs/html/guide/topics/ui/custom-components.jd new file mode 100644 index 0000000..eccc2ca --- /dev/null +++ b/docs/html/guide/topics/ui/custom-components.jd @@ -0,0 +1,563 @@ +page.title=Building Custom Components +parent.title=User Interface +parent.link=index.html +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + <h2>In this document</h2> + <ol> + <li><a href="#basic">The Basic Approach</a></li> + <li><a href="#custom">Fully Customized Components</a></li> + <li><a href="#compound">Compound Controls</a></li> + <li><a href="#modifying">Modifying an Existing View Type</a></li> + </ol> +</div> +</div> + +<p>Android offers a sophisticated and powerful componentized model for building your UI, +based on the fundamental layout classes: {@link android.view.View} and +{@link android.view.ViewGroup}. To start with, the platform includes a variety of prebuilt +View and ViewGroup subclasses — called widgets and layouts, respectively — +that you can use to construct your UI.</p> + +<p>A partial list of available widgets includes {@link android.widget.Button Button}, +{@link android.widget.TextView TextView}, +{@link android.widget.EditText EditText}, +{@link android.widget.ListView ListView}, +{@link android.widget.CheckBox CheckBox}, +{@link android.widget.RadioButton RadioButton}, +{@link android.widget.Gallery Gallery}, +{@link android.widget.Spinner Spinner}, and the more special-purpose +{@link android.widget.AutoCompleteTextView AutoCompleteTextView}, +{@link android.widget.ImageSwitcher ImageSwitcher}, and +{@link android.widget.TextSwitcher TextSwitcher}. </p> + +<p>Among the layouts available are {@link android.widget.LinearLayout LinearLayout}, +{@link android.widget.FrameLayout FrameLayout}, {@link android.widget.AbsoluteLayout AbsoluteLayout}, +and others. For more examples, see <a href="layout-objects.html">Common Layout Objects</a>.</p> + +<p>If none of the prebuilt widgets or layouts meets your needs, you can create your own View subclass. +If you only need to make small adjustments to an existing widget or layout, you can simply subclass +the widget or layout and override its methods. +</p> + +<p>Creating your own View subclasses gives you precise control over the appearance and function +of a screen element. To give an idea of the control you get with custom views, here are some +examples of what you could do with them:</p> + +<ul> + <li> + You could create a completely custom-rendered View type, for example a "volume + control" knob rendered using 2D graphics, and which resembles an + analog electronic control. + </li> + <li> + You could combine a group of View components into a new single component, perhaps + to make something like a ComboBox (a combination of popup list and free + entry text field), a dual-pane selector control (a left and right pane + with a list in each where you can re-assign which item is in which + list), and so on. + </li> + <li> + You could override the way that an EditText component is rendered on the screen + (the <a href="{@docRoot}guide/samples/NotePad/index.html">Notepad Tutorial</a> uses this to good effect, + to create a lined-notepad page). + </li> + <li> + You could capture other events like key presses and handle them in some custom + way (such as for a game). + </li> +</ul> +<p> +The sections below explain how to create custom Views and use them in your application. +For detailed reference information, see the {@link android.view.View} class. </p> + + +<h2 id="basic">The Basic Approach</h2> + +<p>Here is a high level overview of what you need to know to get started in creating your own +View components:</p> + +<ol> + <li> + Extend an existing {@link android.view.View View} class or subclass + with your own class. + </li> + <li> + Override some of the methods from the superclass. The superclass methods + to override start with '<code>on</code>', for + example, {@link android.view.View#onDraw onDraw()}, + {@link android.view.View#onMeasure onMeasure()}, and + {@link android.view.View#onKeyDown onKeyDown()}. + This is similar to the <code>on...</code> events in {@link android.app.Activity Activity} + or {@link android.app.ListActivity ListActivity} + that you override for lifecycle and other functionality hooks. + <li> + Use your new extension class. Once completed, your new extension class + can be used in place of the view upon which it was based. + </li> +</ol> +<p class="note"><strong>Tip:</strong> + Extension classes can be defined as inner classes inside the activities + that use them. This is useful because it controls access to them but + isn't necessary (perhaps you want to create a new public View for + wider use in your application). +</p> + + + +<h2 id="custom">Fully Customized Components</h2> +<p> +Fully customized components can be used to create graphical components that +appear however you wish. Perhaps a graphical VU +meter that looks like an old analog gauge, or a sing-a-long text view where +a bouncing ball moves along the words so you can sing along with a karaoke +machine. Either way, you want something that the built-in components just +won't do, no matter how you combine them.</p> +<p>Fortunately, you can easily create components that look and behave in any +way you like, limited perhaps only by your imagination, the size of the +screen, and the available processing power (remember that ultimately your +application might have to run on something with significantly less power +than your desktop workstation).</p> +<p>To create a fully customized component:</p> +<ol> + <li> + The most generic view you can extend is, unsurprisingly, {@link + android.view.View View}, so you will usually start by extending this to + create your new super component. + </li> + <li> + You can supply a constructor which can + take attributes and parameters from the XML, and you can also consume + your own such attributes and parameters (perhaps the color and range of + the VU meter, or the width and damping of the needle, etc.) + </li> + <li> + You will probably want to create your own event listeners, + property accessors and modifiers, and possibly more sophisticated + behavior in your component class as well. + </li> + <li> + You will almost certainly want to override <code>onMeasure()</code> and + are also likely to need to override <code>onDraw()</code> if you want + the component to show something. While both have default behavior, + the default <code>onDraw()</code> will do nothing, and the default + <code>onMeasure()</code> will always set a size of 100x100 — which is + probably not what you want. + </li> + <li> + Other <code>on...</code> methods may also be overridden as required. + </li> +</ol> + +<h3>Extend <code>onDraw()</code> and <code>onMeasure()</code></h3> +<p>The <code>onDraw()</code> method delivers you a {@link android.graphics.Canvas Canvas} +upon which you can implement anything you want: 2D graphics, other standard or +custom components, styled text, or anything else you can think of.</p> + +<p class="note"><strong>Note:</strong> +This does not apply to 3D graphics. If you want to +use 3D graphics, you must extend {@link android.view.SurfaceView SurfaceView} +instead of View, and draw from a seperate thread. See the +GLSurfaceViewActivity sample +for details.</p> + +<p><code>onMeasure()</code> is a little more involved. <code>onMeasure()</code> +is a critical piece of the rendering contract between your component and its +container. <code>onMeasure()</code> should be overridden to efficiently and +accurately report the measurements of its contained parts. This is made +slightly more complex by the requirements of limits from the parent +(which are passed in to the <code>onMeasure()</code> method) and by the +requirement to call the <code>setMeasuredDimension()</code> method with the +measured width and height once they have been calculated. If you fail to +call this method from an overridden <code>onMeasure()</code> method, the +result will be an exception at measurement time.</p> +<p>At a high level, implementing <code>onMeasure()</code> looks something + like this:</p> + +<ol> + <li> + The overridden <code>onMeasure()</code> method is called with width and + height measure specifications (<code>widthMeasureSpec</code> and + <code>heightMeasureSpec</code> parameters, both are integer codes + representing dimensions) which should be treated as requirements for + the restrictions on the width and height measurements you should produce. A + full reference to the kind of restrictions these specifications can require + can be found in the reference documentation under {@link + android.view.View#onMeasure View.onMeasure(int, int)} (this reference + documentation does a pretty good job of explaining the whole measurement + operation as well). + </li> + <li> + Your component's <code>onMeasure()</code> method should calculate a + measurement width and height which will be required to render the + component. It should try to stay within the specifications passed in, + although it can choose to exceed them (in this case, the parent can + choose what to do, including clipping, scrolling, throwing an exception, + or asking the <code>onMeasure()</code> to try again, perhaps with + different measurement specifications). + </li> + <li> + Once the width and height are calculated, the <code>setMeasuredDimension(int + width, int height)</code> method must be called with the calculated + measurements. Failure to do this will result in an exception being + thrown. + </li> +</ol> + +<p> +Here's a summary of some of the other standard methods that the framework calls on views: +</p> +<table border="2" width="85%" align="center" cellpadding="5"> + <thead> + <tr><th>Category</th> <th>Methods</th> <th>Description</th></tr> + </thead> + + <tbody> + <tr> + <td rowspan="2">Creation</td> + <td>Constructors</td> + <td>There is a form of the constructor that are called when the view + is created from code and a form that is called when the view is + inflated from a layout file. The second form should parse and apply + any attributes defined in the layout file. + </td> + </tr> + <tr> + <td><code>{@link android.view.View#onFinishInflate()}</code></td> + <td>Called after a view and all of its children has been inflated + from XML.</td> + </tr> + + <tr> + <td rowspan="3">Layout</td> + <td><code>{@link android.view.View#onMeasure}</code></td> + <td>Called to determine the size requirements for this view and all + of its children. + </td> + </tr> + <tr> + <td><code>{@link android.view.View#onLayout}</code></td> + <td>Called when this view should assign a size and position to all + of its children. + </td> + </tr> + <tr> + <td><code>{@link android.view.View#onSizeChanged}</code></td> + <td>Called when the size of this view has changed. + </td> + </tr> + + <tr> + <td>Drawing</td> + <td><code>{@link android.view.View#onDraw}</code></td> + <td>Called when the view should render its content. + </td> + </tr> + + <tr> + <td rowspan="4">Event processing</td> + <td><code>{@link android.view.View#onKeyDown}</code></td> + <td>Called when a new key event occurs. + </td> + </tr> + <tr> + <td><code>{@link android.view.View#onKeyUp}</code></td> + <td>Called when a key up event occurs. + </td> + </tr> + <tr> + <td><code>{@link android.view.View#onTrackballEvent}</code></td> + <td>Called when a trackball motion event occurs. + </td> + </tr> + <tr> + <td><code>{@link android.view.View#onTouchEvent}</code></td> + <td>Called when a touch screen motion event occurs. + </td> + </tr> + + <tr> + <td rowspan="2">Focus</td> + <td><code>{@link android.view.View#onFocusChanged}</code></td> + <td>Called when the view gains or loses focus. + </td> + </tr> + + <tr> + <td><code>{@link android.view.View#onWindowFocusChanged}</code></td> + <td>Called when the window containing the view gains or loses focus. + </td> + </tr> + + <tr> + <td rowspan="3">Attaching</td> + <td><code>{@link android.view.View#onAttachedToWindow()}</code></td> + <td>Called when the view is attached to a window. + </td> + </tr> + + <tr> + <td><code>{@link android.view.View#onDetachedFromWindow}</code></td> + <td>Called when the view is detached from its window. + </td> + </tr> + + <tr> + <td><code>{@link android.view.View#onWindowVisibilityChanged}</code></td> + <td>Called when the visibility of the window containing the view + has changed. + </td> + </tr> + </tbody> + + </table> + + + +<h3 id="customexample">A Custom View Example</h3> +<p>The CustomView sample in the +<a href="{@docRoot}guide/samples/ApiDemos/index.html">API Demos</a> provides an example +of a customized View. The custom View is defined in the +<a href="{@docRoot}guide/samples/ApiDemos/src/com/example/android/apis/view/LabelView.html">LabelView</a> +class.</p> +<p>The LabelView sample demonstrates a number of different aspects of custom components:</p> +<ul> + <li>Extending the View class for a completely custom component.</li> + <li>Parameterized constructor that takes the view inflation parameters + (parameters defined in the XML). Some of these are passed through to the + View superclass, but more importantly, there are some custom attributes defined + and used for LabelView.</li> + <li>Standard public methods of the type you would expect to see for a label + component, for example <code>setText()</code>, <code>setTextSize()</code>, + <code>setTextColor()</code> and so on.</li> + <li>An overridden <code>onMeasure</code> method to determine and set the + rendering size of the component. (Note that in LabelView, the real work is done + by a private <code>measureWidth()</code> method.)</li> + <li>An overridden <code>onDraw()</code> method to draw the label onto the + provided canvas.</li> +</ul> +<p>You can see some sample usages of the LabelView custom View in +<a href="{@docRoot}guide/samples/ApiDemos/res/layout/custom_view_1.html">custom_view_1.xml</a> +from the samples. In particular, you can see a mix of both <code>android:</code> +namespace parameters and custom <code>app:</code> namespace parameters. These +<code>app:</code> parameters are the custom ones that the LabelView recognizes +and works with, and are defined in a styleable inner class inside of the +samples R resources definition class.</p> + + +<h2 id="compound">Compound Controls +</h2> +<p>If you don't want to create a completely customized component, but instead +are looking to put together a reusable component that consists of a group of +existing controls, then creating a Compound Component (or Compound Control) might +fit the bill. In a nutshell, this brings together a number of more atomic +controls (or views) into a logical group of items that can be treated as a +single thing. For example, a Combo Box can be thought of as a +combination of a single line EditText field and an adjacent button with an attached + PopupList. If you press the button and select +something from the list, it populates the EditText field, but the user can +also type something directly into the EditText if they prefer.</p> +<p>In Android, there are actually two other Views readily available to do +this: {@link android.widget.Spinner Spinner} and +{@link android.widget.AutoCompleteTextView AutoCompleteTextView}, but +regardless, the concept of a Combo Box makes an easy-to-understand +example.</p> +<p>To create a compound component:</p> +<ol> + <li> + The usual starting point is a Layout of some kind, so create a class + that extends a Layout. Perhaps in the case of a Combo box we might use + a LinearLayout with horizontal orientation. Remember that other layouts + can be nested inside, so the compound component can be arbitrarily + complex and structured. Note that just like with an Activity, you can + use either the declarative (XML-based) approach to creating the + contained components, or you can nest them programmatically from your + code. + </li> + <li> + In the constructor for the new class, take whatever parameters the + superclass expects, and pass them through to the superclass constructor + first. Then you can set up the other views to use within your new + component; this is where you would create the EditText field and the + PopupList. Note that you also might introduce your own attributes and + parameters into the XML that can be pulled out and used by your + constructor. + </li> + <li> + You can also create listeners for events that your contained views might + generate, for example, a listener method for the List Item Click Listener + to update the contents of the EditText if a list selection is made. + </li> + <li> + You might also create your own properties with accessors and modifiers, + for example, allow the EditText value to be set initially in the + component and query for its contents when needed. + </li> + <li> + In the case of extending a Layout, you don't need to override the + <code>onDraw()</code> and <code>onMeasure()</code> methods since the + layout will have default behavior that will likely work just fine. However, + you can still override them if you need to. + </li> + <li> + You might override other <code>on...</code> methods, like + <code>onKeyDown()</code>, to perhaps choose certain default values from + the popup list of a combo box when a certain key is pressed. + </li> +</ol> +<p> + To summarize, the use of a Layout as the basis for a Custom Control has a +number of advantages, including:</p> + +<ul> + <li> + You can specify the layout using the declarative XML files just like + with an activity screen, or you can create views programmatically and + nest them into the layout from your code. + </li> + <li> + The <code>onDraw()</code> and <code>onMeasure()</code> methods (plus + most of the other <code>on...</code> methods) will likely have suitable behavior so + you don't have to override them. + </li> + <li> + In the end, you can very quickly construct arbitrarily complex compound + views and re-use them as if they were a single component. + </li> +</ul> +<h4>Examples of Compound Controls</h4> +<p>In the API Demos project + that comes with the SDK, there are two List + examples — Example 4 and Example 6 under Views/Lists demonstrate a + SpeechView which extends LinearLayout to make a component for displaying + Speech quotes. The corresponding classes in the sample code are + <code>List4.java</code> and <code>List6.java</code>.</p> + + + +<h2 id="modifying">Modifying an Existing View Type</h2> +<p>There is an even easier option for creating a custom View which is +useful in certain circumstances. If there is a component that is already very +similar to what you want, you can simply extend that component and just +override the behavior that you want to change. You can do all of the things +you would do with a fully customized component, but by starting with a more +specialized class in the View heirarchy, you can also get a lot of behavior for +free that probably does exactly what you want.</p> +<p>For example, the SDK includes a <a +href="{@docRoot}guide/samples/NotePad/index.html">NotePad application</a> in the +samples. This demonstrates many aspects of using the Android platform, among +them is extending an EditText View to make a lined notepad. This is not a +perfect example, and the APIs for doing this might change from this early +preview, but it does demonstrate the principles.</p> +<p>If you haven't done so already, import the +NotePad sample into Eclipse (or +just look at the source using the link provided). In particular look at the definition of +<code>MyEditText</code> in the <a +href="{@docRoot}guide/samples/NotePad/src/com/example/android/notepad/NoteEditor.html">NoteEditor.java</a> +file.</p> +<p>Some points to note here</p> +<ol> + <li> + <strong>The Definition</strong> + <p>The class is defined with the following line:<br/> + <code>public static class MyEditText extends EditText</code></p> + + <ul> + <li> + It is defined as an inner class within the <code>NoteEditor</code> + activity, but it is public so that it could be accessed as + <code>NoteEditor.MyEditText</code> from outside of the <code>NoteEditor</code> + class if desired. + </li> + <li> + It is <code>static</code>, meaning it does not generate the so-called + "synthetic methods" that allow it to access data from the parent + class, which in turn means that it really behaves as a separate + class rather than something strongly related to <code>NoteEditor</code>. + This is a cleaner way to create inner classes if they do not need + access to state from the outer class, keeps the generated class + small, and allows it to be used easily from other classes. + </li> + <li> + It extends <code>EditText</code>, which is the View we have chosen to + customize in this case. When we are finished, the new class will be + able to substitute for a normal <code>EditText</code> view. + </li> + </ul> + </li> + <li> + <strong>Class Initialization</strong> + <p>As always, the super is called first. Furthermore, + this is not a default constructor, but a parameterized one. The + EditText is created with these parameters when it is inflated from an + XML layout file, thus, our constructor needs to both take them and pass them + to the superclass constructor as well.</p> + </li> + <li> + <strong>Overridden Methods</strong> + <p>In this example, there is only one method to be overridden: + <code>onDraw()</code> — but there could easily be others needed when you + create your own custom components.</p> + <p>For the NotePad sample, overriding the <code>onDraw()</code> method allows + us to paint the blue lines on the <code>EditText</code> view canvas (the + canvas is passed into the overridden <code>onDraw()</code> method). The + super.onDraw() method is called before the method ends. The + superclass method should be invoked, but in this case, we do it at the + end after we have painted the lines we want to include.</p> + <li> + <strong>Use the Custom Component</strong> + <p>We now have our custom component, but how can we use it? In the + NotePad example, the custom component is used directly from the + declarative layout, so take a look at <code>note_editor.xml</code> in the + <code>res/layout</code> folder.</p> +<pre> +<view + class="com.android.notepad.NoteEditor$MyEditText" + id="@+id/note" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:background="@android:drawable/empty" + android:padding="10dip" + android:scrollbars="vertical" + android:fadingEdge="vertical" /> +</pre> + + <ul> + <li> + The custom component is created as a generic view in the XML, and + the class is specified using the full package. Note also that the + inner class we defined is referenced using the + <code>NoteEditor$MyEditText</code> notation which is a standard way to + refer to inner classes in the Java programming language. + <p>If your custom View component is not defined as an inner class, then you can, + alternatively, declare the View component + with the XML element name, and exclude the <code>class</code> attribute. For example:</p> +<pre> +<com.android.notepad.MyEditText + id="@+id/note" + ... /> +</pre> + <p>Notice that the <code>MyEditText</code> class is now a separate class file. When the class + is nested in the <code>NoteEditor</code> class, this technique will not work.</p> + </li> + <li> + The other attributes and parameters in the definition are the ones + passed into the custom component constructor, and then passed + through to the EditText constructor, so they are the same + parameters that you would use for an EditText view. Note that it is + possible to add your own parameters as well, and we will touch on + this again below. + </li> + </ul> + </li> +</ol> +<p>And that's all there is to it. Admittedly this is a simple case, but +that's the point — creating custom components is only as complicated as you +need it to be.</p> +<p>A more sophisticated component may override even more <code>on...</code> methods and +introduce some of its own helper methods, substantially customizing its properties and +behavior. The only limit is your imagination and what you need the component to +do.</p> + |