From 62044e13f9c7ffddf942f8a9718f8095d3520daf Mon Sep 17 00:00:00 2001 From: serso Date: Tue, 20 Sep 2011 18:20:39 +0400 Subject: [PATCH] some changes --- .../view/prefs/RangeSeekBarPreference.java | 4 +- .../view/widgets/AbstractRangeSeekBar.java | 70 ++++++++++++------- .../view/widgets/NumberRangeSeekBar.java | 10 +-- .../common/math/DiscreteNormalizer.java | 59 ++++++++++++++++ .../common/math/LinearNormalizer.java | 4 +- .../org/solovyev/common/math/Normalizer.java | 13 ++++ .../common/math/DiscreteNormalizerTest.java | 35 ++++++++++ 7 files changed, 164 insertions(+), 31 deletions(-) create mode 100644 src/main/java/org/solovyev/common/math/DiscreteNormalizer.java create mode 100644 src/main/java/org/solovyev/common/math/Normalizer.java create mode 100644 src/test/java/org/solovyev/common/math/DiscreteNormalizerTest.java diff --git a/src/main/java/org/solovyev/android/view/prefs/RangeSeekBarPreference.java b/src/main/java/org/solovyev/android/view/prefs/RangeSeekBarPreference.java index 79748b74..113cf827 100644 --- a/src/main/java/org/solovyev/android/view/prefs/RangeSeekBarPreference.java +++ b/src/main/java/org/solovyev/android/view/prefs/RangeSeekBarPreference.java @@ -51,7 +51,7 @@ public abstract class RangeSeekBarPreference extends AbstractD max = c.convert(maxString == null ? "100" : maxString); - this.rangeSeekBar = new NumberRangeSeekBar(min, max, context); + this.rangeSeekBar = new NumberRangeSeekBar(min, max, null, context); rangeSeekBar.setOnRangeSeekBarChangeListener(this); } @@ -70,7 +70,7 @@ public abstract class RangeSeekBarPreference extends AbstractD protected LinearLayout onCreateDialogView() { final LinearLayout result = super.onCreateDialogView(); - this.rangeSeekBar = new NumberRangeSeekBar(min, max, context); + this.rangeSeekBar = new NumberRangeSeekBar(min, max, null, context); rangeSeekBar.setOnRangeSeekBarChangeListener(this); initRangeSeekBar(); diff --git a/src/main/java/org/solovyev/android/view/widgets/AbstractRangeSeekBar.java b/src/main/java/org/solovyev/android/view/widgets/AbstractRangeSeekBar.java index 6afc59ef..843b4f65 100644 --- a/src/main/java/org/solovyev/android/view/widgets/AbstractRangeSeekBar.java +++ b/src/main/java/org/solovyev/android/view/widgets/AbstractRangeSeekBar.java @@ -14,7 +14,9 @@ import android.widget.ImageView; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.solovyev.android.calculator.R; +import org.solovyev.common.math.DiscreteNormalizer; import org.solovyev.common.math.LinearNormalizer; +import org.solovyev.common.math.Normalizer; import org.solovyev.common.utils.Converter; /** @@ -30,20 +32,7 @@ public abstract class AbstractRangeSeekBar extends ImageView { private final Paint paint = new Paint(); @NotNull - private final Bitmap thumbImage = BitmapFactory.decodeResource(getResources(), R.drawable.seek_thumb_normal); - - @NotNull - private final Bitmap thumbPressedImage = BitmapFactory.decodeResource(getResources(), R.drawable.seek_thumb_pressed); - - private final float thumbWidth = thumbImage.getWidth(); - - private final float thumbHalfWidth = 0.5f * thumbWidth; - - private final float thumbHalfHeight = 0.5f * thumbImage.getHeight(); - - private final float lineHeight = 0.3f * thumbHalfHeight; - - private final float padding = thumbHalfWidth; + private final ThumbContainer tc; @NotNull private final Converter toDoubleConverter; @@ -55,7 +44,7 @@ public abstract class AbstractRangeSeekBar extends ImageView { private final T minValue, maxValue; @NotNull - private final LinearNormalizer normalizer; + private final Normalizer normalizer; private double normalizedMinValue = 0d; @@ -77,7 +66,7 @@ public abstract class AbstractRangeSeekBar extends ImageView { * @param context * @throws IllegalArgumentException Will be thrown if min/max value types are not one of Long, Double, Integer, Float, Short, Byte or BigDecimal. */ - public AbstractRangeSeekBar(@NotNull T minValue, @NotNull T maxValue, Context context) throws IllegalArgumentException { + public AbstractRangeSeekBar(@NotNull T minValue, @NotNull T maxValue, @Nullable Integer steps, Context context) throws IllegalArgumentException { super(context); this.minValue = minValue; @@ -86,7 +75,13 @@ public abstract class AbstractRangeSeekBar extends ImageView { this.toDoubleConverter = getToDoubleConverter(); this.toTConverter = getToTConverter(); - normalizer = new LinearNormalizer(toDoubleConverter.convert(minValue), toDoubleConverter.convert(maxValue)); + if (steps == null) { + normalizer = new LinearNormalizer(toDoubleConverter.convert(minValue), toDoubleConverter.convert(maxValue)); + } else { + normalizer = new DiscreteNormalizer(toDoubleConverter.convert(minValue), toDoubleConverter.convert(maxValue), steps); + } + + tc = new ThumbContainer(); } @NotNull @@ -216,7 +211,8 @@ public abstract class AbstractRangeSeekBar extends ImageView { if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(widthMeasureSpec)) { width = MeasureSpec.getSize(widthMeasureSpec); } - int height = thumbImage.getHeight(); + + int height = tc.thumbImage.getHeight(); if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(heightMeasureSpec)) { height = Math.min(height, MeasureSpec.getSize(heightMeasureSpec)); } @@ -230,7 +226,7 @@ public abstract class AbstractRangeSeekBar extends ImageView { protected void onDraw(Canvas canvas) { super.onDraw(canvas); // draw seek bar background line - final RectF rect = new RectF(padding, 0.5f * (getHeight() - lineHeight), getWidth() - padding, 0.5f * (getHeight() + lineHeight)); + final RectF rect = tc.getRect(); paint.setStyle(Style.FILL); paint.setColor(Color.GRAY); canvas.drawRect(rect, paint); @@ -256,7 +252,7 @@ public abstract class AbstractRangeSeekBar extends ImageView { * @param canvas The canvas to draw upon. */ private void drawThumb(float normalizedToScreenValue, boolean pressed, Canvas canvas) { - canvas.drawBitmap(pressed ? thumbPressedImage : thumbImage, normalizedToScreenValue - thumbHalfWidth, (float) ((0.5f * getHeight()) - thumbHalfHeight), paint); + canvas.drawBitmap(tc.getImage(pressed), normalizedToScreenValue - tc.thumbHalfWidth, (float) ((0.5f * getHeight()) - tc.thumbHalfHeight), paint); } /** @@ -288,7 +284,7 @@ public abstract class AbstractRangeSeekBar extends ImageView { * @return true if x-coordinate is in thumb range, false otherwise. */ private boolean isInThumbRange(float touchX, double normalizedThumbValue) { - return Math.abs(touchX - convertToScreenValue(normalizedThumbValue)) <= thumbHalfWidth; + return Math.abs(touchX - convertToScreenValue(normalizedThumbValue)) <= tc.thumbHalfWidth; } /** @@ -341,7 +337,7 @@ public abstract class AbstractRangeSeekBar extends ImageView { * @return The converted value in screen space. */ private float convertToScreenValue(double normalizedValue) { - return (float) (padding + normalizedValue * (getWidth() - 2 * padding)); + return (float) (tc.padding + normalizedValue * (getWidth() - 2 * tc.padding)); } /** @@ -352,11 +348,11 @@ public abstract class AbstractRangeSeekBar extends ImageView { */ private double convertToNormalizedValue(float screenValue) { int width = getWidth(); - if (width <= 2 * padding) { + if (width <= 2 * tc.padding) { // prevent division by zero, simply return 0. return 0d; } else { - double result = (screenValue - padding) / (width - 2 * padding); + double result = (screenValue - tc.padding) / (width - 2 * tc.padding); return Math.min(1d, Math.max(0d, result)); } } @@ -380,4 +376,30 @@ public abstract class AbstractRangeSeekBar extends ImageView { MIN, MAX } + private class ThumbContainer { + @NotNull + private final Bitmap thumbImage = BitmapFactory.decodeResource(getResources(), R.drawable.seek_thumb_normal); + + @NotNull + private final Bitmap thumbPressedImage = BitmapFactory.decodeResource(getResources(), R.drawable.seek_thumb_pressed); + + private final float thumbWidth = thumbImage.getWidth(); + + private final float thumbHalfWidth = 0.5f * thumbWidth; + + private final float thumbHalfHeight = 0.5f * thumbImage.getHeight(); + + private final float lineHeight = 0.3f * thumbHalfHeight; + + private final float padding = thumbHalfWidth; + + public RectF getRect() { + return new RectF(padding, 0.5f * (getHeight() - lineHeight), getWidth() - padding, 0.5f * (getHeight() + lineHeight)); + } + + public Bitmap getImage(boolean pressed) { + return pressed ? thumbPressedImage : thumbImage; + } + } + } diff --git a/src/main/java/org/solovyev/android/view/widgets/NumberRangeSeekBar.java b/src/main/java/org/solovyev/android/view/widgets/NumberRangeSeekBar.java index 0f4cf9f9..c7b0d06f 100644 --- a/src/main/java/org/solovyev/android/view/widgets/NumberRangeSeekBar.java +++ b/src/main/java/org/solovyev/android/view/widgets/NumberRangeSeekBar.java @@ -2,6 +2,7 @@ package org.solovyev.android.view.widgets; import android.content.Context; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.solovyev.common.utils.Converter; import org.solovyev.common.utils.NumberValuer; @@ -20,11 +21,12 @@ public class NumberRangeSeekBar extends AbstractRangeSeekBar extends AbstractRangeSeekBar() { @NotNull @Override - public T convert(@NotNull Double aDouble) { - return (T) numberType.toNumber(aDouble); + public T convert(@NotNull Double value) { + return (T) numberType.toNumber(value); } }; } diff --git a/src/main/java/org/solovyev/common/math/DiscreteNormalizer.java b/src/main/java/org/solovyev/common/math/DiscreteNormalizer.java new file mode 100644 index 00000000..0dff938b --- /dev/null +++ b/src/main/java/org/solovyev/common/math/DiscreteNormalizer.java @@ -0,0 +1,59 @@ +package org.solovyev.common.math; + +import org.jetbrains.annotations.NotNull; + +/** + * User: serso + * Date: 9/20/11 + * Time: 5:42 PM + */ +public class DiscreteNormalizer implements Normalizer { + + @NotNull + private final LinearNormalizer linearNormalizer; + + private final double min; + + private final double step; + + public DiscreteNormalizer(double min, double max, int steps) { + assert min <= max; + assert steps > 1; + + this.linearNormalizer = new LinearNormalizer(min, max); + + this.step = linearNormalizer.normalize((max - min) / (steps - 1)); + this.min = linearNormalizer.normalize(min); + } + + public DiscreteNormalizer(double min, double max, double step) { + assert min <= max; + assert step > 0; + + this.linearNormalizer = new LinearNormalizer(min, max); + + this.step = linearNormalizer.normalize(step); + this.min = linearNormalizer.normalize(min); + } + + @Override + public double normalize(double value) { + double normalizedValue = linearNormalizer.normalize(value); + + double result = min; + while (true) { + if ( result + step > normalizedValue ) { + break; + } else { + result += step; + } + } + + return result; + } + + @Override + public double denormalize(double value) { + return linearNormalizer.denormalize(value); + } +} diff --git a/src/main/java/org/solovyev/common/math/LinearNormalizer.java b/src/main/java/org/solovyev/common/math/LinearNormalizer.java index c0b3a3af..5e9a15ce 100644 --- a/src/main/java/org/solovyev/common/math/LinearNormalizer.java +++ b/src/main/java/org/solovyev/common/math/LinearNormalizer.java @@ -11,7 +11,7 @@ package org.solovyev.common.math; * Date: 9/19/11 * Time: 9:31 PM */ -public class LinearNormalizer { +public class LinearNormalizer implements Normalizer { private final double min; private final double max; @@ -21,6 +21,7 @@ public class LinearNormalizer { this.max = max; } + @Override public double normalize(double value){ if ((max - min) != 0d) { return (value - min) / (max - min); @@ -29,6 +30,7 @@ public class LinearNormalizer { } } + @Override public double denormalize(double value){ return min + value * (max - min); } diff --git a/src/main/java/org/solovyev/common/math/Normalizer.java b/src/main/java/org/solovyev/common/math/Normalizer.java new file mode 100644 index 00000000..a35d8335 --- /dev/null +++ b/src/main/java/org/solovyev/common/math/Normalizer.java @@ -0,0 +1,13 @@ +package org.solovyev.common.math; + +/** + * User: serso + * Date: 9/20/11 + * Time: 5:30 PM + */ +public interface Normalizer { + + double normalize(double value); + + double denormalize(double value); +} diff --git a/src/test/java/org/solovyev/common/math/DiscreteNormalizerTest.java b/src/test/java/org/solovyev/common/math/DiscreteNormalizerTest.java new file mode 100644 index 00000000..ce4c5906 --- /dev/null +++ b/src/test/java/org/solovyev/common/math/DiscreteNormalizerTest.java @@ -0,0 +1,35 @@ +package org.solovyev.common.math; + +import org.junit.Assert; +import org.junit.Test; +import org.solovyev.common.utils.MathUtils; + +/** + * User: serso + * Date: 9/20/11 + * Time: 5:51 PM + */ +public class DiscreteNormalizerTest { + + @Test + public void testNormalize() throws Exception { + DiscreteNormalizer dn = new DiscreteNormalizer(0, 10, 1); + + Assert.assertTrue(MathUtils.equals(0, dn.normalize(0.5), 2)); + Assert.assertTrue(MathUtils.equals(0, dn.normalize(0.99), 2)); + Assert.assertTrue(MathUtils.equals(0.1, dn.normalize(1), 2)); + Assert.assertTrue(MathUtils.equals(0.1, dn.normalize(1.01), 2)); + Assert.assertTrue(MathUtils.equals(1, dn.normalize(10), 2)); + Assert.assertTrue(MathUtils.equals(0.9, dn.normalize(9.99), 2)); + } + + @Test + public void testDenormalize() throws Exception { + DiscreteNormalizer dn = new DiscreteNormalizer(0, 10, 1); + + Assert.assertTrue(MathUtils.equals(0, dn.normalize(dn.denormalize(0)), 2)); + Assert.assertTrue(MathUtils.equals(0.1, dn.normalize(dn.denormalize(0.1)), 2)); + Assert.assertTrue(MathUtils.equals(1, dn.normalize(dn.denormalize(1)), 2)); + Assert.assertTrue(MathUtils.equals(0.9, dn.normalize(dn.denormalize(0.9)), 2)); + } +}