Android: How to write your own custom composite component

I’ve only been writing code for android for a short time and already my ui requirements got to such proportions, that my layout xml files seemed to explode. I hate copy-pasting the same stuff all over the place and so I spent a day to find out how to “not copy paste” my xml layouts. Instead I wanted a re-usable parametrized tag of my own. What good is xml, if we cannot have our own tags?

The Problem

Imagine you have half a dozen input values for a bunch of measures taken. For each input value you want a label, an input field and a second label showing the unit that is to be used.

[sourcecode language=”xml” wraplines=”true”]
<TextView android:id="@+id/label"
style="@style/LabelFont"
android:text="@string/label_fnord" />
<EditText android:id="@+id/fnord" style="@style/InputBox" />
<TextView android:id="@+id/label_length1"
style="@style/UnitFont"
android:text="@string/metric_length" />
[/sourcecode]

So you got this nice xml which is already using styles to reduce some of the verbosity – six times. It is neither short, nor easy to read and so it is easy to overlook some tiny detail like mismatched labels and ids. It also leads to convoluted java code and more copy-pasting. This is not the solution you are looking for.

The Solution

To effectively create your own tags to describe your UI, you need a so-called composite component. A composite component consists of a few different parts that all work together to produce layouts as simple as this:

[sourcecode language=”xml” wraplines=”true”]
<de.delusions.measure.components.MeasureInput android:id="@+id/weight"
delusions:label="@string/label_weight"
delusions:unit="kg"/>
[/sourcecode]

Example Composite Component

The xml in the example defines a use of a custom component with several custom attributes. This is much easier to read and therefore less prone to errors. The android:id defined here is the id your activity will access to retrieve your custom component like this:

[sourcecode language=”java” light=”true”]MeasureInput input = (MeasureInput) activity.findViewById(this.viewId);[/sourcecode]

How to get there

To arrive at this you need 3 more pieces of code and xml:

  • a custom layout for the component
  • a resouce defining your custom attributes
  • a java class implementing the behaviour of the component

Custom Layout for your component:

The first thing you want is a custom layout for reusable component in your layout/ folder. I opted to do this in xml, because I find it easier and faster than doing it in java code. You can implement the layout as part of the java class.

[sourcecode language=”xml” wraplines=”true”]
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView android:id="@+id/label"
style="@style/LabelFont"
android:layout_width="100dp"
android:layout_height="wrap_content"/>

<EditText android:id="@+id/input"
style="@style/MyNumberBox"
android:layout_width="75dp"
android:layout_height="wrap_content"/>
</merge>
[/sourcecode]

Two important things to note in this xml snippet.

  1. the tag is important because this will be merged into another – surrounding – layout.
  2. the android:ids you define here are the ones that you will use in your custom layout java class to access the parts to make a whole.

Custom Attributes for your component:

If you want a component that can be parametrized through the xml definition you need some attribute definitions of your own. You can put these definitions of your custom attributes in your values/ folder in any xml resource file:

[sourcecode language=”xml” wraplines=”true”]
<resources>
<declare-styleable name="MeasureInput">
<attr name="label" format="string" />
<attr name="unit" format="string" />
</declare-styleable>
</resources>
[/sourcecode]

To access these in the layout for your activity, you need to introduce your own namespace. Just define it in the layout and you’re all set.

[sourcecode language=”xml” light=”true”]
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:delusions= "http://schemas.android.com/apk/res/de.delusions.measure">
[/sourcecode]

You can now access your custom attributes with the namespace you defined:

[sourcecode language=”xml” light=”true”]delusions:unit="kg"[/sourcecode]

Custom Layout class for your component:

Last but not least you need the class that implements what your component does. Things to remember:

Extend one of the existing Layout classes: public class MeasureInput extends LinearLayout {

Inflate your xml layout (if you didn’t opt for the java version)

[sourcecode language=”java”]
final LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.custom_input, this);
[/sourcecode]

You can access the parts of your component by the android:ids and findByViewId as usual:

[sourcecode language=”java” light=”true”] this.labelText = (TextView) findViewById(R.id.label);[/sourcecode]

Write some accessors or data retrieval methods for whoever wants to extract the information that was entered:

[sourcecode language=”java” light=”true”] public String getInput() { return this.input.getText().toString(); }[/sourcecode]

Voilà: your custom composite component is ready to go and be used over and over again!

Demo App ScreenshotUpdate: as requested I added a small demonstration app that shows an example of how to create such a component and can be run on your device or emulator. Download the demo here.

This entry was posted in android. Bookmark the permalink.

17 Responses to Android: How to write your own custom composite component

  1. tom says:

    it`s a great post!
    Do you have demon code about this post?

    thanks!

  2. tom says:

    sorry,it`s “demo code”,not “demon code”

  3. anilrajkumar says:

    i am new to this android, but this merging concept is very useful to me

  4. Kamesh Kompella says:

    Very nice example. May I point out that you would need to recycle the TypedArray a that you obtained in retrieveLabelString in LabeledNumberInput? Will help those who are going to cut and paste from your code.

    • Sonja says:

      Thanks for the tip with the TypedArray. I am going to check it out and fix it. Glad the example can be of help 🙂

  5. Scott says:

    I’m creating a compound component that extends RelativeLayout. When I try to incorporate my custom view into a Honeycomb layout, I receive a NoSuchMethodException in MyCustomComponent.
    What could be the problem?

    • Sonja says:

      What method does it say is missing?

      I have to admit that I have yet to program for Honeycomb. My guess would be that HoneyComb uses different layout classes and you are extending one that is not for tablets? I would check the method name that it says is missing and look for in the documentation. Also I would try to make sure that for Honeycomb I didn’t have to extend a different base class. But since you are already programming for Honeycomb while I am not … it’s all just wild guesses.

  6. Red3 says:

    Thank you, thank you, thank you!

  7. lucas says:

    Thank you for this post… Its very useful. But i have a doubt. Please clarify me. I have created a custom component which has 2 textviews. in xml layout file i set margin properties and align it. it will call the constructor with attrs attributes in my customcomponent class.
    /* In android dev site i read custom components, there in LabelView.java (Custom class) they write two constructor. one
    like public customcomponent (Context context) // from object manually (not from a layout XML file)
    and next public customcomponent (Context context,AttributeSet attrs) // from xml layout passing attributes
    */

    Is it possible to create a new component dynamically from java code(without any coding in xml file) by calling the constructor of my customcomponent class like

    customcomponent custnew = new customcomponent (context); // from my main activity java file
    // constructor of my customcomponent class.
    public customcomponent (Context context) {
    super(context);
    initLabelView();
    }

    Please help me i searched a lot reg this. Because in my app i dnt know how many textview am going to show in the screen. all are dynamic. Sometimes i show 4 textview component(each component has two text view say for eg : name textview and label textview) or sometines 2. Please suggest me what to do. FYI : from xml file if i create a new custom tag it displays correctly, but i dnt know how to create dynamically.

    Another doubt : is it possible to change the text of the already created component.

  8. Pingback: Creating Custom Composite Components | JavaZzzzz….

  9. cri says:

    Hi! Thanks for this great tutorial. What about disabling/enabling the entire custom component?