Custom Control – Extending Existing ViewGroups

In the previous post we saw how we can extend an existing View to enhance its properties. In the following post, we go a step ahead and extend one of the existing layouts, put in a few controls and turn it into a widget.

Introduction


Here’s an use case, where you need a reusable component called the ProgressTracker, which basically consists of a TextView, an ImageView, a ProgressBar and a Button.The function of the ProgressTracker is to show a Text saying what it is that you are doing, an Image to denote the state of the task and a button that the user can use to perform any action by attaching an OnClickListener() to it.

Requirement

  1. We should be able to rearrange the inner components through xml based upon our need.
  2. The imageview and textview are optional but the rest must be provided.
  3. The ProgressTracker must be inherited from RelativeLayout which will give us more flexibility in arranging the child components.
  4. It should be generic and reusable.
  5. The user should be able to decide what happens when the button is clicked.
  6. The user should be able to know what the state of the ProgressTracker is, ie, if the task has not started, in progress, successful or failed.

Implementation

To stay within the scope of the tutorial, I’ll focus only on the concerned code snippets. You can however go through the complete code to get an idea about how the ProgressTracker can be used using the sample project given at the end of the post.

We begin by extending the RelativeLayout and implementing the constructors. In it, we extract the styleable attributes which we define in the attrs.xml. As the progress bar and the button are not optional elements we extract their ids and check their validity. Incase, either of the component is missing we throw an exception. I am not going through the extraction details as I have already explained it in Custom Controls – Extending Existing Views.

attrs.xml
Based upon our requirements we have four attributes that specify each of the components in the widget.

activity_main.xml
The ProgressTracker can be used in the following manner. Each of the components are defined inside the ProgressTracker and also assigned to their respective roles within the widget.

Constructor of ProgressTracker.java
Do not worry about the resetProgressTracker() method. You can see the complete code to see its implementation.

Next, override the protected onFinishInflate() method which is called once the view and all of its children have been inflated from the xml. Now find the views by their ids.

Once we have the views, we now must delegate a few methods from the child components to the ProgressTracker so that they can be set up from outside using the ProgressTracker object. Lets override the following methods

  • setText(String text)
  • void setMax(int max)
  • int getMax()
  • void setProgress(int progress)
  • int getProgress()
  • void incrementProgressBy(int diff)
  • void setButtontext(String str)
  • void setActionButtonOnClickListener(OnClickListener l)

Apart from these we also add a few new methods to support error notification and status management of the progress tracker. Implementation of all the methods can be found in the sample project code.

As I have mentioned earlier, the imageview and the textview are optional. The textview is standard, so there is not much to explain. However, you need to pay attention to the ImageView.

The ImageView is used to show a separate image based upon the status of the ProgressTracker and there are 4 status as mentioned above. To display the 4 states, we use a level-list drawable that has 4 drawables corresponding to each state. Here’s how it looks. Make sure to set a level-list drawable to the ImageView src for proper functioning. Here’s what a level-list drawable looks like

task_status.xml

We will see the level-list drawable in details in another post.

The corresponding code handling the ImageView in the ProgressTracker is shown below

Here’s a little snippet from the MainActivity which shows you how the ProgressTracker can be used. See MainActivity.java for various combinations and complete implementation.

That is pretty much it. The magic is mainly in the attrs.xml, the constructor and the onFinishInflate() method. If you understand these 3, you will soon be extending the different layouts to make your own reusable components. The rest of the code is to give the ProgressTracker it’s functionality.

In the sample project, I have used 3 different ways the same ProgressTracker can be used. That is the beauty of it. The user is absolutely free in arranging the components in anyway they want. I have unfortunately been unable to make the UI too jazzy for now :P

You can view and download the complete working code here.