Creating custom Android views – Part 1: Extending standard views and adding new xml attributes

Some introduction

This will be a series of posts on the subject of making custom views in android. I’ll cover, among other things, how to draw the content, how layouting and measuring works, how to implement view groups and how to animate the content. This first post will start with how to extend views, how to use them and how to create your own xml attributes.

Specific tasks, specific views

Most standard views that android supplies are very general, that is, they can be used for many tasks and in many situations. However, in your application it’s often the fact that much code is used to configure these views to specific tasks. This configuration code is often in the activity part of your application and activities can be a real problem to keep clean. Lot’s of different code tends to end up there and it can be hard to refactor into separate classes.

Let’s say you’re working on an application to keep track of a user’s training statistics. For example, the total distance, total time, etc. for different training types. In order to present the data to the user nicely you might want to adapt the display the time based on how long the duration is. If it’s 2378 seconds then you might want to display the time as “40 minutes” instead and if its 18550 seconds then it might be better to display “5 hours 9 minutes”

Creating a custom view

One way to handle this is to do a specialized view that handles exactly this kind of text. Let’s start with creating our own class, DurationTextView, by extending TextView.

TextView, like all views, has three different constructors: one that takes just a context, the one above that takes a context and an AttributeSet and another one that in addition also takes a default style. For most purposes, it’s sufficient to just implement the second one, which I’ve done above.

Now we’ve created our new view and to use it we add it to the xml like this

Note that you need to specify the fully qualified name of the class for views that you have implemented yourself.

At the moment this class is more or less identical to the regular TextView, so lets add some functionality to it.

Adding presentation logic

Since this view is going to display durations, let’s add a method called setDuration() to the view.

What this method does is that it takes a float, the duration in seconds, formats it to a text using some presentation logic to it, and then sets this text using the setText() method. In this case, the presentation logic rounds to the nearest minute and then splits the duration in minutes and hours. I also contains logic to handle display of “1 minute” correctly without having to display “1 minute(s)”. Then it inserts the formatted time into a template that is defined like this:

It starts with “Duration:” and then it will display the formatted duration in bold letters.

For clarity, I’ve used string literals in the code. In normal code, it would be better to move these string literals to strings.xml so that they can be localized.

Using the view

Let’s start using our new view. In our onCreate() after we set the content view lets add this:

Here we’ve added five different DurationTextView to try out our new view and test it. The result:

Now this looks nice. But we might want to use the formatted durations in other ways than just display it like that, with “Duration:” in front.

Adding xml attributes

If we could specify the template we can use this view for a lot more. What we could do is to add another method that let us set the template. However, the template is unlikely to change very much. The duration needs to be set dynamically, but the template will probably be the same all the time. So, instead of a new method, let’s add an XML attribute to our view.

The first step is to create a values/attrs.xml file where we can define the attributes. For this view, we’re just going to add one attribute, namely the template string. The entire attrs.xml looks like this.

First we declare a styleable with the name TemplateTextView. The name can be anything you want, but It’s generally a good idea to name it the same thing as the view that the attributes are going to be used with. In this case I’ve actually not named it “DurationTextView” since I plan to use the “template” attribute on other views as well.

Then we define the actual attribute. We set the name to something descriptive and the format to string. There are many other formats for example “Color”, “Integer”, “Boolean”, “Reference” and so on.

Then we use the new attribute in our layout xml like this

At the root item we’ve added this line

This declares a namespace and enables us to use the attributes we’ve defined in attrs.xml in our layout. In this case we’ve named our prefix “app” but this could be whatever you want (well, almost). The last part or the URI is the package name of your application.

Now we just need to read and apply the value in our view. First we replace the private static final member “TEMPLATE” with a the private member “template”. Then we add this in our constructor:

The first line uses the attribute set, attrs, which is a set of all the attributes that exists in the xml file. The method obtainStyledAttributes() does two main things. First, it filters all the attributes in attrs through the context and applies styles and resolves references to values. Second, it only returns the attributes that you specify. These are specified in the second argument which is an array of references to the wanted attributes. In this case R.styleable.TemplateTextView is an array with only one entry, R.attr.template, which is the attribute we created in the attrs.xml file.

Then we use the typed array to get the value of the attribute. In case there were no template specified, or if the template that was specified did not contain “%s”, then we set the template to be just “%s”. Finally, we also need to remember to recycle the typed array in order to give back the resources to the system.

With some changes to the layout, the app now looks like this

Here, templates has been set for all the views. Also, the last view is a similar view that instead of duration formats distance.

For most Android views, the regular XML attributes are matched with methods to set the same property through code as well. Depending on how your view is going to be used, it might be a good idea to add a method as well.

In the next part we’ll look into drawing our own content and making a view that can display a line chart.

You can find the sources for this part here.

13 Comments

  1. Not Relevant

    See java.util.concurrent.TimeUnit

  2. Cam

    Excellent. Look forward to seeing more.

  3. not required

    how to add the customview via code..without declaring in the XML?

  4. joegreen

    Some parts of the code you use in the article are replaced by html entities like <, which makes reading the code really hard.

  5. valki

    Thanks, exactly what i was looking for

  6. Jaiva

    Thanks, useful example to provide motivation behind adding custom views.

    As someone has mentioned parts of the code showing xml have the lesser than and greater than symbol show up escaped.

  7. Perfect explanation about Creating custom Android views ..Android Training

  8. great put up, very informative. I wonder why the other experts of this sector don’t notice
    this. You must continue your writing. I’m sure, you have a great readers’ base already!

Trackbacks for this post

  1. Dynamics Animations using Custom Views – Sources – Jayway
  2. Creating custom Android views – Part 2: How padding works and how to draw your own content – Jayway
  3. Anchors in RelativeLayouts – Jayway
  4. In ein TextView zeichnen - Android-Hilfe.de

Leave a Reply