diff --git a/ruler-picker/src/main/java/com/kevalpatel2106/rulerpicker/RulerValuePicker.java b/ruler-picker/src/main/java/com/kevalpatel2106/rulerpicker/RulerValuePicker.java
index bff5430..1bf3988 100644
--- a/ruler-picker/src/main/java/com/kevalpatel2106/rulerpicker/RulerValuePicker.java
+++ b/ruler-picker/src/main/java/com/kevalpatel2106/rulerpicker/RulerValuePicker.java
@@ -34,6 +34,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
@@ -192,9 +193,15 @@ private void init(@Nullable AttributeSet attributeSet) {
if (a.hasValue(R.styleable.RulerValuePicker_min_value) ||
a.hasValue(R.styleable.RulerValuePicker_max_value)) {
- setMinMaxValue(a.getInteger(R.styleable.RulerValuePicker_min_value, 0),
+ initializeMinMaxValue(a.getInteger(R.styleable.RulerValuePicker_min_value, 0),
a.getInteger(R.styleable.RulerValuePicker_max_value, 100));
}
+ if (a.hasValue(R.styleable.RulerValuePicker_long_indicator_step)){
+ int mLongIndicatorStep = a.getInteger(R.styleable.RulerValuePicker_long_indicator_step, 5);
+ if (mLongIndicatorStep < 1)
+ mLongIndicatorStep = 5; /*Fallback to default to prevent unexpected behaviour*/
+ setLongIndicatorStep(mLongIndicatorStep);
+ }
} finally {
a.recycle();
}
@@ -309,30 +316,70 @@ private void calculateNotchPath() {
* will be selected.
*/
public void selectValue(final int value) {
+ //System.out.println("Save RulerValuePicker selectValue="+value+" called");
+ this.value = value;
+ selectValueDelayed(0);
+ }
+
+ /**
+ * Because selectValue can be called multiple times and each of them will cause a delayed selection - we rather keep the last value from the
+ * caller and concentrate on displaying only it not middle random calls.
+ */
+ private int value;
+
+ /**
+ * More precice method on returning the set value - usefull during rotation, etc.
+ * @return
+ */
+ public int getValue(){
+ return value;
+ }
+ /**
+ * Scroll the ruler to the given value.
+ * If value isn't reached yet - loop with delay 3 times till we manage to set the value ASAP.
+ //* @param value Value to select. Value must be between {@link #getMinValue()} and {@link #getMaxValue()}.
+ * If the value is less than {@link #getMinValue()}, {@link #getMinValue()} will be
+ * selected.If the value is greater than {@link #getMaxValue()}, {@link #getMaxValue()}
+ * will be selected.
+ * @param iteration - initial call should pass 0, the rest are done internally
+ */
+ private void selectValueDelayed(/*final int value, */ final int iteration){
+ int valuesToScroll;
+ if (value < mRulerView.getMinValue()) {
+ valuesToScroll = 0;
+ } else if (value > mRulerView.getMaxValue()) {
+ valuesToScroll = mRulerView.getMaxValue() - mRulerView.getMinValue();
+ } else {
+ valuesToScroll = value - mRulerView.getMinValue();
+ }
+
+ mHorizontalScrollView.smoothScrollTo(
+ (valuesToScroll+1) * mRulerView.getIndicatorIntervalWidth(), 0);//extra 1 for spacing which we artificially added in case the labels may wanna get drawn
+
+ //we have all the condition in postDelay, because smoothScrollTo is asynchronious and will take time to finish
mHorizontalScrollView.postDelayed(new Runnable() {
@Override
public void run() {
- int valuesToScroll;
- if (value < mRulerView.getMinValue()) {
- valuesToScroll = 0;
- } else if (value > mRulerView.getMaxValue()) {
- valuesToScroll = mRulerView.getMaxValue() - mRulerView.getMinValue();
- } else {
- valuesToScroll = value - mRulerView.getMinValue();
+ if (getCurrentValue() != value && //for the rest value selection
+ iteration < 3 || //loop protection with iteration from infinity
+ mHorizontalScrollView.getScrollX() == 0 //if minimum value is initially selected, but we actually have extra spaces (for the text) in front and back of the ruler, so it can't be 0.
+ ) {
+ selectValueDelayed(iteration + 1);//we rise iteration for loop protection
}
-
- mHorizontalScrollView.smoothScrollTo(
- valuesToScroll * mRulerView.getIndicatorIntervalWidth(), 0);
}
- }, 400);
+ }, 150);//maybe MhorizontalScrollView will be initialized faster than 400ms?
}
/**
+ * Not the best method to get current value as selection may be in progress
+ * but it is a good method to check weather we finally got our value selected
+ * and it is usefull during ruler initialization while smoothScrollTo will accept values
+ * but in reality - there will be no changes till it's fully initialized.
* @return Get the current selected value.
*/
public int getCurrentValue() {
int absoluteValue = mHorizontalScrollView.getScrollX() / mRulerView.getIndicatorIntervalWidth();
- int value = mRulerView.getMinValue() + absoluteValue;
+ int value = mRulerView.getMinValue() + absoluteValue - 1;//extra 1 for spacing which we artificially added in case the labels may wanna get drawn
if (value > mRulerView.getMaxValue()) {
return mRulerView.getMaxValue();
@@ -358,10 +405,21 @@ public void onScrollStopped() {
private void makeOffsetCorrection(final int indicatorInterval) {
int offsetValue = mHorizontalScrollView.getScrollX() % indicatorInterval;
- if (offsetValue < indicatorInterval / 2) {
+ /*
+ Uncommenting the code in this function would make left / right boundaries strict on their values.
+ But eventually i've noticed it's nicer when you're able to scroll around without
+ these restrictions.
+ */
+ //int maxScrollX = indicatorInterval * getMaxValue();
+ if (offsetValue < indicatorInterval / 2 ){//&&
+ //mHorizontalScrollView.getScrollX() >= indicatorInterval && //this is our left padding of the indicator interval length
+ //mHorizontalScrollView.getScrollX() <= maxScrollX ){//this is our right edge of the padding
mHorizontalScrollView.scrollBy(-offsetValue, 0);
} else {
- mHorizontalScrollView.scrollBy(indicatorInterval - offsetValue, 0);
+ //if (mHorizontalScrollView.getScrollX() <= maxScrollX)//if we're not on the right edge of the padding
+ mHorizontalScrollView.scrollBy(indicatorInterval - offsetValue, 0);
+ //else
+ //mHorizontalScrollView.scrollTo( maxScrollX, 0);//back to the right most max value
}
}
@@ -370,6 +428,9 @@ public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.value = getCurrentValue();
+ ss.minVal = getMinValue();//may change during runtime
+ ss.maxVal = getMaxValue();//may change during runtime
+ //System.out.println("Save RulerValuePicker onSave. "+getValue()+" or *"+getCurrentValue()+"* in ["+ss.minVal+"; "+ss.maxVal+"]");
return ss;
}
@@ -377,7 +438,10 @@ public Parcelable onSaveInstanceState() {
public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
+ //System.out.println("Save RulerValuePicker onRestore I. "+value+" *"+getValue()+" / "+ getCurrentValue()+"* in ["+getMinValue()+"; "+getMaxValue()+"]");
+ setMinMaxValue(ss.minVal, ss.maxVal);
selectValue(ss.value);
+ //System.out.println("Save RulerValuePicker onRestore II. "+value+" *"+getValue()+" / "+ getCurrentValue()+"* in ["+getMinValue()+"; "+getMaxValue()+"]");
}
//**********************************************************************************//
@@ -535,6 +599,26 @@ public void setIndicatorWidth(final int widthPx) {
mRulerView.setIndicatorWidth(widthPx);
}
+ /**
+ * Set a step for a long indictor to be drawn.
+ * Every step a long indicator will be drawn.
+ * @param step - Step in decimal
+ * @see #getLongIndicatorStep()
+ * @see RulerView#mLongIndicatorStep
+ */
+ public void setLongIndicatorStep(final int step){
+ mRulerView.setLongIndicatorStep(step);
+ }
+
+ /**
+ * @return Long indicator step
+ * @see #setLongIndicatorStep(int)
+ * @see RulerView#mLongIndicatorStep
+ */
+ public int getLongIndicatorStep(){
+ return mRulerView.getLongIndicatorStep();
+ }
+
/**
* Set the width of the indicator line in the ruler.
*
@@ -566,6 +650,11 @@ public int getMaxValue() {
return mRulerView.getMaxValue();
}
+ /**
+ * If min / max values were set outside class
+ */
+ private boolean isMinMaxSet = false;
+
/**
* Set the maximum value to display on the ruler. This will decide the range of values and number
* of indicators that ruler will draw.
@@ -578,6 +667,22 @@ public int getMaxValue() {
* @see #getMaxValue()
*/
public void setMinMaxValue(final int minValue, final int maxValue) {
+ //System.out.println("Save RulerValuePicker setMinMax("+minValue+", "+maxValue+");");
+ int oldDiff = mRulerView.getMaxValue() - mRulerView.getMinValue();
+ int curVal = getCurrentValue();
+ mRulerView.setValueRange(minValue, maxValue);
+ if (isMinMaxSet && oldDiff != maxValue - minValue) {//it means we need to recalculate width
+ mRulerView.triggerOnMeasure();
+ }
+ invalidate();
+ if (curVal >= minValue && curVal <= maxValue)
+ selectValue(curVal);//we select same old value on change if we can
+ else
+ selectValue(minValue);//we select minimum value once we're out of edges
+ isMinMaxSet = true;
+ }
+
+ private void initializeMinMaxValue(final int minValue, final int maxValue){
mRulerView.setValueRange(minValue, maxValue);
invalidate();
selectValue(minValue);
@@ -671,6 +776,8 @@ public SavedState[] newArray(int size) {
};
private int value = 0;
+ private int minVal = 0;
+ private int maxVal = 0;
SavedState(Parcelable superState) {
super(superState);
@@ -679,12 +786,18 @@ public SavedState[] newArray(int size) {
private SavedState(Parcel in) {
super(in);
value = in.readInt();
+ minVal = in.readInt();
+ maxVal = in.readInt();
+ //System.out.println("SaveState: savedState="+value+" in ["+minVal+"; "+maxVal+"]");
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(value);
+ out.writeInt(minVal);
+ out.writeInt(maxVal);
+ //System.out.println("SaveState: writeToParcel="+value+" in ["+minVal+"; "+maxVal+"]");
}
}
}
diff --git a/ruler-picker/src/main/java/com/kevalpatel2106/rulerpicker/RulerView.java b/ruler-picker/src/main/java/com/kevalpatel2106/rulerpicker/RulerView.java
index aecc4f5..ebc9f9a 100644
--- a/ruler-picker/src/main/java/com/kevalpatel2106/rulerpicker/RulerView.java
+++ b/ruler-picker/src/main/java/com/kevalpatel2106/rulerpicker/RulerView.java
@@ -27,6 +27,7 @@
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
+import android.widget.LinearLayout;
/**
* Created by Keval Patel on 28 Mar 2018.
@@ -125,6 +126,11 @@ final class RulerView extends View {
*/
private int mShortIndicatorHeight = 0;
+ /**
+ * Allows to customise the step of the long indicator
+ */
+ private int mLongIndicatorStep = 5 /* Default value */;
+
/**
* Integer color of the text, that is displayed on the ruler.
*
@@ -216,7 +222,7 @@ private void parseAttr(@Nullable AttributeSet attributeSet) {
if (a.hasValue(R.styleable.RulerView_indicator_interval)) {
mIndicatorInterval = a.getDimensionPixelSize(R.styleable.RulerView_indicator_interval,
- 4);
+ 14);
}
if (a.hasValue(R.styleable.RulerView_long_height_height_ratio)) {
@@ -236,6 +242,11 @@ private void parseAttr(@Nullable AttributeSet attributeSet) {
mMaxValue = a.getInteger(R.styleable.RulerView_max_value, 100);
}
setValueRange(mMinValue, mMaxValue);
+ if (a.hasValue(R.styleable.RulerValuePicker_long_indicator_step)){
+ mLongIndicatorStep = a.getInteger(R.styleable.RulerValuePicker_long_indicator_step, 5);
+ if (mLongIndicatorStep < 1)
+ mLongIndicatorStep = 5; /*Fallback to default to prevent unexpected behaviour*/
+ }
} finally {
a.recycle();
}
@@ -264,9 +275,9 @@ private void refreshPaint() {
@Override
protected void onDraw(Canvas canvas) {
//Iterate through all value
- for (int value = 1; value < mMaxValue - mMinValue; value++) {
+ for (int value = 1; value <= mMaxValue - mMinValue; value++) {
- if (value % 5 == 0) {
+ if (value % mLongIndicatorStep == 0) {
drawLongIndicator(canvas, value);
drawValueText(canvas, value);
} else {
@@ -274,11 +285,21 @@ protected void onDraw(Canvas canvas) {
}
}
- //Draw the first indicator.
- drawSmallIndicator(canvas, 0);
+ if (mLongIndicatorStep != 1) {//because 1 means that we want every step marked with labels
+ //Draw the first indicator.
+ drawSmallIndicator(canvas, 0);
- //Draw the last indicator.
- drawSmallIndicator(canvas, getWidth());
+ //Draw the last indicator.
+ drawSmallIndicator(canvas, getWidth());
+ }{
+ //Draw the first indicator as long
+ drawLongIndicator(canvas, 0);
+ drawValueText(canvas, 0);
+
+ //Draw the last indicator as long
+ drawLongIndicator(canvas, getWidth());
+ drawValueText(canvas, getWidth());
+ }
super.onDraw(canvas);
}
@@ -286,13 +307,23 @@ protected void onDraw(Canvas canvas) {
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//Measure dimensions
mViewHeight = MeasureSpec.getSize(heightMeasureSpec);
- int viewWidth = (mMaxValue - mMinValue - 1) * mIndicatorInterval;
+
+ int viewWidth = (mMaxValue - mMinValue + 2) * mIndicatorInterval;//+2 are artificially added to have more space for first and last indicator drawValueText()
updateIndicatorHeight(mLongIndicatorHeightRatio, mShortIndicatorHeightRatio);
this.setMeasuredDimension(viewWidth, mViewHeight);
}
+ /**
+ * Method that will trigger onMeasure to be recalled so that the view Width could be recalculated
+ */
+ public void triggerOnMeasure(){
+ LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) this.getLayoutParams();
+ //params.width = mRulerView.calculateWidth();//would be usefull if onMeasure wouldn't get called
+ this.setLayoutParams(params);
+ }
+
/**
* Calculate and update the height of the long and the short indicators based on new ratios.
*
@@ -314,9 +345,9 @@ private void updateIndicatorHeight(final float longIndicatorHeightRatio,
*/
private void drawSmallIndicator(@NonNull final Canvas canvas,
final int value) {
- canvas.drawLine(mIndicatorInterval * value,
+ canvas.drawLine(mIndicatorInterval * (value+1),//extra 1 for spacing which we artificially added in case the labels may wanna get drawn
0,
- mIndicatorInterval * value,
+ mIndicatorInterval * (value+1),//extra 1 for spacing which we artificially added in case the labels may wanna get drawn
mShortIndicatorHeight,
mIndicatorPaint);
}
@@ -329,9 +360,9 @@ private void drawSmallIndicator(@NonNull final Canvas canvas,
*/
private void drawLongIndicator(@NonNull final Canvas canvas,
final int value) {
- canvas.drawLine(mIndicatorInterval * value,
+ canvas.drawLine(mIndicatorInterval * (value+1),//extra 1 for spacing which we artificially added in case the labels may wanna get drawn
0,
- mIndicatorInterval * value,
+ mIndicatorInterval * (value+1),//extra 1 for spacing which we artificially added in case the labels may wanna get drawn
mLongIndicatorHeight,
mIndicatorPaint);
}
@@ -346,7 +377,7 @@ private void drawLongIndicator(@NonNull final Canvas canvas,
private void drawValueText(@NonNull final Canvas canvas,
final int value) {
canvas.drawText(String.valueOf(value + mMinValue),
- mIndicatorInterval * value,
+ mIndicatorInterval * (value+1), //extra 1 for spacing which we artificially added in case the labels may wanna get drawn
mLongIndicatorHeight + mTextPaint.getTextSize(),
mTextPaint);
}
@@ -432,6 +463,23 @@ void setIndicatorWidth(final int widthPx) {
refreshPaint();
}
+ /**
+ * Set a step for a long indictor to be drawn.
+ * Every step a long indicator will be drawn.
+ * @param step - Step in decimal
+ */
+ void setLongIndicatorStep(final int step){
+ mLongIndicatorStep = step;
+ refreshPaint();
+ }
+
+ /**
+ * @return Long indicator step
+ * @see #setLongIndicatorStep(int)
+ */
+ int getLongIndicatorStep(){
+ return mLongIndicatorStep;
+ }
/**
* @return Get the minimum value displayed on the ruler.
diff --git a/ruler-picker/src/main/res/values/attr.xml b/ruler-picker/src/main/res/values/attr.xml
index 5acd22c..63e1ea4 100644
--- a/ruler-picker/src/main/res/values/attr.xml
+++ b/ruler-picker/src/main/res/values/attr.xml
@@ -26,6 +26,8 @@
+
+
@@ -41,6 +43,8 @@
+
+
@@ -58,6 +62,8 @@
+
+