美文网首页
自定义开关按钮控件SwitchView

自定义开关按钮控件SwitchView

作者: 程序猿峰岑 | 来源:发表于2018-08-29 09:47 被阅读0次

    在讲解自定义SwitchView之前  先讲解一下自定义View的基本步骤

    1 .有些自定义需要定义View的属性如:背景颜色  字体大小  字体颜色  需要用到typeArray 大概步骤是在value包下创建attrs文件定义 declare-styleable 名称 主要是定义属性的format 主要知道自定义属性对应的format就可以了 dimension 尺寸值 color 颜色  integer整数值   boolean布尔值 reference 资源文件ID float浮点值 string字符串值 fraction百分数 enum 枚举值 flag 位或运算 因为我这里没用用到自定义属性所以就不过多的介绍了 有兴趣的同学可以下来了解具体的使用

    2.onMeasure定义View的大小  这里介绍下MeasureSpec中的三种模式:分别是UNSPECIFIED   AT_MOST    EXACTLY

    1).精确模式MeasureSpec.EXACTLY 这种情况下是确定该控件的具体大小值  如10dp

    2).最大模式 MeasureSpec.AT_MOST 这个也就是父组件,能够给出最大的控件,当前组件的宽高大小只能为这么大,当然也可以比这个小 如wrap_content

    3).未指定模式 MeasureSpec.EXACTLY 当前组件,可以随便使用控件,不受限制 MATCH_PARENT 

    3.onSizeChanged 用以显示当前View的位置和宽高设置 w h 分别表示当前的宽和高 oldw oldh分别表示改变之前的宽和高

    4 onDraw绘制View  定义画布canvas 然后定义paint 最后就是在画布上绘制View等一些逻辑上的操作 

    5.onTouchEvent触摸事件操作 

    讲了这么多 还不如直接上代码自行研究

    public class SwitchViewextends View {

    private final Paintpaint =new Paint();

      private final PathsPath =new Path();

      private final PathbPath =new Path();

      private final RectFbRectF =new RectF();

      private float sAnim, bAnim;

      private RadialGradientshadowGradient;

      private final AccelerateInterpolatoraInterpolator =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 boolean isOpened =false;

      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;

      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:

    return true;

                case MotionEvent.ACTION_UP:

    lastState =state;

                  if (state ==STATE_SWITCH_OFF) {

    refreshState(STATE_SWITCH_OFF2);

                  }else if (state ==STATE_SWITCH_ON) {

    refreshState(STATE_SWITCH_ON2);

                  }

    bAnim =1;

                  invalidate();

                  if (state ==STATE_SWITCH_OFF2) {

    listener.toggleToOn(this);

                  }else if (state ==STATE_SWITCH_ON2) {

    listener.toggleToOff(this);

                  }

    break;

            }

    }

    return super.onTouchEvent(event);

      }

    private void refreshState(int newState) {

    if (!isOpened && newState ==STATE_SWITCH_ON) {

    isOpened =true;

          }else if (isOpened && newState ==STATE_SWITCH_OFF) {

    isOpened =false;

          }

    lastState =state;

          state = newState;

          postInvalidate();

      }

    /**

        * @return the state of switch view

    */

      public boolean isOpened() {

    return isOpened;

      }

    /**

    * if set true , the state change to on;

    * if set false, the state change to off

    *

        * @param isOpened

        */

      public void setOpened(boolean isOpened) {

    refreshState(isOpened ?STATE_SWITCH_ON :STATE_SWITCH_OFF);

      }

    /**

    * if set true , the state change to on;

    * if set false, the state change to off

        *
    change state with animation

        *

        * @param isOpened

        */

      public void toggleSwitch(final boolean isOpened) {

    this.isOpened = isOpened;

          postDelayed(new Runnable() {

    @Override

            public void run() {

    toggleSwitch(isOpened ?STATE_SWITCH_ON :STATE_SWITCH_OFF);

            }

    }, 300);

      }

    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(View view);

          void toggleToOff(View view);

      }

    private OnStateChangedListenerlistener =new OnStateChangedListener() {

    @Override

          public void toggleToOn(View view) {

    toggleSwitch(STATE_SWITCH_ON);

          }

    @Override

          public void toggleToOff(View view) {

    toggleSwitch(STATE_SWITCH_OFF);

          }

    };

      public void setOnStateChangedListener(OnStateChangedListener listener) {

    if (listener ==null)throw new IllegalArgumentException("empty listener");

          this.listener = listener;

      }

    @Override

      public ParcelableonSaveInstanceState() {

    Parcelable superState =super.onSaveInstanceState();

          SavedState ss =new SavedState(superState);

          ss.isOpened =isOpened;

          return ss;

      }

    @Override

      public void onRestoreInstanceState(Parcelable state) {

    SavedState ss = (SavedState) state;

          super.onRestoreInstanceState(ss.getSuperState());

          this.isOpened = ss.isOpened;

          this.state =this.isOpened ?STATE_SWITCH_ON :STATE_SWITCH_OFF;

      }

    static final class SavedStateextends BaseSavedState {

    private boolean isOpened;

          SavedState(Parcelable superState) {

    super(superState);

          }

    private SavedState(Parcel in) {

    super(in);

            isOpened =1 == in.readInt();

          }

    @Override

          public void writeToParcel(Parcel out, int flags) {

    super.writeToParcel(out, flags);

            out.writeInt(isOpened ?1 :0);

          }

    }

    }

    相关文章

      网友评论

          本文标题:自定义开关按钮控件SwitchView

          本文链接:https://www.haomeiwen.com/subject/kehtwftx.html