some changes

This commit is contained in:
serso 2011-09-20 18:20:39 +04:00
parent 0e2a85ae9e
commit 877412145b
7 changed files with 164 additions and 31 deletions

View File

@ -51,7 +51,7 @@ public abstract class RangeSeekBarPreference<T extends Number> extends AbstractD
max = c.convert(maxString == null ? "100" : maxString); max = c.convert(maxString == null ? "100" : maxString);
this.rangeSeekBar = new NumberRangeSeekBar<T>(min, max, context); this.rangeSeekBar = new NumberRangeSeekBar<T>(min, max, null, context);
rangeSeekBar.setOnRangeSeekBarChangeListener(this); rangeSeekBar.setOnRangeSeekBarChangeListener(this);
} }
@ -70,7 +70,7 @@ public abstract class RangeSeekBarPreference<T extends Number> extends AbstractD
protected LinearLayout onCreateDialogView() { protected LinearLayout onCreateDialogView() {
final LinearLayout result = super.onCreateDialogView(); final LinearLayout result = super.onCreateDialogView();
this.rangeSeekBar = new NumberRangeSeekBar<T>(min, max, context); this.rangeSeekBar = new NumberRangeSeekBar<T>(min, max, null, context);
rangeSeekBar.setOnRangeSeekBarChangeListener(this); rangeSeekBar.setOnRangeSeekBarChangeListener(this);
initRangeSeekBar(); initRangeSeekBar();

View File

@ -14,7 +14,9 @@ import android.widget.ImageView;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.R; import org.solovyev.android.calculator.R;
import org.solovyev.common.math.DiscreteNormalizer;
import org.solovyev.common.math.LinearNormalizer; import org.solovyev.common.math.LinearNormalizer;
import org.solovyev.common.math.Normalizer;
import org.solovyev.common.utils.Converter; import org.solovyev.common.utils.Converter;
/** /**
@ -30,20 +32,7 @@ public abstract class AbstractRangeSeekBar<T> extends ImageView {
private final Paint paint = new Paint(); private final Paint paint = new Paint();
@NotNull @NotNull
private final Bitmap thumbImage = BitmapFactory.decodeResource(getResources(), R.drawable.seek_thumb_normal); private final ThumbContainer tc;
@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;
@NotNull @NotNull
private final Converter<T, Double> toDoubleConverter; private final Converter<T, Double> toDoubleConverter;
@ -55,7 +44,7 @@ public abstract class AbstractRangeSeekBar<T> extends ImageView {
private final T minValue, maxValue; private final T minValue, maxValue;
@NotNull @NotNull
private final LinearNormalizer normalizer; private final Normalizer normalizer;
private double normalizedMinValue = 0d; private double normalizedMinValue = 0d;
@ -77,7 +66,7 @@ public abstract class AbstractRangeSeekBar<T> extends ImageView {
* @param context * @param context
* @throws IllegalArgumentException Will be thrown if min/max value types are not one of Long, Double, Integer, Float, Short, Byte or BigDecimal. * @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); super(context);
this.minValue = minValue; this.minValue = minValue;
@ -86,7 +75,13 @@ public abstract class AbstractRangeSeekBar<T> extends ImageView {
this.toDoubleConverter = getToDoubleConverter(); this.toDoubleConverter = getToDoubleConverter();
this.toTConverter = getToTConverter(); 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 @NotNull
@ -216,7 +211,8 @@ public abstract class AbstractRangeSeekBar<T> extends ImageView {
if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(widthMeasureSpec)) { if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(widthMeasureSpec)) {
width = MeasureSpec.getSize(widthMeasureSpec); width = MeasureSpec.getSize(widthMeasureSpec);
} }
int height = thumbImage.getHeight();
int height = tc.thumbImage.getHeight();
if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(heightMeasureSpec)) { if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(heightMeasureSpec)) {
height = Math.min(height, MeasureSpec.getSize(heightMeasureSpec)); height = Math.min(height, MeasureSpec.getSize(heightMeasureSpec));
} }
@ -230,7 +226,7 @@ public abstract class AbstractRangeSeekBar<T> extends ImageView {
protected void onDraw(Canvas canvas) { protected void onDraw(Canvas canvas) {
super.onDraw(canvas); super.onDraw(canvas);
// draw seek bar background line // 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.setStyle(Style.FILL);
paint.setColor(Color.GRAY); paint.setColor(Color.GRAY);
canvas.drawRect(rect, paint); canvas.drawRect(rect, paint);
@ -256,7 +252,7 @@ public abstract class AbstractRangeSeekBar<T> extends ImageView {
* @param canvas The canvas to draw upon. * @param canvas The canvas to draw upon.
*/ */
private void drawThumb(float normalizedToScreenValue, boolean pressed, Canvas canvas) { 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<T> extends ImageView {
* @return true if x-coordinate is in thumb range, false otherwise. * @return true if x-coordinate is in thumb range, false otherwise.
*/ */
private boolean isInThumbRange(float touchX, double normalizedThumbValue) { 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<T> extends ImageView {
* @return The converted value in screen space. * @return The converted value in screen space.
*/ */
private float convertToScreenValue(double normalizedValue) { 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<T> extends ImageView {
*/ */
private double convertToNormalizedValue(float screenValue) { private double convertToNormalizedValue(float screenValue) {
int width = getWidth(); int width = getWidth();
if (width <= 2 * padding) { if (width <= 2 * tc.padding) {
// prevent division by zero, simply return 0. // prevent division by zero, simply return 0.
return 0d; return 0d;
} else { } 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)); return Math.min(1d, Math.max(0d, result));
} }
} }
@ -380,4 +376,30 @@ public abstract class AbstractRangeSeekBar<T> extends ImageView {
MIN, MAX 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;
}
}
} }

View File

@ -2,6 +2,7 @@ package org.solovyev.android.view.widgets;
import android.content.Context; import android.content.Context;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.utils.Converter; import org.solovyev.common.utils.Converter;
import org.solovyev.common.utils.NumberValuer; import org.solovyev.common.utils.NumberValuer;
@ -20,11 +21,12 @@ public class NumberRangeSeekBar<T extends Number> extends AbstractRangeSeekBar<T
* *
* @param minValue The minimum value of the selectable range. * @param minValue The minimum value of the selectable range.
* @param maxValue The maximum value of the selectable range. * @param maxValue The maximum value of the selectable range.
* @param steps number of steps of range
* @param context parent context * @param context parent context
* @throws IllegalArgumentException Will be thrown if min/max value types are not one of Long, Double, Integer, Float, Short, Byte or BigDecimal. * @throws IllegalArgumentException Will be thrown if min/max value types are not one of Long, Double, Integer, Float, Short, Byte or BigDecimal.
*/ */
public NumberRangeSeekBar(@NotNull T minValue, @NotNull T maxValue, Context context) throws IllegalArgumentException { public NumberRangeSeekBar(@NotNull T minValue, @NotNull T maxValue, @Nullable Integer steps, Context context) throws IllegalArgumentException {
super(minValue, maxValue, context); super(minValue, maxValue, steps, context);
numberType = NumberType.fromNumber(minValue); numberType = NumberType.fromNumber(minValue);
@ -36,8 +38,8 @@ public class NumberRangeSeekBar<T extends Number> extends AbstractRangeSeekBar<T
return new Converter<Double, T>() { return new Converter<Double, T>() {
@NotNull @NotNull
@Override @Override
public T convert(@NotNull Double aDouble) { public T convert(@NotNull Double value) {
return (T) numberType.toNumber(aDouble); return (T) numberType.toNumber(value);
} }
}; };
} }

View File

@ -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);
}
}

View File

@ -11,7 +11,7 @@ package org.solovyev.common.math;
* Date: 9/19/11 * Date: 9/19/11
* Time: 9:31 PM * Time: 9:31 PM
*/ */
public class LinearNormalizer { public class LinearNormalizer implements Normalizer {
private final double min; private final double min;
private final double max; private final double max;
@ -21,6 +21,7 @@ public class LinearNormalizer {
this.max = max; this.max = max;
} }
@Override
public double normalize(double value){ public double normalize(double value){
if ((max - min) != 0d) { if ((max - min) != 0d) {
return (value - min) / (max - min); return (value - min) / (max - min);
@ -29,6 +30,7 @@ public class LinearNormalizer {
} }
} }
@Override
public double denormalize(double value){ public double denormalize(double value){
return min + value * (max - min); return min + value * (max - min);
} }

View File

@ -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);
}

View File

@ -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));
}
}