com.test.slidebutton.SlideButton.java Source code

Java tutorial

Introduction

Here is the source code for com.test.slidebutton.SlideButton.java

Source

/*
 * Copyright (C) 2015 Quinn Chen
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.test.slidebutton;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Bundle;
import android.os.Looper;
import android.os.Parcelable;
import android.support.v4.view.MotionEventCompat;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.OvershootInterpolator;

/**
 * 
 * @author zhangshuo
 */
public class SlideButton extends View {

    public static final int SHAPE_RECT = 1;
    public static final int SHAPE_RECT_CIRCLE = 2;
    public static final int SHAPE_CIRCLE = 3;

    private static final int DEFAULT_COLOR_OPENED = Color.parseColor("#ff00ee00");
    private static final int DEFAULT_COLOR_CLOSED = Color.parseColor("#ffeeeeee");
    private static final int DEFAULT_COLOR_BALL = Color.parseColor("#ffffffff");
    private static final int RIM_SIZE = 6;

    /***/
    private int DEFAULT_WIDTH = 64;
    /***/
    private int DEFAULT_HEIGHT = 32;

    private Context mContext;

    private int colorOpend = DEFAULT_COLOR_OPENED;
    private int colorClosed = DEFAULT_COLOR_CLOSED;
    private int colorSlider = DEFAULT_COLOR_BALL;
    private boolean slidable = true;
    private boolean isOpen = false;
    private int shape = SHAPE_RECT;

    private Paint paint;
    private RectF rectfSlider;
    private RectF rectfBack;
    private int alpha;
    private int max_left;
    private int min_left;
    /**left*/
    private int sliderCurrentLeft;
    /**()*/
    private int silderWidth;
    /***/
    private int sliderArcRadius;
    /***/
    private float backArcRadius;
    /**leftX*/
    private int sliderMoveStartLeft = RIM_SIZE;
    /***/
    private int backCurrentColor;

    private int startX;
    private int startY;
    private int lastX;
    private int lastY;
    private int offsetX = 0;

    /**move*/
    private int touchSlop;

    /**
     * 
     */
    private boolean canMove = false;

    private SlideListener listener;

    /**
     * 
     * @author zhangshuo
     */
    public interface SlideListener {
        public void open();

        public void close();
    }

    public SlideButton(Context context) {
        this(context, null);
    }

    public SlideButton(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SlideButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        this.touchSlop = ViewConfiguration.get(this.mContext).getScaledTouchSlop();
        this.DEFAULT_WIDTH = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this.DEFAULT_WIDTH,
                getResources().getDisplayMetrics());
        this.DEFAULT_HEIGHT = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this.DEFAULT_HEIGHT,
                getResources().getDisplayMetrics());
        listener = null;
        paint = new Paint();
        paint.setAntiAlias(true);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.slide_button);
        initStyle(a);
        a.recycle();
    }

    /**
     * 
     * @param typedArray
     */
    private void initStyle(TypedArray typedArray) {
        if (typedArray.getIndexCount() > 0) {
            int typedCount = typedArray.getIndexCount();
            int typed = -1;
            for (int i = 0; i < typedCount; i++) {
                typed = typedArray.getIndex(i);
                if (R.styleable.slide_button_slideButtonStyle == typed) {
                    int resId = typedArray.getResourceId(typed, 0);
                    TypedArray a1 = mContext.obtainStyledAttributes(resId, R.styleable.slide_button);
                    initStyle(a1);
                    a1.recycle();
                } else if (R.styleable.slide_button_colorOpened == typed) {
                    colorOpend = typedArray.getColor(typed, DEFAULT_COLOR_OPENED);
                } else if (R.styleable.slide_button_colorClosed == typed) {
                    colorClosed = typedArray.getColor(typed, DEFAULT_COLOR_CLOSED);
                } else if (R.styleable.slide_button_colorBall == typed) {
                    colorSlider = typedArray.getColor(typed, DEFAULT_COLOR_BALL);
                } else if (R.styleable.slide_button_slidable == typed) {
                    slidable = typedArray.getBoolean(typed, true);
                } else if (R.styleable.slide_button_isOpen == typed) {
                    isOpen = typedArray.getBoolean(typed, false);
                } else if (R.styleable.slide_button_shape == typed) {
                    shape = typedArray.getInt(typed, SHAPE_RECT);
                }
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = measureDimension(DEFAULT_WIDTH, widthMeasureSpec);
        int height = measureDimension(DEFAULT_HEIGHT, heightMeasureSpec);
        if (width < height) {
            width = height * 2;
        }
        setMeasuredDimension(width, height);
        initDrawingVal();
    }

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

    public void initDrawingVal() {
        int width = getMeasuredWidth();
        int height = getMeasuredHeight();

        rectfBack = new RectF(0, 0, width, height);
        rectfSlider = new RectF();

        silderWidth = height - 2 * RIM_SIZE;

        if (shape == SHAPE_RECT_CIRCLE) {
            backArcRadius = height / 5;
            sliderArcRadius = silderWidth / 5;
        } else if (shape == SHAPE_CIRCLE) {
            backArcRadius = height / 2;
            sliderArcRadius = silderWidth / 2;
        } else {
            backArcRadius = 0;
            sliderArcRadius = 0;
        }

        min_left = RIM_SIZE;

        max_left = width - RIM_SIZE - silderWidth;

        if (isOpen) {
            sliderCurrentLeft = max_left;
            alpha = 255;
        } else {
            sliderCurrentLeft = RIM_SIZE;
            alpha = 0;
        }
        sliderMoveStartLeft = sliderCurrentLeft;
    }

    @Override
    protected void onDraw(Canvas canvas) {

        paint.setColor(backCurrentColor);
        canvas.drawRoundRect(rectfBack, backArcRadius, backArcRadius, paint);

        rectfSlider.set(sliderCurrentLeft, RIM_SIZE, sliderCurrentLeft + silderWidth, RIM_SIZE + silderWidth);
        paint.setColor(colorSlider);
        canvas.drawRoundRect(rectfSlider, sliderArcRadius, sliderArcRadius, paint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (slidable == false)
            return super.onTouchEvent(event);

        int action = MotionEventCompat.getActionMasked(event);
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            startX = (int) event.getRawX();
            startY = (int) event.getRawY();
            break;
        case MotionEvent.ACTION_MOVE:
            lastX = (int) event.getRawX();
            lastY = (int) event.getRawY();

            offsetX = lastX - startX;

            if (!this.canMove) {
                if (Math.abs(offsetX) > touchSlop && Math.abs(offsetX) > Math.abs(lastY - startY)) {
                    this.canMove = true;
                    return true;
                } else {
                    return super.onTouchEvent(event);
                }
            } else {
                sliderCurrentLeft = offsetX + sliderMoveStartLeft;
                if (sliderCurrentLeft > max_left) {
                    sliderCurrentLeft = max_left;
                } else if (sliderCurrentLeft < min_left) {
                    sliderCurrentLeft = min_left;
                }
                alpha = (int) (255 * ((sliderCurrentLeft - min_left) / (float) (max_left - min_left)));
                caculateColor();
                invalidateView();
            }
            break;
        case MotionEvent.ACTION_UP:
            lastX = (int) event.getRawX();
            boolean toRight = (sliderCurrentLeft > max_left / 2 ? true : false);
            if (this.canMove) {
                this.canMove = false;
            } else {//
                toRight = !toRight;
            }
            moveTo(toRight);
            break;
        default:
            break;
        }
        return true;
    }

    /**
     * View
     */
    private void invalidateView() {
        if (Looper.getMainLooper() == Looper.myLooper()) {
            invalidate();
        } else {
            postInvalidate();
        }
    }

    /**
     * 
     * @param listener
     */
    public void setSlideListener(SlideListener listener) {
        this.listener = listener;
    }

    /**
     * max_leftmin_left(moveToboolean toRight)
     * @param toRight
     */
    @Deprecated
    private void moveToDest(final boolean toRight) {
        ValueAnimator toDestAnim = ValueAnimator.ofInt(sliderCurrentLeft, toRight ? max_left : min_left);
        toDestAnim.setDuration(200);
        toDestAnim.setInterpolator(new AccelerateDecelerateInterpolator());
        //      toDestAnim.setInterpolator(new OvershootInterpolator());
        toDestAnim.start();
        toDestAnim.addUpdateListener(new AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                sliderCurrentLeft = (Integer) animation.getAnimatedValue();
                alpha = (int) (255 * (float) sliderCurrentLeft / (float) max_left);
                if (alpha < 0)
                    alpha = 0;
                if (alpha > 255)
                    alpha = 255;
                caculateColor();
                invalidateView();
            }
        });
        toDestAnim.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                if (toRight) {
                    isOpen = true;
                    if (listener != null) {
                        listener.open();
                    }
                    sliderCurrentLeft = sliderMoveStartLeft = max_left;
                    alpha = 255;
                } else {
                    isOpen = false;
                    if (listener != null) {
                        listener.close();
                    }
                    sliderCurrentLeft = sliderMoveStartLeft = min_left;
                    alpha = 0;
                }
                invalidateView();
            }
        });
    }

    /**
     * max_leftmin_left
     * @param toRight
     */
    private void moveTo(final boolean toRight) {
        ObjectAnimator anim = ObjectAnimator.ofInt(this, "sliderCurrentLeft", sliderCurrentLeft,
                toRight ? max_left : min_left);
        anim.setInterpolator(new OvershootInterpolator());
        anim.setDuration(200);
        anim.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                if (toRight) {
                    isOpen = true;
                    if (listener != null) {
                        listener.open();
                    }
                    sliderCurrentLeft = sliderMoveStartLeft = max_left;
                } else {
                    isOpen = false;
                    if (listener != null) {
                        listener.close();
                    }
                    sliderCurrentLeft = sliderMoveStartLeft = min_left;
                }
                invalidateView();
            }
        });
        anim.start();
    }

    /**
     * 
     * @return
     */
    public int getSliderCurrentLeft() {
        return sliderCurrentLeft;
    }

    /**
     * 
     * @param sliderCurrentLeft
     */
    public void setSliderCurrentLeft(int sliderCurrentLeft) {
        this.sliderCurrentLeft = sliderCurrentLeft;
        caculateColor();
        invalidateView();
    }

    /**
     * 
     */
    private void caculateColor() {
        float per = (sliderCurrentLeft - min_left) / (float) (max_left - min_left);
        String StartColor = "#" + Integer.toHexString(colorClosed);
        String endColor = "#" + Integer.toHexString(colorOpend);

        int startAlpha = Integer.parseInt(StartColor.substring(1, 3), 16);
        int startRed = Integer.parseInt(StartColor.substring(3, 5), 16);
        int startGreen = Integer.parseInt(StartColor.substring(5, 7), 16);
        int startBlue = Integer.parseInt(StartColor.substring(7), 16);

        int endAlpha = Integer.parseInt(endColor.substring(1, 3), 16);
        int endRed = Integer.parseInt(endColor.substring(3, 5), 16);
        int endGreen = Integer.parseInt(endColor.substring(5, 7), 16);
        int endBlue = Integer.parseInt(endColor.substring(7), 16);

        int currentAlpha = (int) ((endAlpha - startAlpha) * per + startAlpha);
        int currentRed = (int) ((endRed - startRed) * per + startRed);
        int currentGreen = (int) ((endGreen - startGreen) * per + startGreen);
        int currentBlue = (int) ((endBlue - startBlue) * per + startBlue);

        backCurrentColor = Color.parseColor("#" + getHexString(currentAlpha) + getHexString(currentRed)
                + getHexString(currentGreen) + getHexString(currentBlue));

    }

    /** 
     * 1016 
     */
    private String getHexString(int value) {
        String hexString = Integer.toHexString(value);
        if (hexString.length() == 1) {
            hexString = "0" + hexString;
        }
        return hexString;
    }

    /**
     * 
     * @return
     */
    public boolean isOpen() {
        return isOpen;
    }

    /**
     * 
     * @param isOpen
     */
    public void setOpen(boolean isOpen) {
        this.isOpen = isOpen;
        if (this.isOpen) {
            moveTo(true);
        } else {
            moveTo(false);
        }
    }

    /**
     * 
     * @return
     */
    public int getShape() {
        return shape;
    }

    /**
     * 
     * @param shape
     */
    public void setShape(int shape) {
        this.shape = shape;
    }

    /**
     * 
     * @return
     */
    public boolean isSlidable() {
        return slidable;
    }

    /**
     * 
     * @param slidable
     */
    public void setSlidable(boolean slidable) {
        this.slidable = slidable;
    }

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