美文网首页
自定义开关按钮控件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

    在讲解自定义SwitchView之前 先讲解一下自定义View的基本步骤 1 .有些自定义需要定义View的属性如...

  • 关于iOS中UIControl的介绍

    UIKit提供了一组控件:UISwitch开关、UIButton按钮、UISegmentedControl分段控件...

  • UIControl

    UIKit提供了一组控件:UISwitch开关、UIButton按钮、UISegmentedControl分段控件...

  • UIKit的控件

    UIKit提供了一组控件:UISwitch开关、UIButton按钮、UISegmentedControl分段控件...

  • Android 自定义实现switch开关按钮

    在开发我们经常会使用到自定义实现控件, 今天呢就跟大家说一说switch开关按钮的自定义实现。 为了大家的方便,直...

  • 如何用按钮来实现图片上文字下的效果?

    自定义按钮,实现 自定义按钮,实现layoutSubViews方法调整按钮内部子控件的位置和尺寸

  • Android 设置界面appSetActivity

    自定义开关按钮:SwitchButton.java values/attrs.xml 开关布局: SetAvtiv...

  • 自定义开关

    阅读原文 自定义开关 自定义一个类继承View 实例化控件 重写onMeasure()测量方法 绘制 开关的点击事...

  • 自制控件1 开关按钮

    本文出自 “阿敏其人” 简书博客,转载或引用请注明出处。 在自定义控件——初识自定义控件里面,我们已经大致介绍三种...

  • Android自定义开关按钮ToggleButton

    简介 由于项目开发需要,所以做了一个自定义的开关按钮,样式类似于Android5.0中开关按钮,也有开启关闭的切换...

网友评论

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

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