diff --git a/.gitignore b/.gitignore
index ecf627d..e5e5f47 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,4 @@
.gradle
/local.properties
/.idea
-/build
+/build/*
diff --git a/README.md b/README.md
index c321c3b..ee1a5c5 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,7 @@ ScrollableNumberPicker
============
This view provides an user-friendly numerical input interface. It can easily be customized and is built to be used on Android-TV as well.
+[![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-ScrollableNumberPicker-orange.svg?style=flat)](https://android-arsenal.com/details/1/5676)
[![Download](https://api.bintray.com/packages/michaelmuenzer/ScrollableNumberPicker/ScrollableNumberPicker/images/download.svg) ](https://bintray.com/michaelmuenzer/ScrollableNumberPicker/ScrollableNumberPicker/_latestVersion)
[![API](https://img.shields.io/badge/API-16%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=16)
@@ -32,7 +33,16 @@ Just include `ScrollableNumberPicker` inside our xml-layout. There are samples a
android:layout_height="wrap_content"/>
```
-You can make use of various custom attributes to define how the increment and decrement interactions should behave.
+If you want to change the value by scrolling you can enable and control it like this:
+```xml
+
+```
+
+You can make use of various other custom attributes to define how the increment and decrement interactions should behave:
```xml
```
-There exist attributes which let you customize the general appearance of the view.
+There exist further attributes which let you customize the general appearance of the view:
```xml
```
-You can essentially make the element look exactly like you want by using the `android:background` attribute
+You can essentially make the element look exactly like you want by using the `android:background` attribute.
```xml
mMaxValue) {
+ value = mMaxValue;
+ }
+ if (value < mMinValue) {
+ value = mMinValue;
+ }
+
+ mValue = value;
+ setValue();
+ }
+
+ private void setValue() {
+ mValueTextView.setText(String.valueOf(mValue));
+
+ if (mListener != null) {
+ mListener.onNumberPicked(mValue);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ public int getMaxValue() {
+ return mMaxValue;
+ }
+
+ @SuppressWarnings("unused")
+ public void setMaxValue(int maxValue) {
+ mMaxValue = maxValue;
+ if (maxValue < mValue) {
+ mValue = maxValue;
+ setValue();
+ }
+ }
+
+ @SuppressWarnings("unused")
+ public int getMinValue() {
+ return mMinValue;
+ }
+
+ @SuppressWarnings("unused")
+ public void setMinValue(int minValue) {
+ mMinValue = minValue;
+ if (minValue > mValue) {
+ mValue = minValue;
+ setValue();
+ }
+ }
+
+ @SuppressWarnings("unused")
+ public int getStepSize() {
+ return mStepSize;
+ }
+
+ @SuppressWarnings("unused")
+ public void setStepSize(int stepSize) {
+ mStepSize = stepSize;
+ }
+
+ @SuppressWarnings("unused")
+ public long getOnLongPressUpdateInterval() {
+ return mUpdateIntervalMillis;
+ }
+
+ @SuppressWarnings("unused")
+ public void setOnLongPressUpdateInterval(int intervalMillis) {
+ if (intervalMillis < MIN_UPDATE_INTERVAL_MS) {
+ intervalMillis = MIN_UPDATE_INTERVAL_MS;
+ }
+
+ mUpdateIntervalMillis = intervalMillis;
+ }
+
+ @SuppressWarnings("unused")
+ public void setListener(ScrollableNumberPickerListener listener) {
+ mListener = listener;
+ }
+
+ public boolean handleKeyEvent(int keyCode, KeyEvent event) {
+ int eventAction = event.getAction();
+ if (eventAction == KeyEvent.ACTION_DOWN) {
+ if (mOrientation == HORIZONTAL) {
+ if (keyCode == KEYCODE_DPAD_LEFT) {
+ if (event.getRepeatCount() == 0) {
+ scaleImageViewDrawable(mMinusButton, mButtonTouchScaleFactor);
+ }
+ decrement();
+ return true;
+ } else if (keyCode == KEYCODE_DPAD_RIGHT) {
+ if (event.getRepeatCount() == 0) {
+ scaleImageViewDrawable(mPlusButton, mButtonTouchScaleFactor);
+ }
+ increment();
+ return true;
+ }
+ } else {
+ if (keyCode == KEYCODE_DPAD_UP) {
+ if (event.getRepeatCount() == 0) {
+ scaleImageViewDrawable(mPlusButton, mButtonTouchScaleFactor);
+ }
+ increment();
+ return true;
+ } else if (keyCode == KEYCODE_DPAD_DOWN) {
+ if (event.getRepeatCount() == 0) {
+ scaleImageViewDrawable(mMinusButton, mButtonTouchScaleFactor);
+ }
+ decrement();
+ return true;
+ }
+ }
+ } else if (eventAction == KeyEvent.ACTION_UP) {
+ if (mOrientation == HORIZONTAL) {
+ if (keyCode == KEYCODE_DPAD_LEFT) {
+ setButtonMinusImage();
+ return true;
+ } else if (keyCode == KEYCODE_DPAD_RIGHT) {
+ setButtonPlusImage();
+ return true;
+ }
+ } else {
+ if (keyCode == KEYCODE_DPAD_UP) {
+ setButtonPlusImage();
+ return true;
+ } else if (keyCode == KEYCODE_DPAD_DOWN) {
+ setButtonMinusImage();
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+
private void init(Context context, AttributeSet attrs) {
if (isInEditMode()) {
return;
@@ -114,6 +305,9 @@ private void init(Context context, AttributeSet attrs) {
mValue = typedArray.getInt(R.styleable.ScrollableNumberPicker_snp_value,
res.getInteger(R.integer.default_value));
+ mScrollEnabled = typedArray.getBoolean(R.styleable.ScrollableNumberPicker_snp_scrollEnabled,
+ res.getBoolean(R.bool.default_scrollEnabled));
+
mButtonColorStateList = ContextCompat.getColorStateList(context, typedArray.getResourceId(R.styleable.ScrollableNumberPicker_snp_buttonBackgroundTintSelector, R.color.btn_tint_selector));
mValueMarginStart = (int) typedArray.getDimension(R.styleable.ScrollableNumberPicker_snp_valueMarginStart, res.getDimension(R.dimen.default_value_margin_start));
@@ -150,6 +344,80 @@ private void initViews() {
initButtonPlus();
initButtonMinus();
+
+ if (mScrollEnabled) {
+ setOnTouchListener(new OnTouchListener() {
+ private float lastX = 0.0f;
+ private float lastY = 0.0f;
+ private final int scrollOffsetPx = getResources().getDimensionPixelSize(R.dimen.default_scroll_offset);
+
+ @Override
+ public boolean onTouch(View view, MotionEvent motionEvent) {
+ float currentX = motionEvent.getX();
+ float currentY = motionEvent.getY();
+
+ switch (motionEvent.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ lastX = currentX;
+ lastY = currentY;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (mOrientation == HORIZONTAL) {
+ float moveDeltaX = currentX - lastX;
+ if (moveDeltaX > scrollOffsetPx) {
+ increment();
+ break;
+ } else if ((scrollOffsetPx * -1) > moveDeltaX) {
+ decrement();
+ break;
+ }
+ } else {
+ float moveDeltaY = currentY - lastY;
+ if (moveDeltaY > scrollOffsetPx) {
+ decrement();
+ break;
+ } else if ((scrollOffsetPx * -1) > moveDeltaY) {
+ increment();
+ break;
+ }
+ }
+
+ lastX = currentX;
+ lastY = currentY;
+ break;
+ case MotionEvent.ACTION_UP:
+ int numberOfRuns;
+ int singleRunLength = getResources().getDimensionPixelSize(R.dimen.default_scroll_repeat_length);
+ if (mOrientation == HORIZONTAL) {
+ float moveDeltaX = currentX - lastX;
+ if (moveDeltaX > 0) {
+ mAutoIncrement = true;
+ } else {
+ mAutoDecrement = true;
+ }
+
+ numberOfRuns = (int) (Math.abs(moveDeltaX) / singleRunLength);
+ } else {
+ float moveDeltaY = currentY - lastY;
+ if (moveDeltaY > 0) {
+ mAutoDecrement = true;
+ } else {
+ mAutoIncrement = true;
+ }
+
+ numberOfRuns = (int) (Math.abs(moveDeltaY) / singleRunLength);
+ }
+
+ mUpdateIntervalHandler.post(new RepeatSlowingRunnable(numberOfRuns, mUpdateIntervalMillis));
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+ }
+ });
+ }
}
private void initButtonPlus() {
@@ -282,21 +550,6 @@ private void scaleImageViewDrawable(ImageView view, float scaleFactor) {
}
}
- @SuppressWarnings("unused")
- public ImageView getButtonMinusView() {
- return mMinusButton;
- }
-
- @SuppressWarnings("unused")
- public ImageView getButtonPlusView() {
- return mPlusButton;
- }
-
- @SuppressWarnings("unused")
- public TextView getTextValueView() {
- return mValueTextView;
- }
-
private void increment() {
if (mValue < mMaxValue) {
setValue(mValue + mStepSize);
@@ -309,152 +562,43 @@ private void decrement() {
}
}
- @SuppressWarnings("unused")
- public int getValue() {
- return mValue;
- }
-
- @SuppressWarnings("WeakerAccess")
- public void setValue(int value) {
- if (value > mMaxValue) {
- value = mMaxValue;
- }
- if (value < mMinValue) {
- value = mMinValue;
- }
-
- mValue = value;
- setValue();
- }
-
- private void setValue() {
- mValueTextView.setText(String.valueOf(mValue));
-
- if (mListener != null) {
- mListener.onNumberPicked(mValue);
- }
- }
-
- @SuppressWarnings("unused")
- public int getMaxValue() {
- return mMaxValue;
- }
-
- @SuppressWarnings("unused")
- public void setMaxValue(int maxValue) {
- mMaxValue = maxValue;
- if (maxValue < mValue) {
- mValue = maxValue;
- setValue();
- }
- }
-
- @SuppressWarnings("unused")
- public int getMinValue() {
- return mMinValue;
- }
-
- @SuppressWarnings("unused")
- public void setMinValue(int minValue) {
- mMinValue = minValue;
- if (minValue > mValue) {
- mValue = minValue;
- setValue();
+ private class RepeatRunnable implements Runnable {
+ public void run() {
+ if (mAutoIncrement) {
+ increment();
+ mUpdateIntervalHandler.postDelayed(new RepeatRunnable(), mUpdateIntervalMillis);
+ } else if (mAutoDecrement) {
+ decrement();
+ mUpdateIntervalHandler.postDelayed(new RepeatRunnable(), mUpdateIntervalMillis);
+ }
}
}
- @SuppressWarnings("unused")
- public int getStepSize() {
- return mStepSize;
- }
-
- @SuppressWarnings("unused")
- public void setStepSize(int stepSize) {
- mStepSize = stepSize;
- }
-
- @SuppressWarnings("unused")
- public long getOnLongPressUpdateInterval() {
- return mUpdateIntervalMillis;
- }
+ private class RepeatSlowingRunnable implements Runnable {
+ long mUpdateIntervalMillis = 0;
+ int mNumberOfLeftRuns = 0;
- @SuppressWarnings("unused")
- public void setOnLongPressUpdateInterval(int intervalMillis) {
- if (intervalMillis < MIN_UPDATE_INTERVAL_MS) {
- intervalMillis = MIN_UPDATE_INTERVAL_MS;
+ RepeatSlowingRunnable(int numberOfLeftRuns, long millis) {
+ mUpdateIntervalMillis = millis;
+ mNumberOfLeftRuns = numberOfLeftRuns;
}
- mUpdateIntervalMillis = intervalMillis;
- }
+ public void run() {
+ long millisNextRun = (long) (mUpdateIntervalMillis * SLOWING_FACTOR);
- @SuppressWarnings("unused")
- public void setListener(ScrollableNumberPickerListener listener) {
- mListener = listener;
- }
+ if (mNumberOfLeftRuns > 0) {
+ int leftRuns = mNumberOfLeftRuns - 1;
- public boolean handleKeyEvent(int keyCode, KeyEvent event) {
- int eventAction = event.getAction();
- if (eventAction == KeyEvent.ACTION_DOWN) {
- if (mOrientation == HORIZONTAL) {
- if (keyCode == KEYCODE_DPAD_LEFT) {
- if (event.getRepeatCount() == 0) {
- scaleImageViewDrawable(mMinusButton, mButtonTouchScaleFactor);
- }
- decrement();
- return true;
- } else if (keyCode == KEYCODE_DPAD_RIGHT) {
- if (event.getRepeatCount() == 0) {
- scaleImageViewDrawable(mPlusButton, mButtonTouchScaleFactor);
- }
+ if (mAutoIncrement) {
increment();
- return true;
- }
- } else {
- if (keyCode == KEYCODE_DPAD_UP) {
- if (event.getRepeatCount() == 0) {
- scaleImageViewDrawable(mPlusButton, mButtonTouchScaleFactor);
- }
- increment();
- return true;
- } else if (keyCode == KEYCODE_DPAD_DOWN) {
- if (event.getRepeatCount() == 0) {
- scaleImageViewDrawable(mMinusButton, mButtonTouchScaleFactor);
- }
+ mUpdateIntervalHandler.postDelayed(new RepeatSlowingRunnable(leftRuns, millisNextRun), mUpdateIntervalMillis);
+ } else if (mAutoDecrement) {
decrement();
- return true;
- }
- }
- } else if (eventAction == KeyEvent.ACTION_UP) {
- if (mOrientation == HORIZONTAL) {
- if (keyCode == KEYCODE_DPAD_LEFT) {
- setButtonMinusImage();
- return true;
- } else if (keyCode == KEYCODE_DPAD_RIGHT) {
- setButtonPlusImage();
- return true;
+ mUpdateIntervalHandler.postDelayed(new RepeatSlowingRunnable(leftRuns, millisNextRun), mUpdateIntervalMillis);
}
} else {
- if (keyCode == KEYCODE_DPAD_UP) {
- setButtonPlusImage();
- return true;
- } else if (keyCode == KEYCODE_DPAD_DOWN) {
- setButtonMinusImage();
- return true;
- }
- }
- }
-
- return false;
- }
-
- private class RepeatRunnable implements Runnable {
- public void run() {
- if (mAutoIncrement) {
- increment();
- mUpdateIntervalHandler.postDelayed(new RepeatRunnable(), mUpdateIntervalMillis);
- } else if (mAutoDecrement) {
- decrement();
- mUpdateIntervalHandler.postDelayed(new RepeatRunnable(), mUpdateIntervalMillis);
+ mAutoIncrement = false;
+ mAutoDecrement = false;
}
}
}
diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml
index e2263f9..4e81f8f 100644
--- a/library/src/main/res/values/attrs.xml
+++ b/library/src/main/res/values/attrs.xml
@@ -13,6 +13,7 @@
+
diff --git a/library/src/main/res/values/boolean.xml b/library/src/main/res/values/boolean.xml
new file mode 100644
index 0000000..9d18ed5
--- /dev/null
+++ b/library/src/main/res/values/boolean.xml
@@ -0,0 +1,4 @@
+
+
+ false
+
\ No newline at end of file
diff --git a/library/src/main/res/values/dimen.xml b/library/src/main/res/values/dimen.xml
index eaf591a..76106ab 100644
--- a/library/src/main/res/values/dimen.xml
+++ b/library/src/main/res/values/dimen.xml
@@ -3,6 +3,8 @@
24dp
0dp
0dp
+ 4dp
+ 25dp
- 1.0
\ No newline at end of file
diff --git a/sample-mobile/src/main/res/layout/activity_main.xml b/sample-mobile/src/main/res/layout/activity_main.xml
index 3da1c6c..c922d7a 100644
--- a/sample-mobile/src/main/res/layout/activity_main.xml
+++ b/sample-mobile/src/main/res/layout/activity_main.xml
@@ -83,6 +83,46 @@
app:snp_value="10"
app:snp_valueMarginEnd="5dp"
app:snp_valueMarginStart="5dp"/>
+
+
+
+
+
+