/*
* Copyright (C) 2008 OpenIntents.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//package org.openintents.widget;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* Interface for notifications of position change of slider.
*
* @author Peli
*/
interface OnColorChangedListener {
/**
* This method is called when the user changed the color.
*
* This works in touch mode, by dragging the along the
* color circle with the finger.
*/
void onColorChanged(View view, int newColor);
/**
* This method is called when the user clicks the center button.
*
* @param colorcircle
* @param newColor
*/
void onColorPicked(View view, int newColor);
}
/**
* ColorCircle.
*
* @author Peli, based on API demo code.
*
*/
public class ColorCircle extends View {
private float center_radius;
private final static float CENTER_RADIUS_SCALE = 0.4f;
private Paint mPaint;
private Paint mCenterPaint;
private int[] mColors;
private OnColorChangedListener mListener;
/**
* Constructor. This version is only needed for instantiating the object
* manually (not from a layout XML file).
*
* @param context
*/
public ColorCircle(Context context) {
super(context);
init();
}
/**
* Construct object, initializing with any attributes we understand from a
* layout file.
*
* These attributes are defined in res/values/attrs.xml .
*
* @see android.view.View#View(android.content.Context,
* android.util.AttributeSet, java.util.Map)
*/
public ColorCircle(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO what happens with inflateParams
init();
}
/**
* Initializes variables.
*/
void init() {
mColors = new int[] {
0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF, 0xFF00FF00,
0xFFFFFF00, 0xFFFF0000
};
Shader s = new SweepGradient(0, 0, mColors, null);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setShader(s);
mPaint.setStyle(Paint.Style.STROKE);
mCenterPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mCenterPaint.setStrokeWidth(5);
}
private boolean mTrackingCenter;
private boolean mHighlightCenter;
@Override
protected void onDraw(Canvas canvas) {
float outer_radius = Math.min(getWidth(), getHeight())/2;
float touch_feedback_ring = center_radius + 2*mCenterPaint.getStrokeWidth();
float r = (outer_radius + touch_feedback_ring) / 2;
canvas.translate(getWidth()/2, getHeight()/2);
mPaint.setStrokeWidth(outer_radius - touch_feedback_ring);
// This is the main "color ring"
canvas.drawCircle(0, 0, r, mPaint);
// This is the center "activation button" circle
canvas.drawCircle(0, 0, center_radius, mCenterPaint);
if (mTrackingCenter) {
int c = mCenterPaint.getColor();
mCenterPaint.setStyle(Paint.Style.STROKE);
if (mHighlightCenter) {
mCenterPaint.setAlpha(0xFF);
} else {
mCenterPaint.setAlpha(0x80);
}
// The skinny ring around the center to indicate that it is being pressed
canvas.drawCircle(0, 0,
center_radius + mCenterPaint.getStrokeWidth(),
mCenterPaint);
mCenterPaint.setStyle(Paint.Style.FILL);
mCenterPaint.setColor(c);
}
}
/**
* @see android.view.View#measure(int, int)
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int max_width = MeasureSpec.getSize(widthMeasureSpec);
int max_height = MeasureSpec.getSize(heightMeasureSpec);
int size = Math.min(max_width, max_height);
this.center_radius = CENTER_RADIUS_SCALE * size/2;
setMeasuredDimension(size, size);
}
public void setColor(int color) {
mCenterPaint.setColor(color);
invalidate();
}
public int getColor() {
return mCenterPaint.getColor();
}
public void setOnColorChangedListener(
OnColorChangedListener colorListener) {
mListener = colorListener;
}
private int ave(int s, int d, float p) {
return s + java.lang.Math.round(p * (d - s));
}
private int interpColor(int colors[], float unit) {
if (unit <= 0) {
return colors[0];
}
if (unit >= 1) {
return colors[colors.length - 1];
}
float p = unit * (colors.length - 1);
int i = (int)p;
p -= i;
// now p is just the fractional part [0...1) and i is the index
int c0 = colors[i];
int c1 = colors[i+1];
int a = ave(Color.alpha(c0), Color.alpha(c1), p);
int r = ave(Color.red(c0), Color.red(c1), p);
int g = ave(Color.green(c0), Color.green(c1), p);
int b = ave(Color.blue(c0), Color.blue(c1), p);
return Color.argb(a, r, g, b);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX() - getWidth()/2;
float y = event.getY() - getHeight()/2;
boolean inCenter = PointF.length(x, y) <= center_radius;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mTrackingCenter = inCenter;
if (inCenter) {
mHighlightCenter = true;
invalidate();
break;
}
case MotionEvent.ACTION_MOVE:
if (mTrackingCenter) {
if (mHighlightCenter != inCenter) {
mHighlightCenter = inCenter;
invalidate();
}
} else {
float angle = (float)java.lang.Math.atan2(y, x);
// need to turn angle [-PI ... PI] into unit [0....1]
float unit = angle/(2*(float) Math.PI);
if (unit < 0) {
unit += 1;
}
int newcolor = interpColor(mColors, unit);
mCenterPaint.setColor(newcolor);
if (mListener != null) {
mListener.onColorChanged(this, newcolor);
}
invalidate();
}
break;
case MotionEvent.ACTION_UP:
if (mTrackingCenter) {
if (inCenter) {
if (mListener != null) {
mListener.onColorPicked(this, mCenterPaint.getColor());
}
}
mTrackingCenter = false; // so we draw w/o halo
invalidate();
}
break;
}
return true;
}
}