package com.kedzie.vbox.app; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.TextView; import com.kedzie.vbox.R; /** * Feature-rich Horizontal Slider Form component. * <ul> * <li>Tick marks along axis</li> * <li>Textual display of Min/Max/Current Values</li> * <li>Optionally insert value directly using EditText</li> * </ul> */ public class SliderView extends LinearLayout { public static interface OnSliderViewChangeListener { public void onSliderValidValueChanged(int newValue); public void onSliderInvalidValueChanged(int newValue); } public class SliderBar extends SeekBar { private final int VALID_RANGE_BAR_HEIGHT=Utils.dpiToPx(getContext(), 12); private Rect _invalidRect; private Rect _validRect; private Paint _validPaint; private Paint _invalidPaint; private Paint _tickPaint; private Paint _textPaint; /** How many pixels in a single unit */ private float _pixelsPerUnit; private Drawable mThumb; public SliderBar(Context context) { super(context); setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override public void onStopTrackingTouch(SeekBar seekBar) {} @Override public void onStartTrackingTouch(SeekBar seekBar) {} @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { _valueEditText.setText(progress+_minValue+""); if(_onSliderChangeListener!=null) { int value = progress+_minValue; if(value >= _minValidValue && value <= _maxValidValue) _onSliderChangeListener.onSliderValidValueChanged(value); else _onSliderChangeListener.onSliderInvalidValueChanged(value); } } }); mThumb=getContext().getResources().getDrawable(R.drawable.ic_navigation_expand); setThumb(mThumb); _validPaint = new Paint(); _validPaint.setColor(0xFF00FF00); _validPaint.setStyle(Style.FILL); _invalidPaint = new Paint(); _invalidPaint.setColor(0xFFFF0000); _invalidPaint.setStyle(Style.FILL); _tickPaint = new Paint(); _tickPaint.setColor(0xFF000000); _tickPaint.setStyle(Style.STROKE); _tickPaint.setStrokeWidth(2f); _textPaint = new Paint(); _textPaint.setColor(getResources().getColor(android.R.color.primary_text_dark)); _textPaint.setTextSize(Utils.dpiToPx(getContext(), 12)); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); updateProperties(); } void updateProperties() { _pixelsPerUnit = ((float)getWidth())/(_maxValue-_minValue); int validLeft = (int) ((_minValidValue-_minValue)*_pixelsPerUnit+getLeft()+getPaddingLeft()); int validRight =(int) (getRight()-getPaddingRight()-(_maxValue-_maxValidValue)*_pixelsPerUnit); int bottom = getBottom()-getPaddingBottom()-(int)_textPaint.getTextSize(); int top = bottom-VALID_RANGE_BAR_HEIGHT; _invalidRect = new Rect(getLeft()+getPaddingLeft(), top, getRight()-getPaddingRight(), bottom); _validRect = new Rect(validLeft, top, validRight, bottom); postInvalidate(); } @Override protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { Drawable d = super.getProgressDrawable(); int thumbHeight = mThumb == null ? 0 : mThumb.getIntrinsicHeight(); int dw = 0; int dh = 0; if (d != null) { dw =d.getIntrinsicWidth(); dh = Math.max(thumbHeight, d.getIntrinsicHeight()); } dw += getPaddingLeft() + getPaddingRight(); dh += getPaddingTop() + getPaddingBottom() + VALID_RANGE_BAR_HEIGHT+_textPaint.getTextSize(); setMeasuredDimension(resolveSize(dw, widthMeasureSpec), resolveSize(dh, heightMeasureSpec)); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawRect(_invalidRect, _invalidPaint); canvas.drawRect(_validRect, _validPaint); for(int x=_invalidRect.left; x<=_invalidRect.right; x+=_pixelsPerUnit*_tickSpacing) canvas.drawLine(x, _invalidRect.bottom, x, _invalidRect.top, _tickPaint); String min = _minValue + " " + _unit; Rect minBounds = new Rect(); _textPaint.getTextBounds(min, 0, min.length(), minBounds); canvas.drawText(min, getLeft()+getPaddingLeft(), getBottom(), _textPaint); String max = _maxValue + " " + _unit; Rect maxBounds = new Rect(); _textPaint.getTextBounds(max, 0, max.length(), maxBounds); canvas.drawText(max, getRight()-getPaddingRight()-maxBounds.width(), getBottom(), _textPaint); } } /** tick-mark spacing */ int _tickSpacing; /** Unit for slider values */ private String _unit; int _minValue; int _maxValue; int _minValidValue; int _maxValidValue; int _range; private SliderBar _sliderBar; private TextView _unitLabel; private EditText _valueEditText; private OnSliderViewChangeListener _onSliderChangeListener; public SliderView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SliderView, 0, 0); try { _tickSpacing = a.getInt(R.styleable.SliderView_tickSpacing, 1); _unit = a.hasValue(R.styleable.SliderView_unit) ? a.getString(R.styleable.SliderView_unit) : "NULL"; _minValue = a.getInt(R.styleable.SliderView_minValue, 1); _maxValue = a.getInt(R.styleable.SliderView_maxValue, 10); _minValidValue = a.getInt(R.styleable.SliderView_minValidValue, 4); _maxValidValue = a.getInt(R.styleable.SliderView_maxValidValue, 8); } finally { a.recycle(); } init(); } public SliderView(Context context) { super(context); init(); } private void init() { setOrientation(HORIZONTAL); _sliderBar = new SliderBar(getContext()); addView(_sliderBar, new LayoutParams(0, LayoutParams.WRAP_CONTENT, 1)); _valueEditText = new EditText(getContext()); _valueEditText.setText(getValue()+""); addView(_valueEditText, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); _unitLabel = new TextView(getContext()); LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); lp.leftMargin = 2; addView(_unitLabel, lp); updateProperties(); _unitLabel.setText(_unit); } private void updateProperties() { _range=_maxValue-_minValue; _sliderBar.setMax(_range); _sliderBar.updateProperties(); } public void setOnSliderViewChangeListener(OnSliderViewChangeListener listener) { _onSliderChangeListener = listener; } public int getValue() { return _sliderBar.getProgress()+_minValue; } public void setValue(int value) { _sliderBar.setProgress(value-_minValue); } public int getMaxValue() { return _maxValue; } public void setMaxValue(int max) { _maxValue=max; updateProperties(); } public int getMinValue() { return _minValue; } public void setMinValue(int min) { _minValue=min; updateProperties(); } public int getMaxValidValue() { return _maxValidValue; } public void setMaxValidValue(int max) { _maxValidValue=max; updateProperties(); } public int getMinValidValue() { return _minValidValue; } public void setMinValidValue(int min) { _minValidValue=min; updateProperties(); } }