Android SwitchButton 完善版(在前人基础上完善)

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.RadialGradient;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateInterpolator;

public class SwitchView extends View{

   private final Paint paint = new Paint();
   private final Path sPath = new Path();
   private final Path bPath = new Path();
   private final RectF bRectF = new RectF();
   private float sAnim, bAnim;
   private RadialGradient shadowGradient;
   private final AccelerateInterpolator aInterpolator = new AccelerateInterpolator(2);

   /**
    * state switch on
    */
   public static final int STATE_SWITCH_ON = 4;
   /**
    * state prepare to off
    */
   public static final int STATE_SWITCH_ON2 = 3;
   /**
    * state prepare to on
    */
   public static final int STATE_SWITCH_OFF2 = 2;
   /**
    * state prepare to off
    */
   public static final int STATE_SWITCH_OFF = 1;
   /**
    * current state
    */
   private int state = STATE_SWITCH_OFF;
   /**
    * last state
    */
   private int lastState = state;

   private int mWidth, mHeight;
   private float sWidth, sHeight;
   private float sLeft, sTop, sRight, sBottom;
   private float sCenterX, sCenterY;
   private float sScale;

   private float bOffset;
   private float bRadius, bStrokeWidth;
   private float bWidth;
   private float bLeft, bTop, bRight, bBottom;
   private float bOnLeftX, bOn2LeftX, bOff2LeftX, bOffLeftX;

   private float shadowHeight;
   int  xFirst=0;
   public SwitchView(Context context) {
      this(context, null);
   }

   public SwitchView(Context context, AttributeSet attrs) {
      super(context, attrs);
      setLayerType(LAYER_TYPE_SOFTWARE, null);
   }

   @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
      int widthSize = MeasureSpec.getSize(widthMeasureSpec);
      int heightSize = (int) (widthSize * 0.65f);
      setMeasuredDimension(widthSize, heightSize);
   }

   @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {
      super.onSizeChanged(w, h, oldw, oldh);
      mWidth = w;
      mHeight = h;

      sLeft = sTop = 0;
      sRight = mWidth;
      sBottom = mHeight * 0.91f;
      sWidth = sRight - sLeft;
      sHeight = sBottom - sTop;
      sCenterX = (sRight + sLeft) / 2;
      sCenterY = (sBottom + sTop) / 2;

      shadowHeight = mHeight - sBottom;

      bLeft = bTop = 0;
      bRight = bBottom = sBottom;
      bWidth = bRight - bLeft;
      final float halfHeightOfS = (sBottom - sTop) / 2;
      bRadius = halfHeightOfS * 0.95f;
      bOffset = bRadius * 0.2f;
      bStrokeWidth = (halfHeightOfS - bRadius) * 2;

      bOnLeftX = sWidth - bWidth;
      bOn2LeftX = bOnLeftX - bOffset;
      bOffLeftX = 0;
      bOff2LeftX = 0;

      sScale = 1 - bStrokeWidth / sHeight;

      RectF sRectF = new RectF(sLeft, sTop, sBottom, sBottom);
      sPath.arcTo(sRectF, 90, 180);
      sRectF.left = sRight - sBottom;
      sRectF.right = sRight;
      sPath.arcTo(sRectF, 270, 180);
      sPath.close();

      bRectF.left = bLeft;
      bRectF.right = bRight;
      bRectF.top = bTop + bStrokeWidth / 2;
      bRectF.bottom = bBottom - bStrokeWidth / 2;

      shadowGradient = new RadialGradient(bWidth / 2, bWidth / 2, bWidth / 2, 0xff000000, 0x00000000, Shader.TileMode.CLAMP);
   }

   private void calcBPath(float percent) {
      bPath.reset();
      bRectF.left = bLeft + bStrokeWidth / 2;
      bRectF.right = bRight - bStrokeWidth / 2;
      bPath.arcTo(bRectF, 90, 180);
      bRectF.left = bLeft + percent * bOffset + bStrokeWidth / 2;
      bRectF.right = bRight + percent * bOffset - bStrokeWidth / 2;
      bPath.arcTo(bRectF, 270, 180);
      bPath.close();
   }

   private float calcBTranslate(float percent) {
      float result = 0;
      int wich = state - lastState;
      switch (wich) {
         case 1:
            // off -> off2
            if (state == STATE_SWITCH_OFF2) {
               result = bOff2LeftX - (bOff2LeftX - bOffLeftX) * percent;
            }
            // on2 -> on
            else if (state == STATE_SWITCH_ON) {
               result = bOnLeftX - (bOnLeftX - bOn2LeftX) * percent;
            }
            break;
         case 2:
            // off2 -> on
            if (state == STATE_SWITCH_ON) {
               result = bOnLeftX - (bOnLeftX - bOff2LeftX) * percent;
            }
            // off -> on2
            else if (state == STATE_SWITCH_ON) {
               result = bOn2LeftX - (bOn2LeftX - bOffLeftX) * percent;
            }
            break;
         case 3: // off -> on
            result = bOnLeftX - (bOnLeftX - bOffLeftX) * percent;
            break;
         case -1:
            // on -> on2
            if (state == STATE_SWITCH_ON2) {
               result = bOn2LeftX + (bOnLeftX - bOn2LeftX) * percent;
            }
            // off2 -> off
            else if (state == STATE_SWITCH_OFF) {
               result = bOffLeftX + (bOff2LeftX - bOffLeftX) * percent;
            }
            break;
         case -2:
            // on2 -> off
            if (state == STATE_SWITCH_OFF) {
               result = bOffLeftX + (bOn2LeftX - bOffLeftX) * percent;
            }
            // on -> off2
            else if (state == STATE_SWITCH_OFF2) {
               result = bOff2LeftX + (bOnLeftX - bOff2LeftX) * percent;
            }
            break;
         case -3: // on -> off
            result = bOffLeftX + (bOnLeftX - bOffLeftX) * percent;
            break;
      }

      return result - bOffLeftX;
   }

   @Override protected void onDraw(Canvas canvas) {
      super.onDraw(canvas);
      paint.setAntiAlias(true);
      final boolean isOn = (state == STATE_SWITCH_ON || state == STATE_SWITCH_ON2);
      // draw background
      paint.setStyle(Style.FILL);
      paint.setColor(isOn ? 0xff4bd763 : 0xffe3e3e3);
      canvas.drawPath(sPath, paint);

      sAnim = sAnim - 0.1f > 0 ? sAnim - 0.1f : 0;
      bAnim = bAnim - 0.1f > 0 ? bAnim - 0.1f : 0;

      final float dsAnim = aInterpolator.getInterpolation(sAnim);
      final float dbAnim = aInterpolator.getInterpolation(bAnim);
      // draw background animation
      final float scale = sScale * (isOn ? dsAnim : 1 - dsAnim);
      final float scaleOffset = (bOnLeftX + bRadius - sCenterX) * (isOn ? 1 - dsAnim : dsAnim);
      canvas.save();
      canvas.scale(scale, scale, sCenterX + scaleOffset, sCenterY);
      paint.setColor(0xffffffff);
      canvas.drawPath(sPath, paint);
      canvas.restore();
      // draw center bar
      canvas.save();
      canvas.translate(calcBTranslate(dbAnim), shadowHeight);
      final boolean isState2 = (state == STATE_SWITCH_ON2 || state == STATE_SWITCH_OFF2);
      calcBPath(isState2 ? 1 - dbAnim : dbAnim);
      // draw shadow
      paint.setStyle(Style.FILL);
      paint.setColor(0xff333333);
      paint.setShader(shadowGradient);
      canvas.drawPath(bPath, paint);
      paint.setShader(null);
      canvas.translate(0, -shadowHeight);

      canvas.scale(0.98f, 0.98f, bWidth / 2, bWidth / 2);
      paint.setStyle(Style.FILL);
      paint.setColor(0xffffffff);
      canvas.drawPath(bPath, paint);

      paint.setStyle(Style.STROKE);
      paint.setStrokeWidth(bStrokeWidth * 0.5f);

      paint.setColor(isOn ? 0xff4ada60 : 0xffbfbfbf);
      canvas.drawPath(bPath, paint);

      canvas.restore();

      paint.reset();
      if (sAnim > 0 || bAnim > 0) invalidate();
   }

   @Override public boolean onTouchEvent(MotionEvent event) {

      if ((state == STATE_SWITCH_ON || state == STATE_SWITCH_OFF) && (sAnim * bAnim == 0)) {
         switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
               xFirst =(int)event.getRawX();
               return true;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
               int  xSecond=(int)event.getRawX();
               int  distance=xSecond-xFirst;
               lastState = state;
                   if(distance==0) {
                  if (state == STATE_SWITCH_OFF) {
                     refreshState(STATE_SWITCH_OFF2);
                     actionSwitch();
                  } else if (state == STATE_SWITCH_ON) {
                     refreshState(STATE_SWITCH_ON2);
                     actionSwitch();
                  }
               }
               //????
               if(distance>0){
                  if (state == STATE_SWITCH_OFF) {
                     refreshState(STATE_SWITCH_OFF2);
                     actionSwitch();
                  }
               }
                    //????
               if(distance<0){
                   if (state == STATE_SWITCH_ON) {
                     refreshState(STATE_SWITCH_ON2);
                      actionSwitch();
                  }
               }
               break;
         }
      }
      return  super.onTouchEvent(event);
   }

private void actionSwitch(){
   if (listener != null) {
      if (state == STATE_SWITCH_OFF2 ) {
         listener.toggleToOn();
      }
      else if (state == STATE_SWITCH_ON2) {
         listener.toggleToOff();
      }
   }
}

   private void refreshState(int newState) {
      lastState = state;
      state = newState;
      postInvalidate();


      bAnim = 1;
      invalidate();
   }

   /**
    *
    * @return the state of switch view
    */
   public int getState() {
      return state;
   }

   /**
    * if set true , the state change to on;
    * if set false, the state change to off
    * @param isOn
    */
   public void setState(boolean isOn) {
      final int wich = isOn ? STATE_SWITCH_ON : STATE_SWITCH_OFF;
      if(state!=wich) {
         refreshState(wich);
      }
   }

   /**
    * if set true , the state change to on;
    * if set false, the state change to off
    * 
change state with animation * @param letItOn */ public void toggleSwitch(boolean letItOn) { final int wich = letItOn ? STATE_SWITCH_ON : STATE_SWITCH_OFF; postDelayed(new Runnable() { @Override public void run() { toggleSwitch(wich); } }, 100); } private synchronized void toggleSwitch(int wich) { if (wich == STATE_SWITCH_ON || wich == STATE_SWITCH_OFF) { if ((wich == STATE_SWITCH_ON && (lastState == STATE_SWITCH_OFF || lastState == STATE_SWITCH_OFF2)) || (wich == STATE_SWITCH_OFF && (lastState == STATE_SWITCH_ON || lastState == STATE_SWITCH_ON2))) { sAnim = 1; } bAnim = 1; refreshState(wich); } } public interface OnStateChangedListener { void toggleToOn(); void toggleToOff(); } private OnStateChangedListener listener = new OnStateChangedListener() { @Override public void toggleToOn() { toggleSwitch(STATE_SWITCH_ON); } @Override public void toggleToOff() { toggleSwitch(STATE_SWITCH_OFF); } }; public void setOnStateChangedListener(OnStateChangedListener listener) { if (listener == null) throw new IllegalArgumentException("empty listener"); this.listener = listener; } }

你可能感兴趣的:(Android)