It is not uncommon for developers to find themselves in
need of a UI component that is either not provided by the platform they are
targeting or is, indeed, provided, but lacks a certain property or behavior.
The answer to both scenarios is a custom UI component.

●Inherit an existing
component (i.e. TextView, ImageView, etc.), and add/override needed functionality. For example,
a CircleImageView that
inherits ImageView, overriding the onDraw() function to restrict the displayed image to a circle, and
adding a loadFromFile() function
to load an image from external memory.

●Create a compound component
out of several components. This approach usually takes advantage ofLayouts to
control how the components are arranged on the screen. For example, a LabeledEditTextthat inherits LinearLayout with horizontal orientation, and contains both a TextView acting as a label and an EditText acting as a text entry field.

●This approach could also
make use of the previous one, i.e., the internal components could be native or
custom.

●The most versatile and most
complex approach is to create a self drawn component. In this case, the
component would inherit the generic View class and override functions like onMeasure() to determine its layout, onDraw() to display its contents, etc. Components created this way
usually depend heavily on Android’s 2D drawing API.

Android Customization Case Study: The CalendarView

Android provides a native CalendarView component. It
performs well and provides the minimum functionality expected from any calendar
component, displaying a full month and highlighting the current day. Some might
say it looks good as well, but only if you are going for a native look, and
have no interest in customizing how it looks whatsoever.

For instance, the CalendarView component provides no way of changing how a certain day
is marked, or what background color to use. There is also no way of adding any
custom text or graphics, to mark a special occasion, for example. In short, the
component looks like this, and almost nothing can be changed:

Calendar View in App Compact Light theme.

Make Your Own

So, how does one go about creating one’s own calendar
view? Any of the approaches above would work. However, practicality will
usually rule out the third option (2D graphics) and leave us with the two other
methods, and we will employ a mixture of both in this article.

2. The Component Class

The previous layout can be included as-is in an Activity or a Fragment
and it will work fine. But encapsulating it as a standalone UI component will
prevent code repetition and allow for a modular design, where each module
handles one responsibility.

Our UI component will be a LinearLayout, to match the root of the XML layout file. Note that only
the important parts are shown from the code. The implementation of the
component resides in CalendarView.java:

The code is pretty straightforward. Upon creation, the
component inflates the XML layout, and when that is done, it assigns the
internal controls to local variables for easier access later on.

3. Some Logic is Needed

To make this component actually behave as a calendar view,
some business logic is in order. It might seem complicated at first, but there
is really not much to it. Let’s break it down:

1.The calendar view is seven
days wide, and it is guaranteed that all months will start somewhere in the
first row.

2.First, we need to figure
out what position the month starts at, then fill all the positions before that
with the numbers from the previous month (30, 29, 28.. etc.) until we reach
position 0.

3.Then, we fill out the days
for the current month (1, 2, 3… etc).

4.After that come the days
for the next month (again, 1, 2, 3.. etc), but this time we only fill the
remaining positions in the last row(s) of the grid.

The following diagram illustrates those steps:

Custom
calendar view business logic.

The width of the grid is already specified to be seven
cells, denoting a weekly calendar, but how about the height? The largest size
for the grid be can be determined by the worst case scenario of a 31-days month
starting on a Saturday, which is the last cell in the first row, and will need
5 more rows to display in full. So, setting the calendar to display six rows
(totalling 42 days) will be sufficient to handle all cases.

But not all months have 31 days! We can avoid
complications arising from that by using Android’s built-in date functionality,
avoiding the need to figure out the number of days ourselves.

As mentioned before, the date functionalities provided by
the Calendar class make the
implementation pretty straightforward. In our component, the updateCalendar() function implements this logic:

4. Customizable at Heart

Since the component responsible for displaying individual
days is a GridView, a good place to
customize how days are displayed is the Adapter, since it is responsible for holding the data and
inflating views for individual grid cells.

For this example, we will require the following from our CalendearView:

The first three requirements are simple to achieve by
changing text attributes and background resources. Let’s us implement a CalendarAdapter to carry out this task. It is simple enough that it can
be a member class in CalendarView.
By overriding the getView() function,
we can achieve the above requirements:

if
(date.getMonth() != today.getMonth() ||
date.getYear() != today.getYear())
{// if this day is outside
current month, grey it out
view.setTextColor(getResources().getColor(R.color.greyed_out));
}elseif
(date.getDate() == today.getDate())
{// if it is today, set it to
blue/bold
view.setTypeface(null,
Typeface.BOLD);
view.setTextColor(getResources().getColor(R.color.today));
}

// set text
view.setText(String.valueOf(date.getDate()));

return
view;
}

The final design requirement takes a bit more work. First,
let’s add the colors for the four seasons in /res/values/colors.xml:

This way, selecting an appropriate color is done by
selecting the appropriate season (monthSeason[currentMonth]) and then picking the corresponding color (rainbow[monthSeason[currentMonth]), this is added to updateCalendar() to make sure the appropriate color is selected whenever
the calendar is changed.

Important Note due to the way HashSet compares objects, the above check eventDays.contains(date) in updateCalendar() will not yield true for date objects unless they are
exactly identical. It does not perform any special checks for the Date data type. To work around this, this check is replaced by
the following code:

5. It Looks Ugly in Design Time

Android’s choice for placeholders in design-time can be
questionable. Fortunately, Android actually instantiates our component in order
to render it in the UI designer, and we can exploit this by calling updateCalendar() in the component constructor. This way the component will
actually make sense in design time.

If initializing the component calls for lots of processing
or loads lots of data, it can affect the performance of the IDE. In this case,
Android provides a nifty function called isInEditMode() that can be used to limit the amount of data used when
the component is actually instantiated in the UI designer. For example, if
there are lots of events to be loaded into the CalendarView, we can use isInEditMode() inside the updateCalendar() function to provide an empty/limited event list in design
mode, and load the real one otherwise.

6. Invoking the Component

The component can be included in XML layout files (a
sample usage can be found in activity_main.xml):

The above code creates a HashSet of events, adds the current day to it, then passes it to CalendarView. As a result, the CalendarView will display the current day in bold blue and also put
the event marker on it:

CalendarView displaying an event

7. Adding Attributes

Another facility provided by Android is to assign
attributes to a custom component. This allows Android developers using the component to select settings via the layout XML
and see the result immediately in the UI designer, as opposed to having to wait
and see how the CalendarView
looks like in runtime. Let’s add the ability to change the date format display
in the component, for example to spell out the full name of the month instead
of the three-letter abbreviation.

To do this, the following steps are needed:

●Declare the attribute.
Let’s call it dateFormat and give it string data type. Add it to /res/values/attrs.xml:

] Build the project and you will notice the displayed date
changes in the UI designer to use
the full name of the month, like “July 2015”. Try providing different values
and see what happens.

Changing the
CalendarView attributes.

8. Interacting With the Component

Have you tried pressing on a specific day? The inner UI
elements in our component still behave in their normal expected way and will
fire events in response to user actions. So, how do we handle those events?

The answer involves two parts:

●Capture events inside the
component, and

●Report events to the
component’s parent (could be a Fragment,
an Activity or even another
component).

The first part is pretty straightforward. For example, to
handle long-pressing grid items, we assign a corresponding listener in our
component class:

There are several methods for reporting events. A direct
and simple one is to copy the way Android does it: it provides an interface to
the component’s events that is implemented by the component’s parent (eventHandler in the above code snippet).

The interface’s functions can be passed any data that is
relevant to the application. In our case, the interface needs to expose one
event handler, which is passed the date for the pressed day. The following
interface is defined in CalendarView:

publicinterfaceEventHandler
{void
onDayLongPress(Date date);
}

The implementation provided by the parent can be supplied
to the calendar view via a setEventHandler(). Here is sample usage from `MainActivity.java’:

Long-pressing a day will fire a long-press event that is
captured and handled by the GridView
and reported by calling onDayLongPress() in the provided implementation, which, in turn, will show
the date of the pressed day on the screen:

Another, more advanced way to handle this is by using
Android’s Intents and BroadcastReceivers. This is particularly helpful when several components need
to be notified of the calendar’s event. For example, if pressing a day in the
calendar requires a text to be displayed in an Activity and a file to be downloaded by a background Service.

Using the previous approach will require the Activity to provide an EventHandler to the component, handling the event and then passing it
to the Service. Instead, having the
component broadcast an Intentand both
the Activity and Service accepting it via their own BroadcastReceivers not only makes life easier but also helps decouple the Activity and the Service in
question.

Conclusion

Behold the awesome power of
Android customization!

So, this is how you create your own custom component in a
few simple steps:

●To make it easier to use
the component in the UI designer, use Android’s isInEditMode() function.

In this article, we created a calendar view as an example,
mainly because the stock calendar view is, in many ways, lacking. But, you are
in no way limited as to what kind of components you can create. You can use the
same technique to create anything you need, the sky is the limit!

Thank you for reading this guide, I wish you the best of
luck in your coding endeavors!

About Author

Ahmed is an entrepreneur with a vivid imagination and 8 years of experience developing high performance applications. He is an expert with data storage/manipulation and high precision industrial applications. He is self-motivated and can work alone or as a part of a team.