Android comes with a solid collection of View components that you can use to construct your applications, e.g. Button, TextView, EditText, ListView, CheckBox, RadioButton, Gallery, Spinner, and even some much more advanced and special purpose Views like AutoCompleteTextView, ImageSwitcher, and TextSwitcher. The various layout managers like LinearLayout, FrameLayout, and so forth are also considered Views and are descendents of the View class hierarchy.
You can combine these layouts and controls into a screen to display in your application, and much of the time this may be enough for you, but you should also be aware that you can create custom components by extending Views, Layouts, and even the advanced controls using inheritance. Some typical reasons for doing this might include:
There are many more reasons why you might want to extend an existing View to achieve some goal. This page will give you some starting points on how to do it, and back it up with some examples.
These steps provide a high level overview of what you need to know to get started in creating your own components:
on
', for
example, onDraw(),
onMeasure(), and
onKeyDown().
on...
events in Activity or ListActivity
that you override for life cycle and other functionality hooks.
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 component for wider use in your application).
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.
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).
To create a fully customized component:
onMeasure()
and
are also likely to need to override onDraw()
if you want
the component to show something. While both have default behavior,
the default onDraw()
will do nothing, and the default
onMeasure()
will always set a size of 100x100 — which is
probably not what you want.
on...
methods may also be overridden as required.
onDraw()
and onMeasure()
onDraw()
delivers you a 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.
Note: Except for 3D graphics. If you want to use 3D graphics, you must extend SurfaceView instead of View, and draw from a seperate thread. See the GLSurfaceViewActivity sample for details.
onMeasure()
is a little more involved. onMeasure()
is a critical piece of the rendering contract between your component and its
container. onMeasure()
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 onMeasure()
method) and by the
requirement to call the setMeasuredDimension()
method with the
measured width and height once they have been calculated. If you fail to
call this method from an overridden onMeasure()
method, the
result will be an exception at measurement time.
At a high level, implementing onMeasure()
looks something
like this:
onMeasure()
method is called with width and
height measure specifications (widthMeasureSpec
and
heighMeasureSpec
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 View.onMeasure(int, int) (this reference
documentation does a pretty good job of explaining the whole measurement
operation as well).
onMeasure()
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 onMeasure()
to try again, perhaps with
different measurement specifications).
setMeasuredDimension(int
width, int height)
method must be called with the calculated
measurements. Failure to do this will result in an exception being
thrown.
The CustomView sample in the API Demos provides an example of a customized component. The custom component is defined in the LabelView class.
The LabelView sample demonstrates a number of different aspects of custom components:
setText()
, setTextSize()
,
setTextColor()
and so on.onMeasure
method to determine and set the
rendering size of the component. (Note that in LabelView, the real work is done
by a private measureWidth()
method.)onDraw()
method to draw the label onto the
provided canvas.You can see some sample usages of the LabelView custom component in
custom_view_1.xml
from the samples. In particular, you can see a mix of both android:
namespace parameters and custom app:
namespace parameters. These
app:
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.
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.
In Android, there are actually two other Views readily available to do this: Spinner and AutoCompleteTextView, but regardless, the concept of a Combo Box makes an easy-to-understand example.
To create a Compound Component:
onDraw()
and onMeasure()
methods since the
layout will have default behavior that will likely work just fine. However,
you can still override them if you need to.
on...
methods, like
onKeyDown()
, to perhaps choose certain default values from
the popup list of a combo box when a certain key is pressed.
To summarize, the use of a Layout as the basis for a Custom Control has a number of advantages, including:
onDraw()
and onMeasure()
methods (plus
most of the other on...
methods) will likely have suitable behavior so
you don't have to override them.
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
List4.java
and List6.java
.
There is an even easier option for creating a custom component 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.
For example, the SDK includes a NotePad application 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.
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
MyEditText
in the NoteEditor.java
file.
Some points to note here
The class is defined with the following line:
public static class MyEditText extends EditText
NoteEditor
activity, but it is public so that it could be accessed as
NoteEditor.MyEditText
from outside of the NoteEditor
class if desired.
static
, 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 NoteEditor
.
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.
EditText
, 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 EditText
view.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.
In this example, there is only one method to be overridden:
onDraw()
— but there could easily be others needed when you
create your own custom components.
For the NotePad sample, overriding the onDraw()
method allows
us to paint the blue lines on the EditText
view canvas (the
canvas is passed into the overridden onDraw()
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.
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 note_editor.xml
in the
res/layout
folder.
<view xmlns:android="http://schemas.android.com/apk/res/android" 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" />
NoteEditor$MyEditText
notation which is a standard way to
refer to inner classes in the Java programming language.
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.
A more sophisticated component may override even more on...
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.
As you can see, Android offers a sophisticated and powerful component model where just about anything is possible, from simple tweaking of existing Views, to compound controls, to fully customized components. Combining these techniques, you should be able to achieve the exact look you want for your Android application.
Copyright 2007 Google Inc. | Build 0.9_r1-98467 - 14 Aug 2008 18:48 |