package com.material.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.*;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewParent;
/**
* Created by IntelliJ IDEA.
* User: keith.
* Date: 14-10-10.
* Time: 14:47.
*/
public class Slider extends View {
private static final String TAG = Slider.class.getSimpleName();
private static final int MAX = 4;
private static final int MIN = 0;
private static final int StateNormal = 0;
private static final int StateDragging = 1;
public static interface OnValueChangeListener {
void onValueChanged(Slider slider, int value, boolean fromUser);
}
private Paint mPaint;
private int mColor;
private int mTintColor;
private int mThumbRadius;
private int mRippleRadius;
private int mBarHeight;
private int mMax;
private int mProgress;
private int mThumbBorderWidth;
private RectF mUncoveredBarRectF = new RectF();
private RectF mCoveredBarRectF = new RectF();
private Point mThumbCenter = new Point();
private Canvas mMinCanvas;
private Paint mClearPaint;
private PorterDuffXfermode mPorterDuffXFerMode;
private float mCoordinateX;
private int mState = StateNormal;
private OnValueChangeListener mOnValueChangeListener;
public void setOnValueChangeListener(OnValueChangeListener listener) {
this.mOnValueChangeListener = listener;
}
public Slider(Context context) {
this(context, null);
}
public Slider(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public Slider(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.Slider);
mColor = attributes.getColor(R.styleable.Slider_slider_color,
getResources().getColor(R.color.slider_color));
mTintColor = attributes.getColor(R.styleable.Slider_slider_tint_color,
getResources().getColor(R.color.slider_tint_color));
mThumbRadius = attributes.getDimensionPixelSize(R.styleable.Slider_slider_thumb_radius,
getResources().getDimensionPixelSize(R.dimen.slider_thumb_radius));
mRippleRadius = attributes.getDimensionPixelSize(R.styleable.Slider_slider_ripple_radius,
getResources().getDimensionPixelSize(R.dimen.slider_thumb_ripple_radius));
mBarHeight = attributes.getDimensionPixelSize(R.styleable.Slider_slider_bar_height,
getResources().getDimensionPixelSize(R.dimen.slider_bar_height));
mThumbBorderWidth = attributes.getDimensionPixelSize(R.styleable.TrackSlider_slider_thumb_border_width,
getResources().getDimensionPixelSize(R.dimen.slider_thumb_border_width));
mMax = attributes.getInteger(R.styleable.Slider_slider_max, MAX);
mProgress = attributes.getInteger(R.styleable.Slider_slider_progress, MIN);
mCoordinateX = 0.f;
mPaint = new Paint();
mPaint.setAntiAlias(true);
mClearPaint = new Paint();
mClearPaint.setAntiAlias(true);
mMinCanvas = new Canvas();
mPorterDuffXFerMode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
}
@Override
protected int getSuggestedMinimumWidth() {
return mRippleRadius * 4;
}
@Override
protected int getSuggestedMinimumHeight() {
return mRippleRadius * 2;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measure(widthMeasureSpec, true), measure(heightMeasureSpec, false));
}
private int measure(int measureSpec, boolean isWidth) {
int result;
int mode = MeasureSpec.getMode(measureSpec);
int size = MeasureSpec.getSize(measureSpec);
int padding = isWidth ? getPaddingLeft() + getPaddingRight() : getPaddingTop() + getPaddingBottom();
if (mode == MeasureSpec.EXACTLY) {
result = size;
} else {
result = isWidth ? getSuggestedMinimumWidth() : getSuggestedMinimumHeight();
result += padding;
if (mode == MeasureSpec.AT_MOST) {
if (isWidth) {
result = Math.max(result, size);
} else {
result = Math.min(result, size);
}
}
}
return result;
}
@Override
public boolean onTouchEvent(@NonNull MotionEvent event) {
super.onTouchEvent(event);
ViewParent parent = getParent();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
if (event.getX() >= 0 && event.getX() <= getWidth() &&
event.getY() >= 0 && event.getY() <= getHeight()) {
mCoordinateX = getCoordinateX(event);
}
}
break;
case MotionEvent.ACTION_MOVE: {
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
mState = StateDragging;
if (event.getX() >= 0 && event.getX() <= getWidth() &&
event.getY() >= 0 && event.getY() <= getHeight()) {
mCoordinateX = getCoordinateX(event);
Log.v("EventX", event.getX() + ":" + mCoordinateX);
invalidate();
}
}
break;
case MotionEvent.ACTION_UP: {
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(false);
}
calculateProgress();
mState = StateNormal;
invalidate();
}
break;
case MotionEvent.ACTION_CANCEL: {
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(false);
}
calculateProgress();
mState = StateNormal;
invalidate();
}
break;
}
return true;
}
private float getCoordinateX(MotionEvent event) {
return ((getWidth() - getPaddingLeft() - getPaddingRight()) * event.getX()) / getWidth();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
RectF barRectF = getBarRect();
switch (mState) {
case StateNormal: {
calculateThumbCenterPoint();
if (mProgress == MIN) {
Bitmap bitmap = getMinBitmap(canvas);
mMinCanvas.setBitmap(bitmap);
mPaint.setColor(mColor);
mMinCanvas.drawRect(barRectF, mPaint);
mPaint.setColor(mTintColor);
mMinCanvas.drawCircle(mThumbCenter.x, mThumbCenter.y, mThumbRadius, mPaint);
mClearPaint.setColor(Color.TRANSPARENT);
mClearPaint.setXfermode(mPorterDuffXFerMode);
mMinCanvas.drawCircle(mThumbCenter.x, mThumbCenter.y, mThumbRadius - mThumbBorderWidth, mClearPaint);
canvas.drawBitmap(bitmap, 0, 0, null);
bitmap.recycle();
} else if (mProgress == MAX) {
mPaint.setColor(mColor);
canvas.drawRect(barRectF, mPaint);
mPaint.setColor(mTintColor);
canvas.drawRect(getMaxCoveredBarRect(), mPaint);
mPaint.setColor(mTintColor);
canvas.drawCircle(mThumbCenter.x, mThumbCenter.y, mThumbRadius, mPaint);
} else {
mPaint.setColor(mColor);
canvas.drawRect(barRectF, mPaint);
mPaint.setColor(mTintColor);
canvas.drawRect(getCoveredRectF(mProgress), mPaint);
mThumbCenter.set(getThumbCenterX(mProgress), getHeight() / 2);
mPaint.setColor(mTintColor);
canvas.drawCircle(mThumbCenter.x, mThumbCenter.y, mThumbRadius, mPaint);
}
}
break;
case StateDragging: {
mUncoveredBarRectF.left = getPaddingLeft() + mThumbRadius;
mUncoveredBarRectF.right = getWidth() - getPaddingRight() - mThumbRadius;
mUncoveredBarRectF.top = getHeight() / 2.0f + -mBarHeight / 2.0f;
mUncoveredBarRectF.bottom = getHeight() / 2.0f + mBarHeight / 2.0f;
float realX = getThumbCenterX(mCoordinateX);
mPaint.setColor(mColor);
canvas.drawRect(barRectF, mPaint);
mPaint.setColor(mTintColor);
canvas.drawRect(getCoveredRectF(realX), mPaint);
mPaint.setColor(mTintColor);
mThumbCenter.set((int) realX, getHeight() / 2);
canvas.drawCircle(mThumbCenter.x, mThumbCenter.y, mThumbRadius, mPaint);
}
break;
}
/*
canvas.save();
canvas.getClipBounds(mCanvasRect);
mCanvasRect.inset(-mRippleRadius, -mRippleRadius);
canvas.clipRect(mCanvasRect, Region.Op.REPLACE);
canvas.restore();
calculateBarDrawRect();
calculateThumbCenterPoint();
if (mProgress == MIN) {
mMinBitmap = getMinBitmap(canvas);
mMinCanvas.setBitmap(mMinBitmap);
mPaint.setColor(mColor);
mMinCanvas.drawRect(mUncoveredBarRectF, mPaint);
mPaint.setColor(mTintColor);
mMinCanvas.drawRect(mCoveredBarRectF, mPaint);
mPaint.setColor(mTintColor);
mMinCanvas.drawCircle(mThumbCenter.x, mThumbCenter.y, mThumbRadius, mPaint);
mClearPaint.setColor(Color.TRANSPARENT);
mClearPaint.setXfermode(mPorterDuffXFerMode);
mMinCanvas.drawCircle(mThumbCenter.x, mThumbCenter.y, mThumbRadius - mThumbBorderWidth, mClearPaint);
canvas.drawBitmap(mMinBitmap, 0, 0, null);
} else if (mProgress == MAX) {
mPaint.setColor(mColor);
canvas.drawRect(mUncoveredBarRectF, mPaint);
mPaint.setColor(mTintColor);
canvas.drawRect(mCoveredBarRectF, mPaint);
mPaint.setColor(mTintColor);
canvas.drawCircle(mThumbCenter.x, mThumbCenter.y, mThumbRadius, mPaint);
} else {
}
*/
}
private void calculateProgress() {
float width = getMaxThumbCenterX() - getMinThumbCenterX();
float passed = getThumbCenterX(mCoordinateX) - getMinThumbCenterX();
mProgress = Math.round(passed * mMax / width);
Log.v("Progress", mProgress + ":" + width + ":" + passed);
}
private float getThumbCenterX(float x) {
if (x < getPaddingLeft() + mThumbRadius) {
return getMinThumbCenterX();
} else if (x > getWidth() - getPaddingRight() - mThumbRadius) {
return getMaxThumbCenterX();
} else {
int width = getWidth() - getPaddingLeft() - getPaddingRight() - mThumbRadius * 2;
return mThumbRadius + getPaddingLeft() + x * width / getWidth();
}
}
private int getThumbCenterX(int progress) {
if (progress == MIN) {
return getPaddingLeft() + mThumbRadius;
} else if (progress == MAX) {
return getWidth() - getPaddingRight() - mThumbRadius;
} else {
float width = getMaxThumbCenterX() - getMinThumbCenterX();
float passed = mProgress * width / mMax;
return Math.round(getPaddingLeft() + mThumbRadius + passed);
}
}
private Bitmap getMinBitmap(Canvas canvas) {
return Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
}
private float getMinThumbCenterX() {
return getPaddingLeft() + mThumbRadius;
}
private float getMaxThumbCenterX() {
return getWidth() - getPaddingRight() - mThumbRadius;
}
private RectF getMinCoveredBarRect() {
if (mCoveredBarRectF == null) {
mCoveredBarRectF = new RectF();
}
mCoveredBarRectF.left = getPaddingLeft() + mThumbRadius;
mCoveredBarRectF.right = getPaddingLeft() + mThumbRadius;
mCoveredBarRectF.top = getHeight() / 2.0f + -mBarHeight / 2.0f;
mCoveredBarRectF.bottom = getHeight() / 2.0f + mBarHeight / 2.0f;
return mCoveredBarRectF;
}
private RectF getMaxCoveredBarRect() {
if (mCoveredBarRectF == null) {
mCoveredBarRectF = new RectF();
}
mCoveredBarRectF.left = getPaddingLeft() + mThumbRadius;
mCoveredBarRectF.right = getWidth() - getPaddingRight() - mThumbRadius;
mCoveredBarRectF.top = getHeight() / 2.0f + -mBarHeight / 2.0f;
mCoveredBarRectF.bottom = getHeight() / 2.0f + mBarHeight / 2.0f;
return mCoveredBarRectF;
}
private RectF getBarRect() {
if (mUncoveredBarRectF == null) {
mUncoveredBarRectF = new RectF();
}
mUncoveredBarRectF.left = getPaddingLeft() + mThumbRadius;
mUncoveredBarRectF.right = getWidth() - getPaddingRight() - mThumbRadius;
mUncoveredBarRectF.top = getHeight() / 2.0f + -mBarHeight / 2.0f;
mUncoveredBarRectF.bottom = getHeight() / 2.0f + mBarHeight / 2.0f;
return mUncoveredBarRectF;
}
private RectF getCoveredRectF(float x) {
if (mCoveredBarRectF == null) {
mCoveredBarRectF = new RectF();
}
mCoveredBarRectF.left = getPaddingLeft() + mThumbRadius;
mCoveredBarRectF.right = x;
mCoveredBarRectF.top = getHeight() / 2.0f + -mBarHeight / 2.0f;
mCoveredBarRectF.bottom = getHeight() / 2.0f + mBarHeight / 2.0f;
return mCoveredBarRectF;
}
private RectF getCoveredRectF(int progress) {
if (mCoveredBarRectF == null) {
mCoveredBarRectF = new RectF();
}
mCoveredBarRectF.left = getPaddingLeft() + mThumbRadius;
mCoveredBarRectF.right = getThumbCenterX(progress);
mCoveredBarRectF.top = getHeight() / 2.0f + -mBarHeight / 2.0f;
mCoveredBarRectF.bottom = getHeight() / 2.0f + mBarHeight / 2.0f;
return mCoveredBarRectF;
}
private void calculateThumbCenterPoint() {
if (mProgress == MIN) {
mThumbCenter.set(mThumbRadius, getHeight() / 2);
} else if (mProgress == mMax) {
mThumbCenter.set(getWidth() - getPaddingRight() - mThumbRadius, getHeight() / 2);
}
}
}