/*
* Copyright 2012 Lars Werkman
*
* 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 com.larswerkman.holocolorpicker;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import tv.piratemedia.lightcontroler.R;
public class SVBar extends View {
/*
* Constants used to save/restore the instance state.
*/
private static final String STATE_PARENT = "parent";
private static final String STATE_COLOR = "color";
private static final String STATE_SATURATION = "saturation";
private static final String STATE_VALUE = "value";
private static final String STATE_ORIENTATION = "orientation";
/**
* Constants used to identify orientation.
*/
private static final boolean ORIENTATION_HORIZONTAL = true;
private static final boolean ORIENTATION_VERTICAL = false;
/**
* Default orientation of the bar.
*/
private static final boolean ORIENTATION_DEFAULT = ORIENTATION_HORIZONTAL;
/**
* The thickness of the bar.
*/
private int mBarThickness;
/**
* The length of the bar.
*/
private int mBarLength;
private int mPreferredBarLength;
/**
* The radius of the pointer.
*/
private int mBarPointerRadius;
/**
* The radius of the halo of the pointer.
*/
private int mBarPointerHaloRadius;
/**
* The position of the pointer on the bar.
*/
private int mBarPointerPosition;
/**
* {@code Paint} instance used to draw the bar.
*/
private Paint mBarPaint;
/**
* {@code Paint} instance used to draw the pointer.
*/
private Paint mBarPointerPaint;
/**
* {@code Paint} instance used to draw the halo of the pointer.
*/
private Paint mBarPointerHaloPaint;
/**
* The rectangle enclosing the bar.
*/
private RectF mBarRect = new RectF();
/**
* {@code Shader} instance used to fill the shader of the paint.
*/
private Shader shader;
/**
* {@code true} if the user clicked on the pointer to start the move mode. <br>
* {@code false} once the user stops touching the screen.
*
* @see #onTouchEvent(android.view.MotionEvent)
*/
private boolean mIsMovingPointer;
/**
* The ARGB value of the currently selected color.
*/
private int mColor;
/**
* An array of floats that can be build into a {@code Color} <br>
* Where we can extract the Saturation and Value from.
*/
private float[] mHSVColor = new float[3];
/**
* Factor used to calculate the position to the Saturation/Value on the bar.
*/
private float mPosToSVFactor;
/**
* Factor used to calculate the Saturation/Value to the postion on the bar.
*/
private float mSVToPosFactor;
/**
* {@code ColorPicker} instance used to control the ColorPicker.
*/
private ColorPicker mPicker = null;
/**
* Used to toggle orientation between vertical and horizontal.
*/
private boolean mOrientation;
public SVBar(Context context) {
super(context);
init(null, 0);
}
public SVBar(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public SVBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs, defStyle);
}
private void init(AttributeSet attrs, int defStyle) {
final TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.ColorBars, defStyle, 0);
final Resources b = getContext().getResources();
mBarThickness = a.getDimensionPixelSize(
R.styleable.ColorBars_bar_thickness,
b.getDimensionPixelSize(R.dimen.bar_thickness));
mBarLength = a.getDimensionPixelSize(R.styleable.ColorBars_bar_length,
b.getDimensionPixelSize(R.dimen.bar_length));
mPreferredBarLength = mBarLength;
mBarPointerRadius = a.getDimensionPixelSize(
R.styleable.ColorBars_bar_pointer_radius,
b.getDimensionPixelSize(R.dimen.bar_pointer_radius));
mBarPointerHaloRadius = a.getDimensionPixelSize(
R.styleable.ColorBars_bar_pointer_halo_radius,
b.getDimensionPixelSize(R.dimen.bar_pointer_halo_radius));
mOrientation = a.getBoolean(
R.styleable.ColorBars_bar_orientation_horizontal, ORIENTATION_DEFAULT);
a.recycle();
mBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBarPaint.setShader(shader);
mBarPointerPosition = (mBarLength / 2) + mBarPointerHaloRadius;
mBarPointerHaloPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBarPointerHaloPaint.setColor(Color.BLACK);
mBarPointerHaloPaint.setAlpha(0x50);
mBarPointerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBarPointerPaint.setColor(0xff81ff00);
mPosToSVFactor = 1 / ((float) mBarLength / 2);
mSVToPosFactor = ((float) mBarLength / 2) / 1;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int intrinsicSize = mPreferredBarLength
+ (mBarPointerHaloRadius * 2);
// Variable orientation
int measureSpec;
if (mOrientation == ORIENTATION_HORIZONTAL) {
measureSpec = widthMeasureSpec;
}
else {
measureSpec = heightMeasureSpec;
}
int lengthMode = MeasureSpec.getMode(measureSpec);
int lengthSize = MeasureSpec.getSize(measureSpec);
int length;
if (lengthMode == MeasureSpec.EXACTLY) {
length = lengthSize;
}
else if (lengthMode == MeasureSpec.AT_MOST) {
length = Math.min(intrinsicSize, lengthSize);
}
else {
length = intrinsicSize;
}
int barPointerHaloRadiusx2 = mBarPointerHaloRadius * 2;
mBarLength = length - barPointerHaloRadiusx2;
if(mOrientation == ORIENTATION_VERTICAL) {
setMeasuredDimension(barPointerHaloRadiusx2,
(mBarLength + barPointerHaloRadiusx2));
}
else {
setMeasuredDimension((mBarLength + barPointerHaloRadiusx2),
barPointerHaloRadiusx2);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// Fill the rectangle instance based on orientation
int x1, y1;
if (mOrientation == ORIENTATION_HORIZONTAL) {
x1 = (mBarLength + mBarPointerHaloRadius);
y1 = mBarThickness;
mBarLength = w - (mBarPointerHaloRadius * 2);
mBarRect.set(mBarPointerHaloRadius,
(mBarPointerHaloRadius - (mBarThickness / 2)),
(mBarLength + (mBarPointerHaloRadius)),
(mBarPointerHaloRadius + (mBarThickness / 2)));
}
else {
x1 = mBarThickness;
y1 = (mBarLength + mBarPointerHaloRadius);
mBarLength = h - (mBarPointerHaloRadius * 2);
mBarRect.set((mBarPointerHaloRadius - (mBarThickness / 2)),
mBarPointerHaloRadius,
(mBarPointerHaloRadius + (mBarThickness / 2)),
(mBarLength + (mBarPointerHaloRadius)));
}
// Update variables that depend of mBarLength.
if(!isInEditMode()){
shader = new LinearGradient(mBarPointerHaloRadius, 0,
x1, y1, new int[] {
0xffffffff, Color.HSVToColor(mHSVColor), 0xff000000 },
null, Shader.TileMode.CLAMP);
} else {
shader = new LinearGradient(mBarPointerHaloRadius, 0,
x1, y1, new int[] {
0xffffffff, 0xff81ff00, 0xff000000 }, null,
Shader.TileMode.CLAMP);
Color.colorToHSV(0xff81ff00, mHSVColor);
}
mBarPaint.setShader(shader);
mPosToSVFactor = 1 / ((float) mBarLength / 2);
mSVToPosFactor = ((float) mBarLength / 2) / 1;
float[] hsvColor = new float[3];
Color.colorToHSV(mColor, hsvColor);
if (hsvColor[1] < hsvColor[2]) {
mBarPointerPosition = Math.round((mSVToPosFactor * hsvColor[1])
+ mBarPointerHaloRadius);
} else {
mBarPointerPosition = Math
.round((mSVToPosFactor * (1 - hsvColor[2]))
+ mBarPointerHaloRadius + (mBarLength / 2));
}
if(isInEditMode()){
mBarPointerPosition = (mBarLength / 2) + mBarPointerHaloRadius;
}
}
@Override
protected void onDraw(Canvas canvas) {
// Draw the bar.
canvas.drawRect(mBarRect, mBarPaint);
// Calculate the center of the pointer.
int cX, cY;
if (mOrientation == ORIENTATION_HORIZONTAL) {
cX = mBarPointerPosition;
cY = mBarPointerHaloRadius;
}
else {
cX = mBarPointerHaloRadius;
cY = mBarPointerPosition;
}
// Draw the pointer halo.
canvas.drawCircle(cX, cY, mBarPointerHaloRadius, mBarPointerHaloPaint);
// Draw the pointer.
canvas.drawCircle(cX, cY, mBarPointerRadius, mBarPointerPaint);
};
@Override
public boolean onTouchEvent(MotionEvent event) {
getParent().requestDisallowInterceptTouchEvent(true);
// Convert coordinates to our internal coordinate system
float dimen;
if (mOrientation == ORIENTATION_HORIZONTAL) {
dimen = event.getX();
}
else {
dimen = event.getY();
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mIsMovingPointer = true;
// Check whether the user pressed on the pointer
if (dimen >= (mBarPointerHaloRadius)
&& dimen <= (mBarPointerHaloRadius + mBarLength)) {
mBarPointerPosition = Math.round(dimen);
calculateColor(Math.round(dimen));
mBarPointerPaint.setColor(mColor);
invalidate();
}
break;
case MotionEvent.ACTION_MOVE:
if (mIsMovingPointer) {
// Move the the pointer on the bar.
if (dimen >= mBarPointerHaloRadius
&& dimen <= (mBarPointerHaloRadius + mBarLength)) {
mBarPointerPosition = Math.round(dimen);
calculateColor(Math.round(dimen));
mBarPointerPaint.setColor(mColor);
if (mPicker != null) {
mPicker.setNewCenterColor(mColor);
mPicker.changeOpacityBarColor(mColor);
}
invalidate();
} else if (dimen < mBarPointerHaloRadius) {
mBarPointerPosition = mBarPointerHaloRadius;
mColor = Color.WHITE;
mBarPointerPaint.setColor(mColor);
if (mPicker != null) {
mPicker.setNewCenterColor(mColor);
mPicker.changeOpacityBarColor(mColor);
}
invalidate();
} else if (dimen > (mBarPointerHaloRadius + mBarLength)) {
mBarPointerPosition = mBarPointerHaloRadius + mBarLength;
mColor = Color.BLACK;
mBarPointerPaint.setColor(mColor);
if (mPicker != null) {
mPicker.setNewCenterColor(mColor);
mPicker.changeOpacityBarColor(mColor);
}
invalidate();
}
}
break;
case MotionEvent.ACTION_UP:
mIsMovingPointer = false;
break;
}
return true;
}
/**
* Set the pointer on the bar. With the saturation value.
*
* @param saturation
* float between 0 > 1
*/
public void setSaturation(float saturation) {
mBarPointerPosition = Math.round((mSVToPosFactor * saturation)
+ mBarPointerHaloRadius);
calculateColor(mBarPointerPosition);
mBarPointerPaint.setColor(mColor);
// Check whether the Saturation/Value bar is added to the ColorPicker
// wheel
if (mPicker != null) {
mPicker.setNewCenterColor(mColor);
mPicker.changeOpacityBarColor(mColor);
}
invalidate();
}
/**
* Set the pointer on the bar. With the Value value.
*
* @param value
* float between 0 > 1
*/
public void setValue(float value) {
mBarPointerPosition = Math.round((mSVToPosFactor * (1 - value))
+ mBarPointerHaloRadius + (mBarLength / 2));
calculateColor(mBarPointerPosition);
mBarPointerPaint.setColor(mColor);
// Check whether the Saturation/Value bar is added to the ColorPicker
// wheel
if (mPicker != null) {
mPicker.setNewCenterColor(mColor);
mPicker.changeOpacityBarColor(mColor);
}
invalidate();
}
/**
* Set the bar color. <br>
* <br>
* Its discouraged to use this method.
*
* @param color
*/
public void setColor(int color) {
int x1, y1;
if(mOrientation) {
x1 = (mBarLength + mBarPointerHaloRadius);
y1 = mBarThickness;
} else {
x1 = mBarThickness;
y1 = (mBarLength + mBarPointerHaloRadius);
}
Color.colorToHSV(color, mHSVColor);
shader = new LinearGradient(mBarPointerHaloRadius, 0,
x1, y1, new int[] {Color.WHITE, color, Color.BLACK}, null,
Shader.TileMode.CLAMP);
mBarPaint.setShader(shader);
calculateColor(mBarPointerPosition);
mBarPointerPaint.setColor(mColor);
if (mPicker != null) {
mPicker.setNewCenterColor(mColor);
if(mPicker.hasOpacityBar())
mPicker.changeOpacityBarColor(mColor);
}
invalidate();
}
/**
* Calculate the color selected by the pointer on the bar.
*
* @param coord
* Coordinate of the pointer.
*/
private void calculateColor(int coord) {
coord = coord - mBarPointerHaloRadius;
if (coord > (mBarLength / 2) && (coord < mBarLength)) {
mColor = Color
.HSVToColor(new float[] {
mHSVColor[0], 1f, 1 - (mPosToSVFactor * (coord - (mBarLength / 2)))
});
} else if (coord > 0 && coord < mBarLength) {
mColor = Color.HSVToColor(new float[]{
mHSVColor[0], (mPosToSVFactor * coord), 1f
});
} else if(coord == (mBarLength / 2)){
mColor = Color.HSVToColor(new float[]{
mHSVColor[0], 1f, 1f
});
} else if (coord <= 0) {
mColor = Color.WHITE;
} else if (coord >= mBarLength) {
mColor = Color.BLACK;
}
}
/**
* Get the currently selected color.
*
* @return The ARGB value of the currently selected color.
*/
public int getColor() {
return mColor;
}
/**
* Adds a {@code ColorPicker} instance to the bar. <br>
* <br>
* WARNING: Don't change the color picker. it is done already when the bar
* is added to the ColorPicker
*
* @see ColorPicker#addSVBar(com.larswerkman.holocolorpicker.SVBar)
* @param picker
*/
public void setColorPicker(ColorPicker picker) {
mPicker = picker;
}
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
Bundle state = new Bundle();
state.putParcelable(STATE_PARENT, superState);
state.putFloatArray(STATE_COLOR, mHSVColor);
float[] hsvColor = new float[3];
Color.colorToHSV(mColor, hsvColor);
if (hsvColor[1] < hsvColor[2]) {
state.putFloat(STATE_SATURATION, hsvColor[1]);
} else {
state.putFloat(STATE_VALUE, hsvColor[2]);
}
return state;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
Bundle savedState = (Bundle) state;
Parcelable superState = savedState.getParcelable(STATE_PARENT);
super.onRestoreInstanceState(superState);
setColor(Color.HSVToColor(savedState.getFloatArray(STATE_COLOR)));
if (savedState.containsKey(STATE_SATURATION)) {
setSaturation(savedState.getFloat(STATE_SATURATION));
} else {
setValue(savedState.getFloat(STATE_VALUE));
}
}
}