diff --git a/circularrangeslider/src/main/java/com/bikcrum/circularrangeslider/CircularRangeSlider.java b/circularrangeslider/src/main/java/com/bikcrum/circularrangeslider/CircularRangeSlider.java index 9a2017d..65a1785 100644 --- a/circularrangeslider/src/main/java/com/bikcrum/circularrangeslider/CircularRangeSlider.java +++ b/circularrangeslider/src/main/java/com/bikcrum/circularrangeslider/CircularRangeSlider.java @@ -1,5 +1,6 @@ package com.bikcrum.circularrangeslider; +import android.annotation.SuppressLint; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; @@ -10,10 +11,11 @@ import android.graphics.Rect; import android.graphics.RectF; import android.util.AttributeSet; -import android.util.Log; import android.view.MotionEvent; import android.view.View; +import androidx.annotation.NonNull; + public class CircularRangeSlider extends View { //attributes private int max; @@ -21,7 +23,9 @@ public class CircularRangeSlider extends View { private CharSequence[] labels; private int labelColor; private float labelSize; + private int labelInterval; private boolean hideLabel; + private boolean hideZero; private int circleColor; @@ -38,6 +42,8 @@ public class CircularRangeSlider extends View { private int axisColor; private int startFrom; + private float startAngle; + private float endAngle; private float startIndexStepLength; private float startIndexStepWidth; @@ -46,19 +52,18 @@ public class CircularRangeSlider extends View { private float progress; private boolean progressEnabled; private int progressColor; + private float progressLength; private int startIndex; private int endIndex; - private boolean enabled; - //non user required - private Paint paint = new Paint(); + private final Paint paint = new Paint(); private float centerX; private float centerY; private float radius; private float stepsGap; - private RectF ovalBigArc = new RectF(); + private final RectF ovalBigArc = new RectF(); private float startThumbCenterX; private float endThumbCenterX; private float endThumbCenterY; @@ -68,18 +73,14 @@ public class CircularRangeSlider extends View { private OnRangeChangeListener onRangeChangeListener = null; private int startIndexOld = -1; private int endIndexOld = -1; - private int[] mTempStates = new int[2]; + private final int[] mTempStates = new int[2]; private float transformAngle; - private Rect bounds = new Rect(); + private final Rect bounds = new Rect(); private final String TAG = "demo"; private int getCircleColor() { - if (isEnabled()) { - return circleColor; - } else { - return Color.parseColor("#eeeeee"); - } + return circleColor; } public void setAxisColor(int axisColor) { @@ -94,7 +95,7 @@ public void setLabelVisibility(int visibility) { } - private class Gravity { + private static class Gravity { private static final int TOP = 1; private static final int BOTTOM = 3; private static final int RIGHT = 2; @@ -103,9 +104,7 @@ private class Gravity { public interface OnRangeChangeListener { void onRangePress(int startIndex, int endIndex); - void onRangeChange(int startIndex, int endIndex); - void onRangeRelease(int startIndex, int endIndex); } @@ -118,16 +117,13 @@ public void setOnRangeChangeListener(OnRangeChangeListener onRangeChangeListener } public void setProgress(float progress) { - if (!isEnabled()) { - return; - } this.progress = progress; invalidate(); } public CircularRangeSlider(Context context, AttributeSet attrs) { super(context, attrs); - init(context, attrs, 0); + init(context, attrs); } public void setMax(int max) { @@ -135,7 +131,7 @@ public void setMax(int max) { max = 3; } this.max = max; - stepsGap = 360f / max; + stepsGap = (endAngle - startAngle) / max; if (endIndex >= max) { endIndex = max - 2; @@ -147,6 +143,8 @@ public void setMax(int max) { if (onRangeChangeListener != null) { onRangeChangeListener.onRangeChange(startIndex, endIndex); } + setStartIndex(startIndex); + setEndIndex(endIndex); invalidate(); } @@ -167,11 +165,7 @@ public void setCircleColor(int circleColor) { } private int getBorderColor() { - if (isEnabled()) { - return borderColor; - } else { - return Color.parseColor("#e2e2e2"); - } + return borderColor; } public void setBorderColor(int borderColor) { @@ -180,11 +174,7 @@ public void setBorderColor(int borderColor) { } private int getSectorColor() { - if (isEnabled()) { - return sectorColor; - } else { - return Color.parseColor("#26797979"); - } + return sectorColor; } public void setSectorColor(int sectorColor) { @@ -242,11 +232,7 @@ public void setStartIndexStepWidth(float startIndexStepWidth) { } private int getStartIndexStepColor() { - if (isEnabled()) { - return startIndexStepColor; - } else { - return Color.WHITE; - } + return startIndexStepColor; } public void setStartIndexStepColor(int startIndexStepColor) { @@ -263,11 +249,7 @@ public boolean isProgressEnabled() { } private int getProgressColor() { - if (isEnabled()) { - return progressColor; - } else { - return Color.WHITE; - } + return progressColor; } private void setProgressColor(int progressColor) { @@ -281,9 +263,10 @@ public int getStartIndex() { } public void setStartIndex(int startIndex) { + startIndex = Math.min(Math.max(startIndex, 0), max); this.startIndex = startIndex; - startThumbAngle = startIndex * stepsGap; - endThumbAngle = endIndex * stepsGap; + startThumbAngle = startAngle + (startIndex * stepsGap); + endThumbAngle = startAngle + (endIndex * stepsGap); invalidate(); } @@ -292,12 +275,19 @@ public int getEndIndex() { } public void setEndIndex(int endIndex) { + endIndex = Math.min(Math.max(endIndex, 0), max); this.endIndex = endIndex; - startThumbAngle = startIndex * stepsGap; - endThumbAngle = endIndex * stepsGap; + startThumbAngle = startAngle + (startIndex * stepsGap); + endThumbAngle = startAngle + (endIndex * stepsGap); + invalidate(); + } + public int getLabelInterval() { + return labelInterval; + } + public void setLabelInterval(int labelInterval) { + this.labelInterval = labelInterval; invalidate(); } - public void setLabelColor(int color) { labelColor = color; invalidate(); @@ -307,15 +297,17 @@ public int getMax() { return max; } - private void init(Context context, AttributeSet attrs, int defStyleAttr) { - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircularRangeSlider, defStyleAttr, 0); + private void init(Context context, AttributeSet attrs) { + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircularRangeSlider, 0, 0); max = a.getInteger(R.styleable.CircularRangeSlider_max, 12); stepLength = a.getDimension(R.styleable.CircularRangeSlider_stepLength, 10); labels = a.getTextArray(R.styleable.CircularRangeSlider_labels); labelColor = a.getColor(R.styleable.CircularRangeSlider_labelColor, Color.parseColor("#ffff00")); labelSize = a.getDimension(R.styleable.CircularRangeSlider_labelSize, 30); + labelInterval = a.getInteger(R.styleable.CircularRangeSlider_labelInterval, 1); hideLabel = a.getBoolean(R.styleable.CircularRangeSlider_hideLabel, false); + hideZero = a.getBoolean(R.styleable.CircularRangeSlider_hideZero, false); circleColor = a.getColor(R.styleable.CircularRangeSlider_circleColor, Color.parseColor("#4db6ac")); @@ -333,19 +325,22 @@ private void init(Context context, AttributeSet attrs, int defStyleAttr) { axisColor = a.getColor(R.styleable.CircularRangeSlider_axisColor, Color.WHITE); startFrom = a.getInteger(R.styleable.CircularRangeSlider_startFrom, Gravity.TOP); + startAngle = a.getFloat(R.styleable.CircularRangeSlider_startAngle, 0f); + endAngle = a.getFloat(R.styleable.CircularRangeSlider_endAngle, 360f); - startIndexStepLength = a.getDimension(R.styleable.CircularRangeSlider_startIndexStepLength, stepLength * 2); + startIndexStepLength = a.getDimension(R.styleable.CircularRangeSlider_startIndexStepLength, stepLength); startIndexStepWidth = a.getDimension(R.styleable.CircularRangeSlider_startIndexStepWidth, borderWidth * 1.5f); startIndexStepColor = a.getColor(R.styleable.CircularRangeSlider_startIndexStepColor, Color.WHITE); progress = a.getFloat(R.styleable.CircularRangeSlider_progress, 0); progressEnabled = a.getBoolean(R.styleable.CircularRangeSlider_progressEnabled, false); - progressColor = a.getColor(R.styleable.CircularRangeSlider_progressColor, Color.parseColor("#d50000")); + progressColor = a.getColor(R.styleable.CircularRangeSlider_progressColor, Color.WHITE); + progressLength = a.getFloat(R.styleable.CircularRangeSlider_progressLength, 1); startIndex = a.getInt(R.styleable.CircularRangeSlider_startIndex, 0); endIndex = a.getInt(R.styleable.CircularRangeSlider_endIndex, 1); - enabled = a.getBoolean(R.styleable.CircularRangeSlider_enabled, true); + boolean enabled = a.getBoolean(R.styleable.CircularRangeSlider_enabled, true); setEnabled(enabled); @@ -357,12 +352,9 @@ private void init(Context context, AttributeSet attrs, int defStyleAttr) { endIndex = max - 1; } - stepsGap = 360f / max; + stepsGap = (endAngle - startAngle) / max; switch (startFrom) { - case Gravity.TOP: - transformAngle = 270; - break; case Gravity.BOTTOM: transformAngle = 90; break; @@ -377,8 +369,8 @@ private void init(Context context, AttributeSet attrs, int defStyleAttr) { break; } - startThumbAngle = startIndex * stepsGap; - endThumbAngle = endIndex * stepsGap; + startThumbAngle = startAngle + (startIndex * stepsGap); + endThumbAngle = startAngle + (endIndex * stepsGap); a.recycle(); @@ -388,14 +380,7 @@ private Path generateStepsPath() { Path path = new Path(); for (int i = 0; i < max; i++) { - double angRad = Math.toRadians(i * stepsGap);/* - if (i == 0) { - paint.setColor(startIndexStepColor); - paint.setStrokeWidth(startIndexStepWidth); - } else { - paint.setColor(stepColor); - paint.setStrokeWidth(stepWidth); - }*/ + double angRad = Math.toRadians(startAngle + (i * stepsGap)); float startX = centerX + (float) (radius * Math.cos(angRad)); float startY = centerY + (float) (radius * Math.sin(angRad)); float stopX = centerX + (float) ((radius - (i == 0 ? startIndexStepLength : stepLength)) * Math.cos(angRad)); @@ -405,17 +390,13 @@ private Path generateStepsPath() { path.moveTo(startX, startY); path.lineTo(stopX, stopY); - // String tmp = String.valueOf(i); - // paint.getTextBounds(tmp, 0, tmp.length(), bounds); - - // canvas.drawText(tmp, stopX, stopY, paint); } return path; } @Override - protected void onDraw(Canvas canvas) { + protected void onDraw(@NonNull Canvas canvas) { super.onDraw(canvas); canvas.rotate(transformAngle, centerX, centerY); paint.setStrokeCap(Paint.Cap.ROUND); @@ -428,12 +409,28 @@ protected void onDraw(Canvas canvas) { paint.setStyle(Paint.Style.FILL); canvas.drawCircle(centerX, centerY, radius, paint); + //sector + float sweepAngle; + if (startThumbAngle >= endThumbAngle) { + sweepAngle = 360 - startThumbAngle + endThumbAngle; + } else { + sweepAngle = endThumbAngle - startThumbAngle; + } + + paint.setColor(getSectorColor()); + paint.setStyle(Paint.Style.FILL); + canvas.drawArc(ovalBigArc, + startThumbAngle, + sweepAngle, + true, + paint); + //steps paint.setColor(getBorderColor()); paint.setStrokeWidth(borderWidth); paint.setStyle(Paint.Style.FILL); - for (int i = 0; i < max; i++) { - double angRad = Math.toRadians(i * stepsGap); + for (int i = 0; i <= max; i++) { + double angRad = Math.toRadians(startAngle + (i * stepsGap)); if (i == 0) { paint.setColor(getStartIndexStepColor()); paint.setStrokeWidth(startIndexStepWidth); @@ -448,7 +445,11 @@ protected void onDraw(Canvas canvas) { canvas.drawLine(startX, startY, stopX, stopY, paint); //labels - if (!hideLabel) { + boolean drawLabel = (i % labelInterval == 0); + drawLabel &= !((i == 0) && hideZero); + drawLabel &= !hideLabel; + + if (drawLabel) { String label; if (labels != null) { @@ -476,7 +477,7 @@ protected void onDraw(Canvas canvas) { int x = (int) (centerX + Math.cos(angRad + Math.toRadians(transformAngle)) * (radius - padding - boundRadius)); int y = (int) (centerY + Math.sin(angRad + Math.toRadians(transformAngle)) * (radius - padding - boundRadius)); canvas.rotate(-transformAngle, centerX, centerY); - canvas.drawText(label, x - bounds.width() / 2, y + bounds.height() / 2, paint); + canvas.drawText(label, x - (float) bounds.width() / 2, y + (float) bounds.height() / 2, paint); canvas.rotate(transformAngle, centerX, centerY); } } @@ -487,35 +488,6 @@ protected void onDraw(Canvas canvas) { paint.setStrokeWidth(borderWidth); canvas.drawCircle(centerX, centerY, radius, paint); - //progress - paint.setStyle(Paint.Style.FILL); - if (progressEnabled && isEnabled()) { - float angRad = (float) Math.toRadians(progress * stepsGap); - float startX = centerX + (float) ((radius + borderWidth / 2) * Math.cos(angRad)); - float startY = centerY + (float) ((radius + borderWidth / 2) * Math.sin(angRad)); - float stopX = centerX + (float) ((radius - stepLength) * Math.cos(angRad)); - float stopY = centerY + (float) ((radius - stepLength) * Math.sin(angRad)); - paint.setColor(getProgressColor()); - paint.setStrokeWidth(borderWidth); - canvas.drawLine(startX, startY, stopX, stopY, paint); - } - - //sector - float sweepAngle; - if (startThumbAngle >= endThumbAngle) { - sweepAngle = 360 - startThumbAngle + endThumbAngle; - } else { - sweepAngle = endThumbAngle - startThumbAngle; - } - - paint.setColor(getSectorColor()); - paint.setStyle(Paint.Style.FILL); - canvas.drawArc(ovalBigArc, - startThumbAngle, - sweepAngle, - true, - paint); - //start thumb float cosineOfStartThumbAngle = (float) Math.cos(Math.toRadians(startThumbAngle)); float sineOfStartThumbAngle = (float) Math.sin(Math.toRadians(startThumbAngle)); @@ -550,6 +522,17 @@ protected void onDraw(Canvas canvas) { paint); + //progress + paint.setStyle(Paint.Style.FILL); + if (progressEnabled) { + float angRad = (float) Math.toRadians(startAngle + (progress * stepsGap)); + float stopX = centerX + (float) ((radius * progressLength) * Math.cos(angRad)); + float stopY = centerY + (float) ((radius * progressLength) * Math.sin(angRad)); + paint.setColor(getProgressColor()); + paint.setStrokeWidth(sliderWidth); + canvas.drawLine(centerX, centerY, stopX, stopY, paint); + } + //center axis paint.setColor(getAxisColor()); paint.setStyle(Paint.Style.FILL); @@ -557,16 +540,13 @@ protected void onDraw(Canvas canvas) { } private int getAxisColor() { - if (isEnabled()) { - return axisColor; - } else { - return Color.parseColor("#ffffff"); - } + return axisColor; } private boolean touchedOnStartThumb; private boolean touchedOnEndThumb; + @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(MotionEvent event) { if (!isEnabled()) { @@ -630,8 +610,52 @@ private void moveThumb(float x, float y) { } else if (touchedOnEndThumb) { endThumbAngle = getNearestAngle(x, y); } - startIndex = (int) (startThumbAngle / stepsGap); - endIndex = (int) (endThumbAngle / stepsGap); + int maxIndex = Math.round((endAngle - startAngle) / stepsGap); + startIndex = Math.round((startThumbAngle - startAngle) / stepsGap); + endIndex = Math.round((endThumbAngle - startAngle) / stepsGap); + + if (startIndex >= endIndex) + { + if (touchedOnStartThumb) + { + if (endIndex < maxIndex) + { + endIndex = startIndex + 1; + endThumbAngle = startAngle + (endIndex * stepsGap); + } + else + { + startIndex = endIndex - 1; + startThumbAngle = startAngle + (startIndex * stepsGap); + } + } + else if (touchedOnEndThumb) + { + if (startIndex > 0) + { + startIndex = endIndex - 1; + startThumbAngle = startAngle + (startIndex * stepsGap); + } + else + { + endIndex = startIndex + 1; + endThumbAngle = startAngle + (endIndex * stepsGap); + } + } + } + + if ((startIndex < 0)) + { + startIndex = 0; + startThumbAngle = startAngle; + } + + if (endIndex > maxIndex) + { + endIndex = maxIndex; + endThumbAngle = endAngle; + } + if (startIndex != startIndexOld || endIndex != endIndexOld) { if (onRangeChangeListener != null) { onRangeChangeListener.onRangeChange(startIndex, endIndex); @@ -643,9 +667,11 @@ private void moveThumb(float x, float y) { private float getNearestAngle(float x, float y) { - float angle = (float) (Math.toDegrees(Math.atan2(y - centerY, x - centerX))); - float angleCeil = ((int) Math.ceil(angle / stepsGap)) * stepsGap; - float angleFloor = ((int) Math.floor(angle / stepsGap)) * stepsGap; + float angle = (float)(Math.toDegrees(Math.atan2(y - centerY, x - centerX))); + if (angle < 0) angle += 360; + + float angleCeil = ((float)Math.ceil((angle - startAngle) / stepsGap) * stepsGap) + startAngle; + float angleFloor = ((float)Math.floor((angle - startAngle) / stepsGap) * stepsGap) + startAngle; if (Math.abs(angle - angleCeil) < Math.abs(angle - angleFloor)) { return angleCeil < 0 ? angleCeil + 360 : angleCeil; @@ -666,12 +692,12 @@ private double distance(float x1, float y1, float x2, float y2) { protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (w < h) { - radius = w / 2; + radius = (float) w / 2; centerX = radius; - centerY = h / 2; + centerY = (float) h / 2; } else { - radius = h / 2; - centerX = w / 2; + radius = (float) h / 2; + centerX = (float) w / 2; centerY = radius; } ovalBigArc.set(centerX - radius, centerY - radius, centerX + radius, centerY + radius); @@ -681,50 +707,46 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) { } void debug(String[] names, String... values) { - String a = ""; + StringBuilder a = new StringBuilder(); for (int i = 0; i < names.length; i++) { if (i != 0) { - a += "\n"; + a.append("\n"); } - a += names[i] + " = " + values[i]; + a.append(names[i]).append(" = ").append(values[i]); } // Log.i(TAG, a); } void debug(String[] names, Integer... values) { - String a = ""; + StringBuilder a = new StringBuilder(); for (int i = 0; i < names.length; i++) { if (i != 0) { - a += "\n"; + a.append("\n"); } - a += names[i] + " = " + values[i]; + a.append(names[i]).append(" = ").append(values[i]); } // Log.i(TAG, a); } void debug(String[] names, Float... values) { - String a = ""; + StringBuilder a = new StringBuilder(); for (int i = 0; i < names.length; i++) { if (i != 0) { - a += "\n"; + a.append("\n"); } - a += names[i] + " = " + values[i]; + a.append(names[i]).append(" = ").append(values[i]); } // Log.i(TAG, a); } private int getSliderColor(boolean pressed) { if (sliderColor == null) { - if (isEnabled()) { - if (pressed) { - return Color.parseColor("#e3fae6ab"); - } - return Color.parseColor("#e3ffca28"); - } else { - return Color.parseColor("#eeeeee"); + if (pressed) { + return Color.parseColor("#e3fae6ab"); } + return Color.parseColor("#e3ffca28"); } else { - mTempStates[0] = isEnabled() ? android.R.attr.state_enabled : -android.R.attr.state_enabled; + mTempStates[0] = android.R.attr.state_enabled; mTempStates[1] = pressed ? android.R.attr.state_pressed : -android.R.attr.state_pressed; return sliderColor.getColorForState(mTempStates, 0); } @@ -743,10 +765,7 @@ public boolean isProgressInsideRange() { return progress >= startIndex && progress < endIndex; } else if (startIndex > endIndex) { return progress >= startIndex || progress < endIndex; - } else if (startIndex == endIndex) { - return true; - } - return false; + } else return true; } @@ -782,7 +801,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { height = heightSize; } else if (heightMode == MeasureSpec.AT_MOST) { //Can't be bigger than... - height = Math.min(desiredHeight, heightSize); + height = desiredHeight; } else { //Be whatever you want height = desiredHeight; diff --git a/circularrangeslider/src/main/res/values/attrs.xml b/circularrangeslider/src/main/res/values/attrs.xml index f9adcd5..15e1cea 100644 --- a/circularrangeslider/src/main/res/values/attrs.xml +++ b/circularrangeslider/src/main/res/values/attrs.xml @@ -8,7 +8,9 @@ + + @@ -30,6 +32,8 @@ + + @@ -38,6 +42,7 @@ +