package com.robert.lyx.rangesliderbar;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Region;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.animation.AccelerateInterpolator;
import java.util.concurrent.TimeUnit;
public class RangeSliderBar extends View {
private static final String TAG = RangeSliderBar.class.getSimpleName();
private static final long RIPPLE_ANIMATION_DURATION_MS = TimeUnit.MILLISECONDS.toMillis(700);
private static final int DEFAULT_PAINT_STROKE_WIDTH = 5;
private static final int DEFAULT_FILLED_COLOR = Color.parseColor("#FFA500");
private static final int DEFAULT_EMPTY_COLOR = Color.parseColor("#C3C3C3");
private static final float DEFAULT_BAR_HEIGHT_PERCENT = 0.10f;
private static final float DEFAULT_CIRCLE_RADIUS_PERCENT = 0.125f;
private static final float DEFAULT_SLIDER_RADIUS_PERCENT = 0.25f;
private static final int DEFAULT_RANGE_COUNT = 5;
private static final int DEFAULT_HEIGHT_IN_DP = 50;
protected Paint paint;
protected Paint ripplePaint;
protected Paint mPaint;
protected float radius;
protected float circleRadius;
private int currentIndex;
private float currentSlidingX;
private float currentSlidingY;
private float[] circlePositions;
private int filledColor = DEFAULT_FILLED_COLOR;
private int emptyColor = DEFAULT_EMPTY_COLOR;
private float barHeightPercent = DEFAULT_BAR_HEIGHT_PERCENT;
private int rangeCount = DEFAULT_RANGE_COUNT;
private int barHeight;
private float rippleRadius = 0.0f;
private float downX;
private float downY;
private Path innerPath = new Path();
private Path outerPath = new Path();
private float circleRadiusPercent = DEFAULT_CIRCLE_RADIUS_PERCENT;
private float sliderRadiusPercent = DEFAULT_SLIDER_RADIUS_PERCENT;
private int layoutHeight;
public RangeSliderBar(Context context) {
this(context, null);
}
public RangeSliderBar(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public RangeSliderBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if (attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RangeSliderView);
TypedArray sa = context.obtainStyledAttributes(attrs, new int[]{android.R.attr.layout_height});
try {
layoutHeight = sa.getLayoutDimension(
0, 0);
rangeCount = a.getInt(
R.styleable.RangeSliderView_rangeCount, DEFAULT_RANGE_COUNT);
filledColor = a.getColor(
R.styleable.RangeSliderView_filledColor, DEFAULT_FILLED_COLOR);
emptyColor = a.getColor(
R.styleable.RangeSliderView_emptyColor, DEFAULT_EMPTY_COLOR);
barHeightPercent = a.getFloat(
R.styleable.RangeSliderView_barHeightPercent, DEFAULT_BAR_HEIGHT_PERCENT);
barHeightPercent = a.getFloat(
R.styleable.RangeSliderView_barHeightPercent, DEFAULT_BAR_HEIGHT_PERCENT);
circleRadiusPercent = a.getFloat(
R.styleable.RangeSliderView_circleRadiusPercent, DEFAULT_CIRCLE_RADIUS_PERCENT);
sliderRadiusPercent = a.getFloat(
R.styleable.RangeSliderView_sliderRadiusPercent, DEFAULT_SLIDER_RADIUS_PERCENT);
} finally {
a.recycle();
sa.recycle();
}
}
setBarHeightPercent(barHeightPercent);
setRangeCount(rangeCount);
setCircleRadiusPercent(circleRadiusPercent);
setSliderRadiusPercent(sliderRadiusPercent);
circlePositions = new float[rangeCount];
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeWidth(DEFAULT_PAINT_STROKE_WIDTH);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
ripplePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
ripplePaint.setStrokeWidth(2.0f);
ripplePaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.WHITE);
mPaint.setTextSize(40);
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
getViewTreeObserver().removeOnPreDrawListener(this);
// Update radius after we got new height
updateRadius(getHeight());
// Compute drawing position again
preComputeDrawingPosition();
// Ready to draw now
return true;
}
});
currentIndex = 0;
}
private void updateRadius(int height) {
barHeight = (int) (height * barHeightPercent);
radius = height * sliderRadiusPercent;
circleRadius = height * circleRadiusPercent;
}
public void setRangeCount(int rangeCount) {
if (rangeCount < 2) {
throw new IllegalArgumentException("rangeCount must be >= 2");
}
this.rangeCount = rangeCount;
}
public void setBarHeightPercent(float percent) {
if (percent <= 0.0 || percent > 1.0) {
throw new IllegalArgumentException("Bar height percent must be in (0, 1]");
}
this.barHeightPercent = percent;
}
public void setCircleRadiusPercent(float percent) {
if (percent <= 0.0 || percent > 1.0) {
throw new IllegalArgumentException("circle radius percent must be in (0, 1]");
}
this.circleRadiusPercent = percent;
}
public void setSliderRadiusPercent(float percent) {
if (percent <= 0.0 || percent > 1.0) {
throw new IllegalArgumentException("Slider radius percent must be in (0, 1]");
}
this.sliderRadiusPercent = percent;
}
/**
* Perform all the calculation before drawing, should only run once
*/
private void preComputeDrawingPosition() {
int w = getWidthWithPadding();
int h = getHeightWithPadding();
/** Space between each circle */
int spacing = w / rangeCount;
/** Center vertical */
int y = getPaddingTop() + h / 2;
currentSlidingY = y;
int x = getPaddingLeft() + (spacing / 2);
/** Store the position of each circle index */
for (int i = 0; i < rangeCount; ++i) {
circlePositions[i] = x;
if (i == currentIndex) {
currentSlidingX = x;
}
x += spacing;
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
}
/**
* Measures height according to the passed measure spec
*
* @param measureSpec int measure spec to use
* @return int pixel size
*/
private int measureHeight(int measureSpec) {
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
int result;
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
final int height;
if (layoutHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
height = dpToPx(getContext(), DEFAULT_HEIGHT_IN_DP);
} else if (layoutHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
height = getMeasuredHeight();
} else {
height = layoutHeight;
}
result = height + getPaddingTop() + getPaddingBottom() + (2 * DEFAULT_PAINT_STROKE_WIDTH);
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}
/**
* Measures width according to the passed measure spec
*
* @param measureSpec int measure spec to use
* @return int pixel size
*/
private int measureWidth(int measureSpec) {
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
int result;
if (specMode == MeasureSpec.EX