Thursday, January 5, 2012

How to write custom CheckBox View?

Many would be interested in writing custom Checkbox in aligning to the theme of the application. For instance changing the checkbox background color and tick color.

You can do in many ways, one usual way is to define a drawables for different states and use selector resource to pick the right image for different states of checkbox.
and set the selector resource as background drawable to CheckBox.In this you need to have different sets of images.

This implementation is about having only two states, checked and not checked. ( basically meaning, how this widget behaves touched and touched again). My requirement was to have tick mark extending outside the checkbox square. Something like this.



So i planned to write a custom view, doing this job for me.

This implementation doesnt include flexible width, height for the checkbox. If you want to do so, use AttributeSet in the constructor and take the height and width from XML attributes and use those values in setMeasuredDimension().

- Written a custom View, a class extending 'View' class and drawing necessary things required to bring the checkbox effect.
- Since I needed 'tick' mark to cross the boundary of checkbox square, i have drawn the checkbox rectangle within View's boundary ( with rectangle width and height is set lesser than View's rectangle ), after drawing the checkbox rectangle then drawn the tick mark image which was positioned in such a way to occupy the entire View rectangle.

View Rect and CheckBox square Rect:



Tick Mark:




- I have given harcoded values of 48,48 for checkboxView,you can change this by gettin g values from xml.
- Overriden onTouchEvent() to know when to draw the tickmark and when not to draw the tick mark. Its a simple check with boolean variable.

- Provided a interface onCheckedChange() to let users listen for checkbox change events.

public interface onCheckedChange {
void onCheckClick(View v);
}

public void setChecklistener(onCheckedChange );

checkBoxView.java
--------------------



import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;

public class CheckBoxView extends View{

public interface onCheckedChange {
void onCheckClick(View v);
}
boolean isTouchDown = false;
Bitmap mTick;
Rect mViewRect;
Rect mCheckboxRect;
Paint mPaint;
int mHeight;

public CheckBoxView(Context context) {
super(context);
}

public CheckBoxView(Context context,AttributeSet attrs) {
super(context,attrs);
mTick = BitmapFactory.decodeResource(context.getResources(), R.drawable.tick);
DisplayMetrics metrics = new DisplayMetrics();
((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(metrics);
mHeight = metrics.heightPixels;
if(mHeight == 320 || mHeight == 240) {
mViewRect = new Rect(0,0,32,32);
mCheckboxRect = new Rect(0,12,25,32);
} else {
mViewRect = new Rect(0,0,48,48);
mCheckboxRect = new Rect(0,20,38,48);
}
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(Color.rgb(0x60,0x33,0x11));
}

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if(mHeight == 320 || mHeight == 240)
setMeasuredDimension(32,32);
else
setMeasuredDimension(48,48);
}

public boolean isChecked() {
return isCheckDrawn;
}

public void setCheckBox(boolean draw) {
isCheckDrawn = draw;
invalidate();
}
private onCheckedChange mCB = null;
public void setChecklistener(onCheckedChange aCB) {
mCB = aCB;
}

int currX;
boolean isMovementDetected = false;
boolean isCheckDrawn = false;

public boolean onTouchEvent(MotionEvent event) {

int pointerX = (int) event.getX();
int pointerY = (int) event.getY();
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
if(mCheckboxRect.contains(pointerX, pointerY) == true)
isTouchDown = true;
break;
case MotionEvent.ACTION_MOVE:
currX = (int) event.getX();
int deltaX = Math.abs(currX - pointerX);

if(deltaX > ViewConfiguration.getTouchSlop())
{
isMovementDetected = true;
}

break;
case MotionEvent.ACTION_UP:
if(isMovementDetected == false && isTouchDown == true) {
if(isCheckDrawn == false) {
isCheckDrawn = true;
} else {
isCheckDrawn = false;
}
if(mCB != null)
mCB.onCheckClick(this);
invalidate();
isTouchDown = false;
}
isMovementDetected = false;
break;
}
return true;
}

public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(mRect1, mPaint);
if(isCheckDrawn == true) {
canvas.drawBitmap(mTick, null, mRect, null);
}
}
}



Example usage of checkBoxView:
-------------------------------

1 comment:

  1. There is no reference of mRect1 and mRect which you have used in onDraw() method.

    ReplyDelete