package uz.shift.colorpicker; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import org.solovyev.android.calculator.R; public class LineColorPicker extends View { public static final int HORIZONTAL = 0; public static final int VERTICAL = 1; int[] colors = isInEditMode() ? Palette.DEFAULT : new int[1]; // indicate if nothing selected boolean isColorSelected = false; private Paint paint; private Rect rect = new Rect(); private int selectedColor = colors[0]; private OnColorChangedListener onColorChanged; private int cellSize; private int mOrientation = HORIZONTAL; private boolean isClick = false; private int screenW; private int screenH; public LineColorPicker(Context context, AttributeSet attrs) { super(context, attrs); paint = new Paint(); paint.setStyle(Style.FILL); final TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.LineColorPicker, 0, 0); try { mOrientation = a.getInteger(R.styleable.LineColorPicker_lcp_orientation, HORIZONTAL); if (!isInEditMode()) { final int colorsArrayResId = a.getResourceId(R.styleable.LineColorPicker_lcp_colors, -1); if (colorsArrayResId > 0) { final int[] colors = context.getResources().getIntArray(colorsArrayResId); setColors(colors); } } final int selected = a.getInteger(R.styleable.LineColorPicker_lcp_selectedColorIndex, -1); if (selected != -1) { final int[] currentColors = getColors(); final int currentColorsLength = currentColors != null ? currentColors.length : 0; if (selected < currentColorsLength) { setSelectedColorPosition(selected); } } } finally { a.recycle(); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mOrientation == HORIZONTAL) { drawHorizontalPicker(canvas); } else { drawVerticalPicker(canvas); } } private void drawVerticalPicker(Canvas canvas) { rect.left = 0; rect.top = 0; rect.right = getWidth(); rect.bottom = 0; // 8% int margin = Math.round(getWidth() * 0.08f); for (int i = 0; i < colors.length; i++) { paint.setColor(colors[i]); rect.top = rect.bottom; rect.bottom += cellSize; if (isColorSelected && colors[i] == selectedColor) { rect.left = 0; rect.right = getWidth(); } else { rect.left = margin; rect.right = getWidth() - margin; } canvas.drawRect(rect, paint); } } private void drawHorizontalPicker(Canvas canvas) { rect.left = 0; rect.top = 0; rect.right = 0; rect.bottom = getHeight(); // 8% int margin = Math.round(getHeight() * 0.08f); for (int i = 0; i < colors.length; i++) { paint.setColor(colors[i]); rect.left = rect.right; rect.right += cellSize; if (isColorSelected && colors[i] == selectedColor) { rect.top = 0; rect.bottom = getHeight(); } else { rect.top = margin; rect.bottom = getHeight() - margin; } canvas.drawRect(rect, paint); } } private void onColorChanged(int color) { if (onColorChanged != null) { onColorChanged.onColorChanged(color); } } @Override public boolean onTouchEvent(MotionEvent event) { int actionId = event.getAction(); int newColor; switch (actionId) { case MotionEvent.ACTION_DOWN: isClick = true; break; case MotionEvent.ACTION_UP: newColor = getColorAtXY(event.getX(), event.getY()); setSelectedColor(newColor); if (isClick) { performClick(); } break; case MotionEvent.ACTION_MOVE: newColor = getColorAtXY(event.getX(), event.getY()); setSelectedColor(newColor); break; case MotionEvent.ACTION_CANCEL: isClick = false; break; case MotionEvent.ACTION_OUTSIDE: isClick = false; break; default: break; } return true; } /** * Return color at x,y coordinate of view. */ private int getColorAtXY(float x, float y) { // FIXME: colors.length == 0 -> devision by ZERO.s if (mOrientation == HORIZONTAL) { int left = 0; int right = 0; for (int i = 0; i < colors.length; i++) { left = right; right += cellSize; if (left <= x && right >= x) { return colors[i]; } } } else { int top = 0; int bottom = 0; for (int i = 0; i < colors.length; i++) { top = bottom; bottom += cellSize; if (y >= top && y <= bottom) { return colors[i]; } } } return selectedColor; } @Override protected Parcelable onSaveInstanceState() { // begin boilerplate code that allows parent classes to save state Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); // end ss.selectedColor = this.selectedColor; ss.isColorSelected = this.isColorSelected; return ss; } @Override protected void onRestoreInstanceState(Parcelable state) { // begin boilerplate code so parent classes can restore state if (!(state instanceof SavedState)) { super.onRestoreInstanceState(state); return; } SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); // end this.selectedColor = ss.selectedColor; this.isColorSelected = ss.isColorSelected; } @Override public boolean performClick() { return super.performClick(); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { screenW = w; screenH = h; recalcCellSize(); super.onSizeChanged(w, h, oldw, oldh); } /** * Return currently selected color. */ public int getColor() { return selectedColor; } // @Override // protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // int parentWidth = MeasureSpec.getSize(widthMeasureSpec); // int parentHeight = MeasureSpec.getSize(heightMeasureSpec); // this.setMeasuredDimension(parentWidth, parentHeight); // super.onMeasure(widthMeasureSpec, heightMeasureSpec); // } /** * Set selected color as color value from palette. */ public void setSelectedColor(int color) { // not from current palette if (!containsColor(colors, color)) { return; } // do we need to re-draw view? if (!isColorSelected || selectedColor != color) { this.selectedColor = color; isColorSelected = true; invalidate(); onColorChanged(color); } } /** * Set selected color as index from palete */ public void setSelectedColorPosition(int position) { setSelectedColor(colors[position]); } private int recalcCellSize() { if (mOrientation == HORIZONTAL) { cellSize = Math.round(screenW / (colors.length * 1f)); } else { cellSize = Math.round(screenH / (colors.length * 1f)); } return cellSize; } /** * Return current picker palete */ public int[] getColors() { return colors; } /** * Set picker palette */ public void setColors(int[] colors) { // TODO: selected color can be NOT in set of colors // FIXME: colors can be null this.colors = colors; if (!containsColor(colors, selectedColor)) { selectedColor = colors[0]; } recalcCellSize(); invalidate(); } /** * Return true if palette contains this color */ private boolean containsColor(int[] colors, int c) { for (int i = 0; i < colors.length; i++) { if (colors[i] == c) return true; } return false; } /** * Set onColorChanged listener * * @param l */ public void setOnColorChangedListener(OnColorChangedListener l) { this.onColorChanged = l; } static class SavedState extends BaseSavedState { // required field that makes Parcelables from a Parcel public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; int selectedColor; boolean isColorSelected; SavedState(Parcelable superState) { super(superState); } private SavedState(Parcel in) { super(in); this.selectedColor = in.readInt(); this.isColorSelected = in.readInt() == 1; } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeInt(this.selectedColor); out.writeInt(this.isColorSelected ? 1 : 0); } } }