|
As a Notes developer, I've gotten used to having a date input box with a perpetual calendar. I
thought it would be nice to have one for my Java projects. It turned out to be less difficult
to create than I'd originally imagined.
The one truly nice thing about Java is the richness of it's API. I was able to create the
CalendarComboBox by simply arranging a number of existing components:
JFormattedTextField, BasicArrowButton, JTable, and Popup. Of course,
code always looks simple once it's finished. Originally I didn't know the
BasicArrowButton and Popup classes even existed. It took some poking
around in the API and Java source code related to JComboBox before I tracked them
down.
I also needed to figure out how to build an array to hold the days in a month and leverage
the various date related classes: Calendar, GregorianCalendar, DateFormat, and
DateFormatSymbols. Mr. Dunn's book, Java Rules was particularly useful
in helping me understand how these classes worked.
And last, but not least, were the layout experiments. I got stuck for a few hours on the
calendar display; the buttons in the navigation panel kept changing size, it was very
distracting. Finally realized that part of the problem was the JLabel
component I was using to display the month and year name and the fact that I was using
a BoxLayout. Once I changed the label to a JTextField and
the calendar panel layout to BorderLayout, with the navigation portion
placed in BorderLayout.NORTH and the table in BorderLayout.CENTER
the display started to behave itself.
I ran across a few other snags, they are hightlighted in the code comments. Below are my
reasons for designing the class as I did.
Design Decisions
- Class fields
The values represented by these fields are common to the system the class is
running on. The data is based on the system Locale which is not
likely to change; at least, not during the active life of a running application.
- Field access modifiers
All fields (except popup ) are declared private and
final. This is good coding practice.
The private keyword
helps to enforce encapsulation and forces you to
think about your classes public interface. In this case, only one field, current
needed to be publicly exposed; a gettor method, public Calendar
getDate() was provided to return current as it's reasonable to
assume an external class would need access to the currently selected date.
The keyword final emphasizes that the fields are required and that
references cannot be accidently modifed during the life of an object. It also
notifies the compiler that the code relating to these values can be safely optimized.
Another advantage is that it helps ensure that everything the object requires to
work correctly will be available once it is created; if you fail to initialize a final
variable during object creation the compiler complains.
- Why
popup isn't final
The API recommends using PopupFactory to create Popup
objects. PopupFactory caches popup objects, managing their reuse and
disposal. As the programmer's at Sun have been kind enough to supply me with a class
that can manage popup's it seemed sensible to use it rather than create a final
popup reference and attempt to manage it myself.
- Listeners as inner classes
There are three basic ways to implement listeners: as external classes, as inner
classes or as anonymous classes. The only reason to implement one as an external class
is if it could possibly be used by another class; yet listeners are generally very
specific in nature and certainly are specific in this case so there was nothing to be
gained by implementing them as external classes.
Anonymous listener classes are generally used if they are required by only one
element in the class and if they can be written in nine or ten lines of code. When I
started writing the class I had no idea how long a particular listeners code would be
and I did know that one listener, ButtonListener, would be required by
three elements, not one. So again, there was little to be gained by implementing the
listeners as anonymous classes. Add to that the difficulty of maintaining code that
is peppered with anonymous classes and the choice of using inner classes became even
more attractive.
- The
registerListeners() method
For the most part, this is simply a personal preference. I find it easier to keep
track of listeners when they are all located in one spot. Having a separate method
to handle them just makes life easier for me.
Summary
If you've avoided creating custom components, thinking they're to much trouble or that
you need to be an expert programmer to create them, here's the proof that it just ain't so!
They can be alot easier to create than you realize.
If you end up using the class in one
of your applications please let me know how it fares<g>
Enhancements
David Underhill used the code in a class project, adding some nice features:
- added a constructor which allows the user to specify their own date format if they prefer
- added the option for the user to have the popup collapse automatically when the user
selects a date (however, it is smart enough to not collapse when the user is just browsing months)
- added the ability for the class to track listeners and notify them when a change is made to the
selection. This listener could be expanded further to listen for more specific
of events, but this makes it easy to do that
- made the input field uneditable. This is just the preference for our project, but it seems to make
sense that if you have the box normally you would prefer the user to not manually enter the date.
David has kindly offered to share his code.
CalendarComboBox.java
CalendarComboBoxListener.java
|