diff --git a/.travis.yml b/.travis.yml index f504eb3..39e55ff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,11 @@ language: android -jdk: oraclejdk7 +jdk: oraclejdk8 android: components: - platform-tools - tools - - build-tools-23.0.3 - - android-23 + - build-tools-25.0.0 + - android-25 #Additional libs from Android - extra-google-m2repository diff --git a/README.md b/README.md index 5f94f58..64315f2 100644 --- a/README.md +++ b/README.md @@ -41,15 +41,16 @@ The rangeseekbar-sample shows the available features and customizations in code ```groovy dependencies { - compile 'org.florescu.android.rangeseekbar:rangeseekbar-library:0.3.0' + compile 'org.florescu.android.rangeseekbar:rangeseekbar-library:0.4.0' } ``` * For the latest work-in-progress snapshot: +(NOTE: Version 1.0 is making breaking API modifications and the API may continue to change until development has finished) ```groovy dependencies { - compile 'org.florescu.android.rangeseekbar:rangeseekbar-library:0.4.0-SNAPSHOT' + compile 'org.florescu.android.rangeseekbar:rangeseekbar-library:1.0.0-SNAPSHOT' } ``` diff --git a/build.gradle b/build.gradle index dfe5c8f..e681e9b 100644 --- a/build.gradle +++ b/build.gradle @@ -7,8 +7,8 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.0' - classpath 'com.github.ben-manes:gradle-versions-plugin:0.12.0' + classpath 'com.android.tools.build:gradle:2.2.2' + classpath 'com.github.ben-manes:gradle-versions-plugin:0.13.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/gradle.properties b/gradle.properties index c04991e..94561cf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,9 +16,8 @@ # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true - -VERSION_NAME=0.4.0 -VERSION_CODE=004000 +VERSION_NAME=1.0.0-SNAPSHOT +VERSION_CODE=100000 GROUP=org.florescu.android.rangeseekbar SNAPSHOT_REPOSITORY_URL=https://oss.sonatype.org/content/repositories/snapshots diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 83e1d30..69dcfcf 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu May 26 16:38:46 BST 2016 +#Fri Nov 25 14:42:07 GMT 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/rangeseekbar-sample/build.gradle b/rangeseekbar-sample/build.gradle index 4fd63f7..463963d 100644 --- a/rangeseekbar-sample/build.gradle +++ b/rangeseekbar-sample/build.gradle @@ -6,16 +6,17 @@ repositories { } android { - compileSdkVersion 23 - buildToolsVersion "23.0.3" + compileSdkVersion 25 + buildToolsVersion "25.0.0" defaultConfig { applicationId "org.florescu.android.rangeseekbar.sample" minSdkVersion 15 - targetSdkVersion 23 + targetSdkVersion 25 versionCode 1 versionName "1.0" } + buildTypes { release { minifyEnabled false @@ -26,6 +27,6 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:23.4.0' + compile 'com.android.support:appcompat-v7:25.0.1' compile project(':rangeseekbar') } diff --git a/rangeseekbar-sample/src/main/AndroidManifest.xml b/rangeseekbar-sample/src/main/AndroidManifest.xml index e9995a9..1b44aca 100644 --- a/rangeseekbar-sample/src/main/AndroidManifest.xml +++ b/rangeseekbar-sample/src/main/AndroidManifest.xml @@ -9,7 +9,8 @@ android:label="@string/app_name"> + android:label="@string/app_name" + android:theme="@style/AppTheme"> diff --git a/rangeseekbar-sample/src/main/java/org/florescu/android/rangeseekbar/sample/DemoActivity.java b/rangeseekbar-sample/src/main/java/org/florescu/android/rangeseekbar/sample/DemoActivity.java index 29162fd..1794903 100644 --- a/rangeseekbar-sample/src/main/java/org/florescu/android/rangeseekbar/sample/DemoActivity.java +++ b/rangeseekbar-sample/src/main/java/org/florescu/android/rangeseekbar/sample/DemoActivity.java @@ -20,7 +20,6 @@ import android.app.Activity; import android.os.Bundle; import android.widget.FrameLayout; -import android.widget.LinearLayout; import org.florescu.android.rangeseekbar.RangeSeekBar; @@ -35,11 +34,17 @@ public void onCreate(Bundle savedInstanceState) { setContentView(R.layout.main); // Setup the new range seek bar - RangeSeekBar rangeSeekBar = new RangeSeekBar<>(this); + RangeSeekBar rangeSeekBar = new RangeSeekBar(this); // Set the range rangeSeekBar.setRangeValues(15, 90); rangeSeekBar.setSelectedMinValue(20); rangeSeekBar.setSelectedMaxValue(88); + rangeSeekBar.setTextFormatter(new RangeSeekBar.TextFormatter() { + @Override + public String formatValue(int value) { + return value + " kittens"; + } + }); // Add to layout FrameLayout layout = (FrameLayout) findViewById(R.id.seekbar_placeholder); @@ -48,5 +53,15 @@ public void onCreate(Bundle savedInstanceState) { // Seek bar for which we will set text color in code RangeSeekBar rangeSeekBarTextColorWithCode = (RangeSeekBar) findViewById(R.id.rangeSeekBarTextColorWithCode); rangeSeekBarTextColorWithCode.setTextAboveThumbsColorResource(android.R.color.holo_blue_bright); + + // Seekbar with double values + RangeSeekBar rsbDoubles = (RangeSeekBar) findViewById(R.id.rsb_double_values); + rsbDoubles.setRangeValues(1523, 14835); + rsbDoubles.setTextFormatter(new RangeSeekBar.TextFormatter() { + @Override + public String formatValue(int value) { + return "£" + value / 100d; + } + }); } } diff --git a/rangeseekbar-sample/src/main/res/layout/main.xml b/rangeseekbar-sample/src/main/res/layout/main.xml index 69fb5f7..0e9f5d2 100644 --- a/rangeseekbar-sample/src/main/res/layout/main.xml +++ b/rangeseekbar-sample/src/main/res/layout/main.xml @@ -8,6 +8,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" + android:padding="8dp"> @@ -37,6 +39,7 @@ + + android:text="Range seek bar with double values" /> + android:layout_height="wrap_content" /> - - diff --git a/rangeseekbar/build.gradle b/rangeseekbar/build.gradle index 67fd780..97dbb44 100644 --- a/rangeseekbar/build.gradle +++ b/rangeseekbar/build.gradle @@ -1,22 +1,12 @@ -buildscript { - repositories { - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:2.1.0' - } -} - apply plugin: 'com.android.library' android { - compileSdkVersion 23 - buildToolsVersion "23.0.3" + compileSdkVersion 25 + buildToolsVersion "25.0.0" defaultConfig { minSdkVersion 15 - targetSdkVersion 23 + targetSdkVersion 25 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -27,21 +17,21 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } - android.sourceSets.test.java.srcDirs += "build/generated/source/r/debug" } dependencies { - repositories { - mavenCentral() - } - compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:23.4.0' - compile 'com.android.support:support-annotations:23.4.0' + compile 'com.android.support:support-compat:25.0.1' + compile 'com.android.support:appcompat-v7:25.0.1' // Robolectric - testCompile 'org.robolectric:robolectric:3.1-rc1' - testCompile "org.mockito:mockito-core:2.0.53-beta" + testCompile 'org.robolectric:robolectric:3.1.4' + testCompile "org.mockito:mockito-core:2.2.23" + testCompile 'junit:junit:4.12' + testCompile('com.squareup.assertj:assertj-android:1.1.1') { + exclude group: 'com.android.support', module: 'support-annotations' + } + } apply from: 'https://raw.github.com/chrisbanes/gradle-mvn-push/master/gradle-mvn-push.gradle' \ No newline at end of file diff --git a/rangeseekbar/src/main/java/org/florescu/android/rangeseekbar/RangeSeekBar.java b/rangeseekbar/src/main/java/org/florescu/android/rangeseekbar/RangeSeekBar.java index 9e68614..503e4d8 100644 --- a/rangeseekbar/src/main/java/org/florescu/android/rangeseekbar/RangeSeekBar.java +++ b/rangeseekbar/src/main/java/org/florescu/android/rangeseekbar/RangeSeekBar.java @@ -17,6 +17,7 @@ package org.florescu.android.rangeseekbar; +import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; @@ -30,10 +31,14 @@ import android.graphics.Path; import android.graphics.RectF; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Bundle; import android.os.Parcelable; +import android.support.annotation.ColorInt; import android.support.annotation.ColorRes; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.content.ContextCompat; import android.util.AttributeSet; import android.util.TypedValue; import android.view.MotionEvent; @@ -43,40 +48,36 @@ import org.florescu.android.util.BitmapUtil; import org.florescu.android.util.PixelUtil; -import java.math.BigDecimal; - /** * Widget that lets users select a minimum and maximum value on a given numerical range. - * The range value types can be one of Long, Double, Integer, Float, Short, Byte or BigDecimal.
*
* Improved {@link android.view.MotionEvent} handling for smoother use, anti-aliased painting for improved aesthetics. * - * @param The Number type of the range values. One of Long, Double, Integer, Float, Short, Byte or BigDecimal. * @author Stephan Tittel (stephan.tittel@kom.tu-darmstadt.de) * @author Peter Sinnott (psinnott@gmail.com) * @author Thomas Barrasso (tbarrasso@sevenplusandroid.org) * @author Alex Florescu (alex@florescu.org) * @author Michael Keppler (bananeweizen@gmx.de) */ -public class RangeSeekBar extends ImageView { +public class RangeSeekBar extends ImageView { /** * Default color of a {@link RangeSeekBar}, #FF33B5E5. This is also known as "Ice Cream Sandwich" blue. */ - public static final int ACTIVE_COLOR = Color.argb(0xFF, 0x33, 0xB5, 0xE5); + private static final int ACTIVE_COLOR = Color.argb(0xFF, 0x33, 0xB5, 0xE5); /** * An invalid pointer id. */ - public static final int INVALID_POINTER_ID = 255; + private static final int INVALID_POINTER_ID = 255; // Localized constants from MotionEvent for compatibility // with API < 8 "Froyo". - public static final int ACTION_POINTER_INDEX_MASK = 0x0000ff00, ACTION_POINTER_INDEX_SHIFT = 8; + private static final int ACTION_POINTER_INDEX_MASK = 0x0000ff00, ACTION_POINTER_INDEX_SHIFT = 8; - public static final Integer DEFAULT_MINIMUM = 0; - public static final Integer DEFAULT_MAXIMUM = 100; - public static final Integer DEFAULT_STEP = 1; - public static final int HEIGHT_IN_DP = 30; - public static final int TEXT_LATERAL_PADDING_IN_DP = 3; + private static final Integer DEFAULT_MINIMUM = 0; + private static final Integer DEFAULT_MAXIMUM = 100; + private static final Integer DEFAULT_STEP = 1; + private static final int HEIGHT_IN_DP = 30; + private static final int TEXT_LATERAL_PADDING_IN_DP = 3; private static final int INITIAL_PADDING_IN_DP = 8; private static final int DEFAULT_TEXT_SIZE_IN_DP = 14; @@ -95,15 +96,18 @@ public class RangeSeekBar extends ImageView { private float thumbHalfHeight; private float padding; - protected T absoluteMinValue, absoluteMaxValue, absoluteStepValue; - protected NumberType numberType; - protected double absoluteMinValuePrim, absoluteMaxValuePrim, absoluteStepValuePrim; - protected double normalizedMinValue = 0d; - protected double normalizedMaxValue = 1d; - protected double minDeltaForDefault = 0; + private int absoluteMinValue, absoluteMaxValue, absoluteStepValue; + private double absoluteMinValuePrim, absoluteMaxValuePrim, absoluteStepValuePrim; + private double normalizedMinValue = 0d; + private double normalizedMaxValue = 1d; + private double minDeltaForDefault = 0; private Thumb pressedThumb = null; private boolean notifyWhileDragging = false; - private OnRangeSeekBarChangeListener listener; + @Nullable + private OnRangeSeekBarChangeListener listener; + @Nullable + private TextFormatter textFormatter; + private float downMotionX; @@ -125,6 +129,7 @@ public class RangeSeekBar extends ImageView { private float internalPad; private int activeColor; private int defaultColor; + @ColorInt private int textAboveThumbsColor; private boolean thumbShadow; @@ -153,19 +158,18 @@ public RangeSeekBar(Context context, AttributeSet attrs, int defStyle) { init(context, attrs); } - @SuppressWarnings("unchecked") - private T extractNumericValueFromAttributes(TypedArray a, int attribute, int defaultValue) { + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public RangeSeekBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(context, attrs); + } + + private int extractNumericValueFromAttributes(TypedArray a, int attribute, int defaultValue) { TypedValue tv = a.peekValue(attribute); if (tv == null) { - return (T) Integer.valueOf(defaultValue); - } - - int type = tv.type; - if (type == TypedValue.TYPE_FLOAT) { - return (T) Float.valueOf(a.getFloat(attribute, defaultValue)); - } else { - return (T) Integer.valueOf(a.getInteger(attribute, defaultValue)); + return defaultValue; } + return a.getInteger(attribute, defaultValue); } private void init(Context context, AttributeSet attrs) { @@ -248,7 +252,7 @@ private void init(Context context, AttributeSet attrs) { thumbHalfWidth = 0.5f * thumbImage.getWidth(); thumbHalfHeight = 0.5f * thumbImage.getHeight(); - setValuePrimAndNumberType(); + setValuePrim(); textSize = PixelUtil.dpToPx(context, DEFAULT_TEXT_SIZE_IN_DP); distanceToTop = PixelUtil.dpToPx(context, DEFAULT_TEXT_DISTANCE_TO_TOP_IN_DP); @@ -278,43 +282,37 @@ private void init(Context context, AttributeSet attrs) { } } - public void setRangeValues(T minValue, T maxValue) { + public void setRangeValues(int minValue, int maxValue) { this.absoluteMinValue = minValue; this.absoluteMaxValue = maxValue; - setValuePrimAndNumberType(); + setValuePrim(); } - public void setRangeValues(T minValue, T maxValue, T step) { + public void setRangeValues(int minValue, int maxValue, int step) { this.absoluteStepValue = step; setRangeValues(minValue, maxValue); } - public void setTextAboveThumbsColor(int textAboveThumbsColor) { - this.textAboveThumbsColor = textAboveThumbsColor; - invalidate(); - } - public void setTextAboveThumbsColorResource(@ColorRes int resId) { - setTextAboveThumbsColor(getResources().getColor(resId)); + this.textAboveThumbsColor = ContextCompat.getColor(getContext(), resId); + invalidate(); } - @SuppressWarnings("unchecked") // only used to set default values when initialised from XML without any values specified private void setRangeToDefaultValues() { - this.absoluteMinValue = (T) DEFAULT_MINIMUM; - this.absoluteMaxValue = (T) DEFAULT_MAXIMUM; - this.absoluteStepValue = (T) DEFAULT_STEP; - setValuePrimAndNumberType(); + this.absoluteMinValue = DEFAULT_MINIMUM; + this.absoluteMaxValue = DEFAULT_MAXIMUM; + this.absoluteStepValue = DEFAULT_STEP; + setValuePrim(); } - private void setValuePrimAndNumberType() { - absoluteMinValuePrim = absoluteMinValue.doubleValue(); - absoluteMaxValuePrim = absoluteMaxValue.doubleValue(); - absoluteStepValuePrim = absoluteStepValue.doubleValue(); - numberType = NumberType.fromNumber(absoluteMinValue); + private void setValuePrim() { + absoluteMinValuePrim = absoluteMinValue; + absoluteMaxValuePrim = absoluteMaxValue; + absoluteStepValuePrim = absoluteStepValue; } - @SuppressWarnings("unused") + @SuppressWarnings("unused") // we're a library public void resetSelectedValues() { setSelectedMinValue(absoluteMinValue); setSelectedMaxValue(absoluteMaxValue); @@ -338,7 +336,7 @@ public void setNotifyWhileDragging(boolean flag) { * * @return The absolute minimum value of the range. */ - public T getAbsoluteMinValue() { + public int getAbsoluteMinValue() { return absoluteMinValue; } @@ -347,7 +345,7 @@ public T getAbsoluteMinValue() { * * @return The absolute maximum value of the range. */ - public T getAbsoluteMaxValue() { + public int getAbsoluteMaxValue() { return absoluteMaxValue; } @@ -357,9 +355,9 @@ public T getAbsoluteMaxValue() { * @return rounded off value */ @SuppressWarnings("unchecked") - private T roundOffValueToStep(T value) { - double d = Math.round(value.doubleValue() / absoluteStepValuePrim) * absoluteStepValuePrim; - return (T) numberType.toNumber(Math.max(absoluteMinValuePrim, Math.min(absoluteMaxValuePrim, d))); + private int roundOffValueToStep(int value) { + double d = Math.round(value / absoluteStepValuePrim) * absoluteStepValuePrim; + return (int) Math.max(absoluteMinValuePrim, Math.min(absoluteMaxValuePrim, d)); } /** @@ -367,7 +365,7 @@ private T roundOffValueToStep(T value) { * * @return The currently selected min value. */ - public T getSelectedMinValue() { + public int getSelectedMinValue() { return roundOffValueToStep(normalizedToValue(normalizedMinValue)); } @@ -378,9 +376,9 @@ public boolean isDragging() { /** * Sets the currently selected minimum value. The widget will be invalidated and redrawn. * - * @param value The Number value to set the minimum value to. Will be clamped to given absolute minimum/maximum range. + * @param value The value to set the minimum value to. Will be clamped to given absolute minimum/maximum range. */ - public void setSelectedMinValue(T value) { + public void setSelectedMinValue(int value) { // in case absoluteMinValue == absoluteMaxValue, avoid division by zero when normalizing. if (0 == (absoluteMaxValuePrim - absoluteMinValuePrim)) { setNormalizedMinValue(0d); @@ -394,16 +392,16 @@ public void setSelectedMinValue(T value) { * * @return The currently selected max value. */ - public T getSelectedMaxValue() { + public int getSelectedMaxValue() { return roundOffValueToStep(normalizedToValue(normalizedMaxValue)); } /** * Sets the currently selected maximum value. The widget will be invalidated and redrawn. * - * @param value The Number value to set the maximum value to. Will be clamped to given absolute minimum/maximum range. + * @param value The value to set the maximum value to. Will be clamped to given absolute minimum/maximum range. */ - public void setSelectedMaxValue(T value) { + public void setSelectedMaxValue(int value) { // in case absoluteMinValue == absoluteMaxValue, avoid division by zero when normalizing. if (0 == (absoluteMaxValuePrim - absoluteMinValuePrim)) { setNormalizedMaxValue(1d); @@ -418,28 +416,15 @@ public void setSelectedMaxValue(T value) { * @param listener The listener to notify about changed selected values. */ @SuppressWarnings("unused") - public void setOnRangeSeekBarChangeListener(OnRangeSeekBarChangeListener listener) { + public void setOnRangeSeekBarChangeListener(OnRangeSeekBarChangeListener listener) { this.listener = listener; } - /** - * Set the path that defines the shadow of the thumb. This path should be defined assuming - * that the center of the shadow is at the top left corner (0,0) of the canvas. The - * {@link #drawThumbShadow(float, Canvas)} method will place the shadow appropriately. - * - * @param thumbShadowPath The path defining the thumb shadow - */ - @SuppressWarnings("unused") - public void setThumbShadowPath(Path thumbShadowPath) { - this.thumbShadowPath = thumbShadowPath; - } - /** * Handles thumb selection and movement. Notifies listener callback on certain events. */ @Override public boolean onTouchEvent(@NonNull MotionEvent event) { - if (!isEnabled()) { return false; } @@ -542,7 +527,6 @@ private void onSecondaryPointerUp(MotionEvent ev) { if (pointerId == activePointerId) { // This was our active pointer going up. Choose // a new active pointer and adjust accordingly. - // TODO: Make this decision more intelligent. final int newPointerIndex = pointerIndex == 0 ? 1 : 0; downMotionX = ev.getX(newPointerIndex); activePointerId = ev.getPointerId(newPointerIndex); @@ -572,15 +556,21 @@ private void attemptClaimDrag() { /** * This is called when the user has started touching this widget. */ - void onStartTrackingTouch() { + private void onStartTrackingTouch() { isDragging = true; + if (listener != null) { + listener.onStartTrackingTouch(this); + } } /** * This is called when the user either releases his touch or the touch is canceled. */ - void onStopTrackingTouch() { + private void onStopTrackingTouch() { isDragging = false; + if (listener != null) { + listener.onStopTrackingTouch(this); + } } /** @@ -698,8 +688,17 @@ protected synchronized void onDraw(@NonNull Canvas canvas) { } - protected String valueToString(T value) { - return String.valueOf(value); + + @SuppressWarnings("unused") + public void setTextFormatter(@NonNull TextFormatter textFormatter) { + this.textFormatter = textFormatter; + } + + private String valueToString(int value) { + if (textFormatter == null) { + return String.valueOf(value); + } + return textFormatter.formatValue(value); } /** @@ -811,27 +810,26 @@ private void setNormalizedMaxValue(double value) { } /** - * Converts a normalized value to a Number object in the value space between absolute minimum and maximum. + * Converts a normalized value to an int in the value space between absolute minimum and maximum. */ @SuppressWarnings("unchecked") - protected T normalizedToValue(double normalized) { + private int normalizedToValue(double normalized) { double v = absoluteMinValuePrim + normalized * (absoluteMaxValuePrim - absoluteMinValuePrim); - // TODO parameterize this rounding to allow variable decimal points - return (T) numberType.toNumber(Math.round(v * 100) / 100d); + return (int) (Math.round(v * 100) / 100d); } /** - * Converts the given Number value to a normalized double. + * Converts the given value to a normalized double. * - * @param value The Number value to normalize. + * @param value The int value to normalize. * @return The normalized double. */ - protected double valueToNormalized(T value) { + private double valueToNormalized(int value) { if (0 == absoluteMaxValuePrim - absoluteMinValuePrim) { // prevent division by zero, simply return 0. return 0d; } - return (value.doubleValue() - absoluteMinValuePrim) / (absoluteMaxValuePrim - absoluteMinValuePrim); + return (value - absoluteMinValuePrim) / (absoluteMaxValuePrim - absoluteMinValuePrim); } /** @@ -869,68 +867,65 @@ private enum Thumb { } /** - * Utility enumeration used to convert between Numbers and doubles. - * - * @author Stephan Tittel (stephan.tittel@kom.tu-darmstadt.de) + * Callback listener interface to notify about changed range values. */ - protected enum NumberType { - LONG, DOUBLE, INTEGER, FLOAT, SHORT, BYTE, BIG_DECIMAL; + // TODO should we add fromUser + public interface OnRangeSeekBarChangeListener { - public static NumberType fromNumber(E value) throws IllegalArgumentException { - if (value instanceof Long) { - return LONG; - } - if (value instanceof Double) { - return DOUBLE; - } - if (value instanceof Integer) { - return INTEGER; - } - if (value instanceof Float) { - return FLOAT; - } - if (value instanceof Short) { - return SHORT; - } - if (value instanceof Byte) { - return BYTE; - } - if (value instanceof BigDecimal) { - return BIG_DECIMAL; - } - throw new IllegalArgumentException("Number class '" + value.getClass().getName() + "' is not supported"); - } + /** + * Notification that the progress level has changed. Clients can use the fromUser parameter + * to distinguish user-initiated changes from those that occurred programmatically. + * + * @param rangeSeekBar The RangeSeekBar whose progress has changed + * @param selectedMinValue The current value selected by the left/minimum thumb. + * @param selectedMaxValue The current value selected by the right/maximum thumb. + */ + void onRangeSeekBarValuesChanged(RangeSeekBar rangeSeekBar, int selectedMinValue, int selectedMaxValue); - public Number toNumber(double value) { - switch (this) { - case LONG: - return (long) value; - case DOUBLE: - return value; - case INTEGER: - return (int) value; - case FLOAT: - return (float) value; - case SHORT: - return (short) value; - case BYTE: - return (byte) value; - case BIG_DECIMAL: - return BigDecimal.valueOf(value); - } - throw new InstantiationError("can't convert " + this + " to a Number object"); - } + /** + * Notification that the user has started a touch gesture. Clients may want to use this + * to disable advancing the seekbar. + * + * @param rangeSeekBar The RangeSeekBar in which the touch gesture began + */ + void onStartTrackingTouch(RangeSeekBar rangeSeekBar); + + /** + * Notification that the user has finished a touch gesture. Clients may want to use this + * to re-enable advancing the seekbar. + * + * @param rangeSeekBar The RangeSeekBar in which the touch gesture began + */ + void onStopTrackingTouch(RangeSeekBar rangeSeekBar); } /** - * Callback listener interface to notify about changed range values. - * - * @param The Number type the RangeSeekBar has been declared with. - * @author Stephan Tittel (stephan.tittel@kom.tu-darmstadt.de) + * An utility interface allowing clients to format the text shown by the bar in any way they want. */ - public interface OnRangeSeekBarChangeListener { + public interface TextFormatter { + String formatValue(int value); + } + + /** + * A helper abstract class so that clients can implement only the listener methods they care about + * from {@link OnRangeSeekBarChangeListener} + */ + public abstract class SimpleRangeSeekBarChangeListener implements OnRangeSeekBarChangeListener { + + @Override + public void onRangeSeekBarValuesChanged(RangeSeekBar rangeSeekBar, int selectedMinValue, int selectedMaxValue) { - void onRangeSeekBarValuesChanged(RangeSeekBar bar, T minValue, T maxValue); + } + + @Override + public void onStartTrackingTouch(RangeSeekBar rangeSeekBar) { + + } + + @Override + public void onStopTrackingTouch(RangeSeekBar rangeSeekBar) { + + } } } diff --git a/rangeseekbar/src/main/java/org/florescu/android/util/BitmapUtil.java b/rangeseekbar/src/main/java/org/florescu/android/util/BitmapUtil.java index d01faa3..81022cb 100644 --- a/rangeseekbar/src/main/java/org/florescu/android/util/BitmapUtil.java +++ b/rangeseekbar/src/main/java/org/florescu/android/util/BitmapUtil.java @@ -5,7 +5,12 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; -public class BitmapUtil { +public final class BitmapUtil { + + private BitmapUtil() { + throw new AssertionError("Don't instantiate me"); + } + public static Bitmap drawableToBitmap(Drawable drawable) { if (drawable instanceof BitmapDrawable) { return ((BitmapDrawable) drawable).getBitmap(); diff --git a/rangeseekbar/src/main/java/org/florescu/android/util/PixelUtil.java b/rangeseekbar/src/main/java/org/florescu/android/util/PixelUtil.java index 0af431a..d07a934 100644 --- a/rangeseekbar/src/main/java/org/florescu/android/util/PixelUtil.java +++ b/rangeseekbar/src/main/java/org/florescu/android/util/PixelUtil.java @@ -23,19 +23,14 @@ /** * Util class for converting between dp, px and other magical pixel units */ -public class PixelUtil { +public final class PixelUtil { private PixelUtil() { + throw new AssertionError("Don't instantiate me"); } public static int dpToPx(Context context, int dp) { - int px = Math.round(dp * getPixelScaleFactor(context)); - return px; - } - - public static int pxToDp(Context context, int px) { - int dp = Math.round(px / getPixelScaleFactor(context)); - return dp; + return Math.round(dp * getPixelScaleFactor(context)); } private static float getPixelScaleFactor(Context context) { diff --git a/rangeseekbar/src/main/res/values/strings.xml b/rangeseekbar/src/main/res/values/strings.xml index 5136f1f..84ad260 100644 --- a/rangeseekbar/src/main/res/values/strings.xml +++ b/rangeseekbar/src/main/res/values/strings.xml @@ -1,6 +1,5 @@ - RangeSeekBar Demo Min Max diff --git a/rangeseekbar/src/test/java/org/florescu/android/rangeseekbar/RangeSeekBarTest.java b/rangeseekbar/src/test/java/org/florescu/android/rangeseekbar/RangeSeekBarTest.java index ce0f2a5..a2ea926 100644 --- a/rangeseekbar/src/test/java/org/florescu/android/rangeseekbar/RangeSeekBarTest.java +++ b/rangeseekbar/src/test/java/org/florescu/android/rangeseekbar/RangeSeekBarTest.java @@ -1,25 +1,19 @@ package org.florescu.android.rangeseekbar; -import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; -import org.robolectric.RobolectricGradleTestRunner; -import org.robolectric.RuntimeEnvironment; +import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; -@RunWith(RobolectricGradleTestRunner.class) +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 21) public class RangeSeekBarTest { @Test - public void rsb_should_handle_long_values() { - RangeSeekBar mSeekBar = new RangeSeekBar<>(RuntimeEnvironment.application); - // Set up the seek bar - mSeekBar.setRangeValues(0L, 100L); - long minValue = mSeekBar.getAbsoluteMinValue(); - Assert.assertEquals(0L, minValue); - long maxValue = mSeekBar.getAbsoluteMaxValue(); - Assert.assertEquals(100L, maxValue); + public void emptyTest() { + // TODO + assertThat(true).isTrue(); } - } \ No newline at end of file