package greendroid.graphics.drawable;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.LightingColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.Drawable;
import com.cyrilmottier.android.greendroid.R;
import com.google.android.maps.MapView;
/**
* A {@link Drawable} dedicated to {@link MapView}s. A {@link MapPinDrawable}
* displays a rounded pin with a dot in the middle. This class lets you easily
* change the color of the pin as well as the dot in the center of the pin.
*
* @author Cyril Mottier
*/
public class MapPinDrawable extends Drawable {
private static final int COLOR_MODE_UNKNOWN = -1;
private static final int COLOR_MODE_COLOR = 1;
private static final int COLOR_MODE_COLOR_STATE_LIST = 2;
private static final Paint sClearerPaint;
static {
sClearerPaint = new Paint();
sClearerPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
private int mColorMode = COLOR_MODE_UNKNOWN;
private int mPinColor;
private int mDotColor;
private ColorStateList mPinColorStateList;
private ColorStateList mDotColorStateList;
private int mCurrentPinColor;
private int mCurrentDotColor;
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Paint mMapPinPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Bitmap mBase;
private Bitmap mPin;
private Bitmap mDot;
private Bitmap mMapPin;
private boolean mNeedRebuild;
/**
* Create a new {@link MapPinDrawable} that has a single color.
*
* @param res The application resources
* @param color The color of the pin/dot
*/
public MapPinDrawable(Resources res, int color) {
this(res, color, color);
}
/**
* Create a new {@link MapPinDrawable}.
*
* @param res The application resources
* @param pinClor The color of the pin
* @param dotColor The color of the dot
*/
public MapPinDrawable(Resources res, int pinColor, int dotColor) {
initBitmaps(res);
setColors(pinColor, dotColor);
}
/**
* Create a new {@link MapPinDrawable} that may change color depending on
* its current state.
*
* @param res The application resources
* @param color A {@link ColorStateList} object giving a set of colors
* changing depending on the current {@link Drawable}'s state
*/
public MapPinDrawable(Resources res, ColorStateList color) {
this(res, color, color);
}
/**
* Create a new {@link MapPinDrawable} that may change color depending on
* its current state.
*
* @param res The application resources
* @param pinColor A {@link ColorStateList} object giving a set of colors
* for the pin changing depending on the current {@link Drawable}
* 's state
* @param dotColor A {@link ColorStateList} object giving a set of colors
* for the dot changing depending on the current {@link Drawable}
* 's state
*/
public MapPinDrawable(Resources res, ColorStateList pinColor, ColorStateList dotColor) {
initBitmaps(res);
setColors(pinColor, dotColor);
}
private void initBitmaps(Resources res) {
// TODO Cyril: Share those Bitmaps between all instances of
// MapPinDrawable in order to save memory
mBase = BitmapFactory.decodeResource(res, R.drawable.gd_map_pin_base);
mPin = BitmapFactory.decodeResource(res, R.drawable.gd_map_pin_pin);
mDot = BitmapFactory.decodeResource(res, R.drawable.gd_map_pin_dot);
}
/**
* Set the color for the pin/dot
*
* @param pinClor The color of the pin
* @param dotColor The color of the dot
*/
public void setColors(int pinColor, int dotColor) {
if (mColorMode != COLOR_MODE_COLOR || mPinColor != pinColor || mDotColor != dotColor) {
mColorMode = COLOR_MODE_COLOR;
mPinColor = mCurrentPinColor = pinColor;
mDotColor = mCurrentDotColor = dotColor;
mNeedRebuild = true;
}
}
/**
* Set the color for the pin/dot
*
* @param pinClor The color of the pin
* @param dotColor The color of the dot
*/
public void setColors(ColorStateList pinColor, ColorStateList dotColor) {
if (mColorMode != COLOR_MODE_COLOR_STATE_LIST || mPinColorStateList != pinColor || mDotColorStateList != dotColor) {
mColorMode = COLOR_MODE_COLOR_STATE_LIST;
mPinColorStateList = pinColor;
mDotColorStateList = dotColor;
mNeedRebuild = true;
}
}
@Override
public boolean isStateful() {
return true;
}
@Override
public int getIntrinsicWidth() {
return (mBase != null) ? mBase.getWidth() : -1;
}
@Override
public int getIntrinsicHeight() {
return (mBase != null) ? mBase.getHeight() : -1;
}
@Override
protected boolean onStateChange(int[] stateSet) {
if (mColorMode == COLOR_MODE_COLOR_STATE_LIST) {
int pinColor = (mPinColorStateList != null) ? mPinColorStateList.getColorForState(stateSet, Color.BLACK) : Color.BLACK;
int dotColor = (mDotColorStateList != null) ? mDotColorStateList.getColorForState(stateSet, Color.BLACK) : Color.BLACK;
if (mCurrentPinColor != pinColor || mCurrentDotColor != dotColor) {
mCurrentPinColor = pinColor;
mCurrentDotColor = dotColor;
mNeedRebuild = true;
return true;
}
}
return false;
}
@Override
public void draw(Canvas canvas) {
if (mNeedRebuild) {
if (mMapPin == null) {
mMapPin = Bitmap.createBitmap(mBase.getWidth(), mBase.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas c = new Canvas(mMapPin);
c.drawRect(0, 0, mMapPin.getWidth(), mMapPin.getHeight(), sClearerPaint);
// 1 - Draw the base
c.drawBitmap(mBase, 0, 0, null);
// 2 - Draw the pin on top of it
mPaint.setColorFilter(new LightingColorFilter(Color.BLACK, mCurrentPinColor));
c.drawBitmap(mPin, 0, 0, mPaint);
// 3 - Draw the dot on top of everything
mPaint.setColorFilter(new LightingColorFilter(Color.BLACK, mCurrentDotColor));
c.drawBitmap(mDot, 0, 0, mPaint);
mNeedRebuild = false;
}
canvas.drawBitmap(mMapPin, null, getBounds(), mMapPinPaint);
}
@Override
public void setAlpha(int alpha) {
mMapPinPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
mMapPinPaint.setColorFilter(cf);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}