Namespaces
Variants
Actions

Building a Java ME Canvas based calendar/date picker

Jump to: navigation, search
Article Metadata

Article
Created: jappit (16 May 2008)
Last edited: hamishwillee (07 Feb 2012)


Contents

Introduction

Here's a calendar component usable on J2ME Canvas. You can use it, for example, as a simple and intuitive date picker.
Frame
You can see this component in action here: Calendar Widget in action.

Source code: CalendarWidget class

This class represents the Calendar itself.

import java.util.Calendar;
import java.util.Date;
 
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
 
public class CalendarWidget
{
}

Let's define some initial variables to hold labels. We'll use in our calendar: month and week day labels

// defining array of string.
static final String[] MONTH_LABELS = new String[]{
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
};
static final String[] WEEKDAY_LABELS = new String[]{
"M", "T", "W", "T", "F", "S", "S"
};

Now we'll define some appearance-related properties. This properties can be customized to make the date picker appear as you want it to look like.

/* starting week day: 0 for monday, 6 for sunday */
public int startWeekday = 0;
 
/* elements padding */
public int padding = 1;
 
/* cells border properties */
public int borderWidth = 4;
public int borderColor = 0x0000ff;
 
/* weekday labels properties */
public Font weekdayFont = Font.getDefaultFont();
public int weekdayBgColor = 0x0000ff;
public int weekdayColor = 0xffffff;
 
/* header (month-year label) properties */
public Font headerFont = Font.getDefaultFont();
public int headerBgColor = 0x0000ff;
public int headerColor = 0xffffff;
 
/* cells properties */
public Font font = Font.getDefaultFont();
public int foreColor = 0x000000;
public int bgColor = 0x9999ff;
public int selectedBgColor = 0xffff00;
public int selectedForeColor = 0xff0000;

Now we'll define some internal properties that we'll use to handle size/time properties.

/* internal properties */
int width = 0;
int height = 0;
int headerHeight = 0;
int weekHeight = 0;
int cellWidth = 0;
int cellHeight = 0;
 
/* internal time properties */
long currentTimestamp = 0;
Calendar calendar = null;
int weeks = 0;

Now, let's define a simple constructor, accepting a Date instance as an argument

public CalendarWidget(Date date)
{
calendar = Calendar.getInstance();
 
//we'll see these 2 methods later
setDate(date);
 
initialize();
}

Then, we'll define some useful methods to set and get the selected date

public Date getSelectedDate()
{
return calendar.getTime();
}
public void setDate(Date d)
{
currentTimestamp = d.getTime();
 
calendar.setTime(d);
 
//weeks number can change, depending on week starting day and month total days
this.weeks = (int)Math.ceil(((double)getStartWeekday() + getMonthDays()) / 7);
}
public void setDate(long timestamp)
{
setDate(new Date(timestamp));
}

Here we will use two methods to initialize calendar properties. This methods will be called by constructor, and should be called each time you change some public properties (like padding, fonts or border width) so that sizes can be recalculated correctly .

void initialize()
{
//let's initialize calendar size
this.cellWidth = font.stringWidth("MM") + 2 * padding;
this.cellHeight = font.getHeight() + 2 * padding;
 
this.headerHeight = headerFont.getHeight() + 2 * padding;
this.weekHeight = weekdayFont.getHeight() + 2 * padding;
 
this.width = 7 * (cellWidth + borderWidth) + borderWidth;
initHeight();
}
void initHeight()
{
this.height =
headerHeight + weekHeight +
this.weeks * (cellHeight + borderWidth) + borderWidth;
}

These two methods will be used to get, respectively, the number of days and the start week day of the current month.

int getMonthDays()
{
int month = calendar.get(Calendar.MONTH);
 
switch(month)
{
case 3:
case 5:
case 8:
case 10:
return 30;
case 1:
int year = calendar.get(Calendar.YEAR);
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) ? 29 : 28;
default:
return 31;
}
}
int getStartWeekday()
{
//let's create a new calendar with same month and year, but with day 1
Calendar c = Calendar.getInstance();
 
c.set(Calendar.MONTH, calendar.get(Calendar.MONTH));
c.set(Calendar.YEAR, calendar.get(Calendar.YEAR));
c.set(Calendar.DAY_OF_MONTH, 1);
 
return (c.get(Calendar.DAY_OF_WEEK) + 5) % 7;
}

Now we must handle key events, to allow user to select the date they want. We handle left/right keys shifting current date by one day, while up/down keys will shift the current date by 7 days.

public void keyPressed(int key)
{
switch(key)
{
case Canvas.UP:
go(-7);
break;
case Canvas.DOWN:
go(7);
break;
case Canvas.RIGHT:
go(1);
break;
case Canvas.LEFT:
go(-1);
break;
}
}
void go(int delta)
{
int prevMonth = calendar.get(Calendar.MONTH);
 
setDate(currentTimestamp + 86400000 * delta);
 
//we have to check if month has changed
//if yes, we have to recalculate month height
//since weeks number could be changed
if(calendar.get(Calendar.MONTH) != prevMonth)
{
initHeight();
}
}

Now that we've finished with utility functions, we can finally paint our date picker.

public void paint(Graphics g)
{
//painting background
g.setColor(bgColor);
g.fillRect(0, 0, width, height);
 
//painting header (month-year label)
g.setFont(headerFont);
g.setColor(headerColor);
g.drawString(MONTH_LABELS[calendar.get(Calendar.MONTH)] + " " + calendar.get(Calendar.YEAR), width / 2, padding, Graphics.TOP | Graphics.HCENTER);
 
//painting week days labels
g.translate(0, headerHeight);
 
g.setColor(weekdayBgColor);
g.fillRect(0, 0, width, weekHeight);
 
g.setColor(weekdayColor);
g.setFont(weekdayFont);
 
for(int i = 0; i < 7; i++)
{
g.drawString(WEEKDAY_LABELS[(i + startWeekday) % 7],
borderWidth + i * (cellWidth + borderWidth) + cellWidth / 2,
padding,
Graphics.TOP | Graphics.HCENTER
);
}
 
//painting cells borders
g.translate(0, weekHeight);
 
g.setColor(borderColor);
 
for(int i = 0; i <= weeks; i++)
{
g.fillRect(0, i * (cellHeight + borderWidth), width, borderWidth);
}
for(int i = 0; i <= 7; i++)
{
g.fillRect(i * (cellWidth + borderWidth), 0, borderWidth, height - headerHeight - weekHeight);
}
 
//painting days
int days = getMonthDays();
int dayIndex = (getStartWeekday() - this.startWeekday + 7) % 7;
 
g.setColor(foreColor);
 
int currentDay = calendar.get(Calendar.DAY_OF_MONTH);
 
for(int i = 0; i < days; i++)
{
int weekday = (dayIndex + i) % 7;
int row = (dayIndex + i) / 7;
 
int x = borderWidth + weekday * (cellWidth + borderWidth) + cellWidth / 2;
int y = borderWidth + row * (cellHeight + borderWidth) + padding;
 
//if this is the current day, we'll use selected bg and fore colors
if(i + 1 == currentDay)
{
g.setColor(selectedBgColor);
g.fillRect(
borderWidth + weekday * (cellWidth + borderWidth),
borderWidth + row * (cellHeight + borderWidth),
cellWidth, cellHeight);
g.setColor(selectedForeColor);
}
 
g.drawString("" + (i + 1), x, y, Graphics.TOP | Graphics.HCENTER);
 
//if this is the current day, we must restore standard fore color
if(i + 1 == currentDay)
{
g.setColor(foreColor);
}
}
//let's translate back!
g.translate(0, - headerHeight - weekHeight);
}

Sample usage

Here's how you can initialize and customize your Canvas based date picker:

calendar = new CalendarWidget(new Date());
 
calendar.headerFont = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_LARGE);
calendar.weekdayFont = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_MEDIUM);
calendar.weekdayBgColor = 0xccccff;
calendar.weekdayColor = 0x0000ff;
calendar.headerColor = 0xffffff;
 
calendar.initialize();

Resources

You can download full source code here:

Comments

Reviewer-approved.png
21 Sep
2009
Article Review by Larry101 (20090921)

This article provides a useful control for selecting dates in Java ME midlets. The example shows how to create a calendar widget, controls that have proven popular on the web (both as JavaScript and server-side controls) and in desktop applications for selecting dates. These controls help avoid problems regarding differing date formats in different countries. Furthermore, if a date must be manually entered, users are often unsure as to whether punctuation such as backslashes or hyphens is necessary.

The code example shows how to create the calendar control using low-level drawing operations. Example code is given demonstrating how to implement the calendar control using a Canvas. The widget also provides several customizable properties, such as the fonts used and the background and foreground colors. The widget is also linked to an instance of the Calendar class in java.util which is used to retrieve information regarding which day of the week a month starts on.

The result of the code is a very professional looking and customizable widget which would be a useful addition to any midlet which requires users to enter date information. When a date needs to be entered, the midlet can simply redirect to a canvas displaying such a calendar control and return once the user has selected a date. The code can take care of formatting the date as required. Perhaps one drawback is that the control does not take the available screen dimensions into account, which could result in a control that doesn’t fit on the screen in smaller devices and which doesn’t make use of all the available screen space on larger devices.

This page was last modified on 7 February 2012, at 06:22.
386 page views in the last 30 days.
Nokia Developer aims to help you create apps and publish them so you can connect with users around the world.

京ICP备05048969号  © Copyright Nokia 2012 All rights reserved