From a7a8f25954e8c12d0baa1f1c09228e2528f2cc40 Mon Sep 17 00:00:00 2001 From: Ed George Date: Mon, 25 Jul 2016 10:21:39 +0100 Subject: [PATCH] Add Percentage Offset attribute The percentage offset attribute allows the user control of the slider switch to be drawn as a percentage of the overall control. The valid values for this are floats greater than 0 and less than 1 --- README.md | 10 +- SlideSwitch/res/values/attrs.xml | 2 + .../com/leaking/slideswitch/SlideSwitch.java | 503 +++++++++--------- 3 files changed, 270 insertions(+), 245 deletions(-) diff --git a/README.md b/README.md index c9503ed..a60dcb6 100644 --- a/README.md +++ b/README.md @@ -39,12 +39,12 @@ allprojects { ### Eclipse -import it as a library project. +Import it as a library project. ## How to use it -you can define a slideswitch in xml like the following example +You can define a slideswitch in a layout xml using the following example ```xml @@ -53,13 +53,13 @@ you can define a slideswitch in xml like the following example android:layout_height="120dip" slideswitch:isOpen="false" slideswitch:shape="circle" - slideswitch:themeColor="#f200aa96" > - + slideswitch:offsetPercent="0.65" + slideswitch:themeColor="#f200aa96"/> ``` -you can initial the state(on or off) in jave code in this way +You can initial the state (on or off) in Java code using the following method ```java diff --git a/SlideSwitch/res/values/attrs.xml b/SlideSwitch/res/values/attrs.xml index 6a784a0..37de41f 100644 --- a/SlideSwitch/res/values/attrs.xml +++ b/SlideSwitch/res/values/attrs.xml @@ -4,10 +4,12 @@ + + \ No newline at end of file diff --git a/SlideSwitch/src/com/leaking/slideswitch/SlideSwitch.java b/SlideSwitch/src/com/leaking/slideswitch/SlideSwitch.java index bb51baf..ab1db90 100644 --- a/SlideSwitch/src/com/leaking/slideswitch/SlideSwitch.java +++ b/SlideSwitch/src/com/leaking/slideswitch/SlideSwitch.java @@ -39,262 +39,285 @@ public class SlideSwitch extends View { - public static final int SHAPE_RECT = 1; - public static final int SHAPE_CIRCLE = 2; - private static final int RIM_SIZE = 6; - private static final int DEFAULT_COLOR_THEME = Color.parseColor("#ff00ee00"); - // 3 attributes - private int color_theme; - private boolean isOpen; - private int shape; - // varials of drawing - private Paint paint; - private Rect backRect; - private Rect frontRect; - private RectF frontCircleRect; - private RectF backCircleRect; - private int alpha; - private int max_left; - private int min_left; - private int frontRect_left; - private int frontRect_left_begin = RIM_SIZE; - private int eventStartX; - private int eventLastX; - private int diffX = 0; - private boolean slideable = true; - private SlideListener listener; + private static final float OFFSET_NOT_SET = -1; + private static final float DEFAULT_OFFSET_PERCENT = 0.55f; + public static final int SHAPE_RECT = 1; + public static final int SHAPE_CIRCLE = 2; + private static final int RIM_SIZE = 6; + private static final int DEFAULT_COLOR_THEME = Color.parseColor("#ff00ee00"); + // 4 attributes + private int color_theme; + private boolean isOpen; + private int shape; + private float offsetPercent; + // varials of drawing + private Paint paint; + private Rect backRect; + private Rect frontRect; + private RectF frontCircleRect; + private RectF backCircleRect; + private int alpha; + private int max_left; + private int min_left; + private int frontRect_left; + private int frontRect_left_begin = RIM_SIZE; + private int eventStartX; + private int eventLastX; + private int diffX = 0; + private boolean slideable = true; + private SlideListener listener; - public interface SlideListener { - public void open(); + public interface SlideListener { + void open(); + void close(); + } - public void close(); - } + public SlideSwitch(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + listener = null; + paint = new Paint(); + paint.setAntiAlias(true); + TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.slideswitch); + color_theme = a.getColor(R.styleable.slideswitch_themeColor, + DEFAULT_COLOR_THEME); + isOpen = a.getBoolean(R.styleable.slideswitch_isOpen, false); + shape = a.getInt(R.styleable.slideswitch_shape, SHAPE_RECT); + offsetPercent = a.getFloat(R.styleable.slideswitch_offsetPercent, OFFSET_NOT_SET); + a.recycle(); - public SlideSwitch(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - listener = null; - paint = new Paint(); - paint.setAntiAlias(true); - TypedArray a = context.obtainStyledAttributes(attrs, - R.styleable.slideswitch); - color_theme = a.getColor(R.styleable.slideswitch_themeColor, - DEFAULT_COLOR_THEME); - isOpen = a.getBoolean(R.styleable.slideswitch_isOpen, false); - shape = a.getInt(R.styleable.slideswitch_shape, SHAPE_RECT); - a.recycle(); - } + if(offsetPercent == 0 || offsetPercent >= 1){ + offsetPercent = DEFAULT_OFFSET_PERCENT; + } - public SlideSwitch(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } + } - public SlideSwitch(Context context) { - this(context, null); - } + public SlideSwitch(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - int width = measureDimension(280, widthMeasureSpec); - int height = measureDimension(140, heightMeasureSpec); - if (shape == SHAPE_CIRCLE) { - if (width < height) - width = height * 2; - } - setMeasuredDimension(width, height); - initDrawingVal(); - } + public SlideSwitch(Context context) { + this(context, null); + } - public void initDrawingVal() { - int width = getMeasuredWidth(); - int height = getMeasuredHeight(); + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + int width = measureDimension(280, widthMeasureSpec); + int height = measureDimension(140, heightMeasureSpec); + if (shape == SHAPE_CIRCLE) { + if (width < height) + width = height * 2; + } + setMeasuredDimension(width, height); + initDrawingVal(); + } - backCircleRect = new RectF(); - frontCircleRect = new RectF(); - frontRect = new Rect(); - backRect = new Rect(0, 0, width, height); - min_left = RIM_SIZE; - if (shape == SHAPE_RECT) - max_left = width / 2; - else - max_left = width - (height - 2 * RIM_SIZE) - RIM_SIZE; - if (isOpen) { - frontRect_left = max_left; - alpha = 255; - } else { - frontRect_left = RIM_SIZE; - alpha = 0; - } - frontRect_left_begin = frontRect_left; - } + public void initDrawingVal() { + int width = getMeasuredWidth(); + int height = getMeasuredHeight(); - public int measureDimension(int defaultSize, int measureSpec) { - int result; - int specMode = MeasureSpec.getMode(measureSpec); - int specSize = MeasureSpec.getSize(measureSpec); - if (specMode == MeasureSpec.EXACTLY) { - result = specSize; - } else { - result = defaultSize; // UNSPECIFIED - if (specMode == MeasureSpec.AT_MOST) { - result = Math.min(result, specSize); - } - } - return result; - } + backCircleRect = new RectF(); + frontCircleRect = new RectF(); + frontRect = new Rect(); + backRect = new Rect(0, 0, width, height); + min_left = RIM_SIZE; + if(offsetPercent == OFFSET_NOT_SET) { + if (shape == SHAPE_RECT) { + max_left = width / 2; + } else { + max_left = width - (height - 2 * RIM_SIZE) - RIM_SIZE; + } + }else { + max_left = width - Math.round(width * offsetPercent); + } + if (isOpen) { + frontRect_left = max_left; + alpha = 255; + } else { + frontRect_left = RIM_SIZE; + alpha = 0; + } + frontRect_left_begin = frontRect_left; + } - @Override - protected void onDraw(Canvas canvas) { - if (shape == SHAPE_RECT) { - paint.setColor(Color.GRAY); - canvas.drawRect(backRect, paint); - paint.setColor(color_theme); - paint.setAlpha(alpha); - canvas.drawRect(backRect, paint); - frontRect.set(frontRect_left, RIM_SIZE, frontRect_left - + getMeasuredWidth() / 2 - RIM_SIZE, getMeasuredHeight() - - RIM_SIZE); - paint.setColor(Color.WHITE); - canvas.drawRect(frontRect, paint); - } else { - // draw circle - int radius; - radius = backRect.height() / 2 - RIM_SIZE; - paint.setColor(Color.GRAY); - backCircleRect.set(backRect); - canvas.drawRoundRect(backCircleRect, radius, radius, paint); - paint.setColor(color_theme); - paint.setAlpha(alpha); - canvas.drawRoundRect(backCircleRect, radius, radius, paint); - frontRect.set(frontRect_left, RIM_SIZE, frontRect_left - + backRect.height() - 2 * RIM_SIZE, backRect.height() - - RIM_SIZE); - frontCircleRect.set(frontRect); - paint.setColor(Color.WHITE); - canvas.drawRoundRect(frontCircleRect, radius, radius, paint); - } - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (slideable == false) - return super.onTouchEvent(event); - int action = MotionEventCompat.getActionMasked(event); - switch (action) { - case MotionEvent.ACTION_DOWN: - eventStartX = (int) event.getRawX(); - break; - case MotionEvent.ACTION_MOVE: - eventLastX = (int) event.getRawX(); - diffX = eventLastX - eventStartX; - int tempX = diffX + frontRect_left_begin; - tempX = (tempX > max_left ? max_left : tempX); - tempX = (tempX < min_left ? min_left : tempX); - if (tempX >= min_left && tempX <= max_left) { - frontRect_left = tempX; - alpha = (int) (255 * (float) tempX / (float) max_left); - invalidateView(); - } - break; - case MotionEvent.ACTION_UP: - int wholeX = (int) (event.getRawX() - eventStartX); - frontRect_left_begin = frontRect_left; - boolean toRight; - toRight = (frontRect_left_begin > max_left / 2 ? true : false); - if (Math.abs(wholeX) < 3) { - toRight = !toRight; - } - moveToDest(toRight); - break; - default: - break; - } - return true; - } + public int measureDimension(int defaultSize, int measureSpec) { + int result; + int specMode = MeasureSpec.getMode(measureSpec); + int specSize = MeasureSpec.getSize(measureSpec); + if (specMode == MeasureSpec.EXACTLY) { + result = specSize; + } else { + result = defaultSize; // UNSPECIFIED + if (specMode == MeasureSpec.AT_MOST) { + result = Math.min(result, specSize); + } + } + return result; + } - /** - * draw again - */ - private void invalidateView() { - if (Looper.getMainLooper() == Looper.myLooper()) { - invalidate(); - } else { - postInvalidate(); - } - } + @Override + protected void onDraw(Canvas canvas) { + if (shape == SHAPE_RECT) { + paint.setColor(Color.GRAY); + canvas.drawRect(backRect, paint); + paint.setColor(color_theme); + paint.setAlpha(alpha); + canvas.drawRect(backRect, paint); + int maxCutoff; + if(offsetPercent == OFFSET_NOT_SET){ + maxCutoff = getMeasuredWidth() / 2; + }else{ + maxCutoff = Math.round(getMeasuredWidth() * offsetPercent); + } + frontRect.set(frontRect_left, RIM_SIZE, frontRect_left + + maxCutoff - RIM_SIZE, getMeasuredHeight() + - RIM_SIZE); + paint.setColor(Color.WHITE); + canvas.drawRect(frontRect, paint); + } else { + // draw circle + int radius; + radius = backRect.height() / 2 - RIM_SIZE; + paint.setColor(Color.GRAY); + backCircleRect.set(backRect); + canvas.drawRoundRect(backCircleRect, radius, radius, paint); + paint.setColor(color_theme); + paint.setAlpha(alpha); + int maxCutoff; + if(offsetPercent == OFFSET_NOT_SET){ + maxCutoff = backRect.height() - 2 * RIM_SIZE; + }else{ + maxCutoff = Math.round(getMeasuredWidth() * offsetPercent) - RIM_SIZE; + } + frontRect.set(frontRect_left, RIM_SIZE, frontRect_left + maxCutoff , backRect.height() + - RIM_SIZE); + frontCircleRect.set(frontRect); + paint.setColor(Color.WHITE); + canvas.drawRoundRect(frontCircleRect, radius, radius, paint); + } + } - public void setSlideListener(SlideListener listener) { - this.listener = listener; - } + @Override + public boolean onTouchEvent(MotionEvent event) { + if (!slideable) + return super.onTouchEvent(event); + int action = MotionEventCompat.getActionMasked(event); + switch (action) { + case MotionEvent.ACTION_DOWN: + eventStartX = (int) event.getRawX(); + break; + case MotionEvent.ACTION_MOVE: + eventLastX = (int) event.getRawX(); + diffX = eventLastX - eventStartX; + int tempX = diffX + frontRect_left_begin; + tempX = (tempX > max_left ? max_left : tempX); + tempX = (tempX < min_left ? min_left : tempX); + if (tempX >= min_left && tempX <= max_left) { + frontRect_left = tempX; + alpha = (int) (255 * (float) tempX / (float) max_left); + invalidateView(); + } + break; + case MotionEvent.ACTION_UP: + int wholeX = (int) (event.getRawX() - eventStartX); + frontRect_left_begin = frontRect_left; + boolean toRight; + toRight = (frontRect_left_begin > max_left / 2); + if (Math.abs(wholeX) < 3) { + toRight = !toRight; + } + moveToDest(toRight); + break; + default: + break; + } + return true; + } - public void moveToDest(final boolean toRight) { - ValueAnimator toDestAnim = ValueAnimator.ofInt(frontRect_left, - toRight ? max_left : min_left); - toDestAnim.setDuration(500); - toDestAnim.setInterpolator(new AccelerateDecelerateInterpolator()); - toDestAnim.start(); - toDestAnim.addUpdateListener(new AnimatorUpdateListener() { + /** + * draw again + */ + private void invalidateView() { + if (Looper.getMainLooper() == Looper.myLooper()) { + invalidate(); + } else { + postInvalidate(); + } + } - @Override - public void onAnimationUpdate(ValueAnimator animation) { - frontRect_left = (Integer) animation.getAnimatedValue(); - alpha = (int) (255 * (float) frontRect_left / (float) max_left); - invalidateView(); - } - }); - toDestAnim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (toRight) { - isOpen = true; - if (listener != null) - listener.open(); - frontRect_left_begin = max_left; - } else { - isOpen = false; - if (listener != null) - listener.close(); - frontRect_left_begin = min_left; - } - } - }); - } + public void setSlideListener(SlideListener listener) { + this.listener = listener; + } - public void setState(boolean isOpen) { - this.isOpen = isOpen; - initDrawingVal(); - invalidateView(); - if (listener != null) - if (isOpen == true) { - listener.open(); - } else { - listener.close(); - } - } + public void moveToDest(final boolean toRight) { + ValueAnimator toDestAnim = ValueAnimator.ofInt(frontRect_left, + toRight ? max_left : min_left); + toDestAnim.setDuration(500); + toDestAnim.setInterpolator(new AccelerateDecelerateInterpolator()); + toDestAnim.start(); + toDestAnim.addUpdateListener(new AnimatorUpdateListener() { - public void setShapeType(int shapeType) { - this.shape = shapeType; - } + @Override + public void onAnimationUpdate(ValueAnimator animation) { + frontRect_left = (Integer) animation.getAnimatedValue(); + alpha = (int) (255 * (float) frontRect_left / (float) max_left); + invalidateView(); + } + }); + toDestAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (toRight) { + isOpen = true; + if (listener != null) + listener.open(); + frontRect_left_begin = max_left; + } else { + isOpen = false; + if (listener != null) + listener.close(); + frontRect_left_begin = min_left; + } + } + }); + } - public void setSlideable(boolean slideable) { - this.slideable = slideable; - } + public void setState(boolean isOpen) { + this.isOpen = isOpen; + initDrawingVal(); + invalidateView(); + if (listener != null) + if (isOpen) { + listener.open(); + } else { + listener.close(); + } + } - @Override - protected void onRestoreInstanceState(Parcelable state) { - if (state instanceof Bundle) { - Bundle bundle = (Bundle) state; - this.isOpen = bundle.getBoolean("isOpen"); - state = bundle.getParcelable("instanceState"); - } - super.onRestoreInstanceState(state); - } + public void setShapeType(int shapeType) { + this.shape = shapeType; + } - @Override - protected Parcelable onSaveInstanceState() { - Bundle bundle = new Bundle(); - bundle.putParcelable("instanceState", super.onSaveInstanceState()); - bundle.putBoolean("isOpen", this.isOpen); - return bundle; - } -} + public void setSlideable(boolean slideable) { + this.slideable = slideable; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + if (state instanceof Bundle) { + Bundle bundle = (Bundle) state; + this.isOpen = bundle.getBoolean("isOpen"); + state = bundle.getParcelable("instanceState"); + } + super.onRestoreInstanceState(state); + } + + @Override + protected Parcelable onSaveInstanceState() { + Bundle bundle = new Bundle(); + bundle.putParcelable("instanceState", super.onSaveInstanceState()); + bundle.putBoolean("isOpen", this.isOpen); + return bundle; + } +} \ No newline at end of file