Thursday, February 25, 2010

Creating a custom widget Calendar

In android, there is DatePicker and DatePickerDialog. But these two have lot of restrictions. So I decided to create a Calendar widget for one of my Android application.


The features of this calendar are,
- Set the startDate and endDate.
- Set a different color for dates that is coming outside the range of startDate and endDate.


Create two Spinners, one for Month and one for year. Create a TableLayout and add the TextView array, you can use buttons too, its up to you.

Creating spinner and adding values to it is straight forward. For year, from startDate and endDate, create the String array for year. MonthSpinner will be updated when you click on the startYear and endYear.

For creating spinner I created a function as,


public Spinner createSpinner(String[] results) {
Spinner spinner = new Spinner(ctContext);
ArrayAdapter adapter = new ArrayAdapter(ctContext,
android.R.layout.simple_spinner_item, results);
spinner.setAdapter(adapter);
return spinner;
}

Creating the spinner and add values to it for month and year as,


String[] months = new String[] { "January", "February", "March", "April",
"May", "June", "July", "August", "September", "October",
"November", "December" };
Spinner monthSpinner = createSpinner(months);

String[] rangeOfYears = new String[endYear - startYear + 1];
for (int iIndex = 0; iIndex < (endYear - startYear + 1); iIndex++) {
rangeOfYears[iIndex] = "" + tempStartYear;
tempStartYear++;
}
Spinner yearSpinner = createSpinner(rangeOfYears);


As I need a lot of textView for this application, created function as,


public TextView createTextView(String label, int width, int height,
float textSize, int textColor) {
TextView tv = new TextView(ctContext);
tv.setText(label);
tv.setWidth(width);
tv.setHeight(height);
tv.setTextSize(textSize);
tv.setTextColor(textColor);
return tv;
}

Creating textView in the tableLayout as,


TextView[] row = new TextView[49];
TableRow rowDate = new TableRow(ctContext);
for (int m = 0; m < 7; m++) {
rowDate = new TableRow(ctContext);
for (int n = 0; n < 7; n++) {
if (m == 0)
row[n] = createTextView(days[n], 30, 30, 14, 0xff00ff00);
else {
row[m * 7 + n] = createTextView(" ", 30, 30, 14, 0xff00ff00);
}
rowDate.addView(row1[m * 7 + n]);
}
layout.addView(rowDate);
}


Then for displaying days, I am using tableLayout with an array of TextViews. For getting the calendar date velues, I am using Georgian Calendar and for getting currentDate, using java.util.Calendar

I have a function called refreshCalendar(), that will be called everytime when you click on month or year spinner.

refreshCalendar() is the core of my Calendar application. I am passing month and the year value to this function, Then the GeorgianCalendar will create the corresponding calendar for me. This is also redrawing the calendar every time when click on month or year.


public void refreshCalendar(int year, int month) {
cal = new GregorianCalendar(year, month - 1, 1); // Create
nod = cal.getActualMaximum(GregorianCalendar.DAY_OF_MONTH);
som = cal.get(GregorianCalendar.DAY_OF_WEEK);
int dateLabel = 0;
for (int j = 1; j < 7; j++) {
for (int index = 0; index < 7; index++) {
if (j == 1 && (som - 1) > index) {
row1[j * 7 + index].setText(" ");
row1[j * 7 + index].setFocusable(false);
row1[j * 7 + index].setClickable(false);
} else if (((som - 1) <= index && j == 1) || j > 1) {
row1[j * 7 + index].setText(" " + (dateLabel + 1));
row1[j * 7 + index].setId((dateLabel + 1));
if (currentSelectedYear == endYear
&& currentSelectedMonth == endMonth
&& (dateLabel + 1) > endMonth) {
row1[j * 7 + index].setFocusable(false);
row1[j * 7 + index].setClickable(false);
} else if (currentSelectedYear == startYear
&& currentSelectedMonth == startMonth
&& (dateLabel + 1) < startDate) {
row1[j * 7 + index].setFocusable(false);
row1[j * 7 + index].setClickable(false);
} else {
row1[j * 7 + index].setFocusable(true);
row1[j * 7 + index].setClickable(true);
row1[j * 7 + index].setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Toast.makeText(ctContext," " + v.getId() + " mon : "+currentSelectedMonth+ " yr : "+ currentSelectedYear,Toast.LENGTH_SHORT).show();
}
});
dateLabel++;
}
if (dateLabel > nod) {
row1[j * 7 + index].setText(" ");
row1[j * 7 + index].setFocusable(false);
row1[j * 7 + index].setClickable(false);
}
}
}
}

Animation through Bitmap In Android

In Android, we can do frame by frame animation on Bitmaps. For this, first convert the bitmap into drawable objects.

Create a class called "BitmapDrawable" which extends Drawable and pass the bitmap to its constructor. In the draw(), draw the bitmap.


class BitmapDrawable extends Drawable {
private Bitmap mBitmap;

BitmapDrawable(Bitmap b) {
mBitmap = b;
}

@Override
public void draw(Canvas canvas) {
canvas.drawBitmap(mBitmap, 0.0f, 0.0f, null);
}

@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}

@Override
public void setAlpha(int alpha) {
}

@Override
public void setColorFilter(ColorFilter cf) {
}

@Override
public int getIntrinsicWidth() {
return mBitmap.getWidth();
}

@Override
public int getIntrinsicHeight() {
return mBitmap.getHeight();
}

@Override
public int getMinimumWidth() {
return mBitmap.getWidth();
}

@Override
public int getMinimumHeight() {
return mBitmap.getHeight();
}

public Bitmap getBitmap() {
return mBitmap;
}
}


Create the objects of the "BitmapDrawable" and add the "BitmapDrawable" to the AnimationDrawable using addFrame().


AnimationDrawable animDrawable = new AnimationDrawable();
Drawable frame1 = new BitmapDrawable(bmp);
animDrawable.addFrame(frame1, 250);


Create as many drawable as needed and add it to the AnimationDrawable.


Drawable frame1 = new BitmapDrawable(b1);
animDrawable.addFrame(frame1, 250);
Drawable frame2 = new BitmapDrawable(b2);
animDrawable.addFrame(frame2, 250);
Drawable frame3 = new BitmapDrawable(b3);
animDrawable.addFrame(frame3, 250);
Drawable frame4 = new BitmapDrawable(b4);
animDrawable.addFrame(frame4, 250);
Drawable frame5 = new BitmapDrawable(b5);
animDrawable.addFrame(frame5, 250);
Drawable frame6 = new BitmapDrawable(b6);
animDrawable.addFrame(frame6, 250);

Then create an ImageView and set the background as the animationDrawable.


ImageView img = new ImageView(this);
img.setBackgroundDrawable(animDrawable);


Lastly, create a Handler and call animDrawable.start();


Handler startAnimation = new Handler() {
public void handleMessage(Message msg) {
super.handleMessage(msg);
animDrawable.start();
}
};


and call the handler as


Message msg = new Message();
startAnimation.sendMessage(msg);

Soft KeyBoard in Android

If in a screen, we have more than one view or we have a tab activity, then we may need to hide the virtual keyboard, if it is open. We can do this using


InputMethodManager imm = context.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(this.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);


Always hide soft key board

To always hide the soft keyboprad, use one of the two methods.


InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(myEditText.getWindowToken(), 0);


or

getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);

Soft Keyboard in Landscape mode

In landscape mode soft keyboard will show in full screen. So edittext of password type won't work. For this, just add this API,


editText.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);

This will resize the soft keyboard and will show the edit text.

To get the events of soft keyboard, we can use


editText.addTextChangedListener(new TextWatcher());


TextWatcher interface contains three functions as,


- afterTextChanged();
- beforeTextChanged();
- onTextChanged();


which will be called accordingly.

Androidians