Android 自定义带动画效果的开关按钮

转自:http://blog.csdn.net/qq_22393017/article/details/52198453

自定义带动画效果的开关按钮

        一个自定义控制开关的控件,简单实用,效果特别棒。项目中常会用到,比那种用两张图片做按钮背景,selector选择的方式,视觉效果好很多。忘了是以前从哪找的了,本着开源的精神整理了一下分享出来。好了,废话不多说,先展示效果图:

  

Github 项目地址:https://github.com/xiaosong520/SwitchButtonDemo.git

步骤:

1.创建SwitchButton类添加到自己的项目中:
[java]  view plain  copy
 print ?
  1. /** 
  2.  * @TODO<自定义选择开关按钮> 
  3.  * @author 小嵩 
  4.  * @date 2016-8-5 09:57:29 
  5.  */  
  6. public class SwitchButton extends View{  
  7.     /** 圆角大小*/  
  8.     private float radius;  
  9.     /** 开启颜色*/  
  10.     private int onColor = Color.parseColor("#4ebb7f");  
  11.     /** 关闭颜色*/  
  12.     private int offBorderColor = Color.parseColor("#dadbda");  
  13.     /** 灰色带颜色*/  
  14.     private int offColor = Color.parseColor("#ffffff");  
  15.     /** 手柄颜色*/  
  16.     private int spotColor = Color.parseColor("#ffffff");  
  17.     /** 边框颜色*/  
  18.     private int borderColor = offBorderColor;  
  19.     /** 画笔*/  
  20.     private Paint paint ;  
  21.     /** 开关状态*/  
  22.     private boolean toggleOn = false;  
  23.     /** 边框大小*/  
  24.     private int borderWidth = 2;  
  25.     /** 垂直中心*/  
  26.     private float centerY;  
  27.     /** 按钮的开始和结束位置*/  
  28.     private float startX, endX;  
  29.     /** 手柄X位置的最小和最大值*/  
  30.     private float spotMinX, spotMaxX;  
  31.     /**手柄大小 */  
  32.     private int spotSize ;  
  33.     /** 手柄X位置*/  
  34.     private float spotX;  
  35.     /** 关闭时内部灰色带高度*/  
  36.     private float offLineWidth;  
  37.     /** */  
  38.     private RectF rect = new RectF();  
  39.     /** 默认使用动画*/  
  40.     private boolean defaultAnimate = true;  
  41.       
  42.     private OnToggleChanged listener;  
  43.       
  44.     private SwitchButton(Context context) {  
  45.         super(context);  
  46.     }  
  47.     public SwitchButton(Context context, AttributeSet attrs, int defStyleAttr) {  
  48.         super(context, attrs, defStyleAttr);  
  49.         setup(attrs);  
  50.     }  
  51.     public SwitchButton(Context context, AttributeSet attrs) {  
  52.         super(context, attrs);  
  53.         setup(attrs);  
  54.     }  
  55.   
  56.     public void setup(AttributeSet attrs) {  
  57.         paint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  58.         paint.setStyle(Style.FILL);  
  59.         paint.setStrokeCap(Cap.ROUND);  
  60.                   
  61.         this.setOnClickListener(new OnClickListener() {  
  62.             @Override  
  63.             public void onClick(View arg0) {  
  64.                 toggle(defaultAnimate);  
  65.             }  
  66.         });  
  67.           
  68.         TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.SwitchButton);  
  69.         offBorderColor = typedArray.getColor(R.styleable.SwitchButton_offBorderColor, offBorderColor);  
  70.         onColor = typedArray.getColor(R.styleable.SwitchButton_onColor, onColor);  
  71.         spotColor = typedArray.getColor(R.styleable.SwitchButton_spotColor, spotColor);  
  72.         offColor = typedArray.getColor(R.styleable.SwitchButton_offColor, offColor);  
  73.         borderWidth = typedArray.getDimensionPixelSize(R.styleable.SwitchButton_borderWidth, borderWidth);  
  74.         defaultAnimate = typedArray.getBoolean(R.styleable.SwitchButton_animate, defaultAnimate);  
  75.         typedArray.recycle();  
  76.           
  77.         borderColor = offBorderColor;  
  78.     }  
  79.       
  80.     public void toggle() {  
  81.         toggle(true);  
  82.     }  
  83.       
  84.     public void toggle(boolean animate) {  
  85.         toggleOn = !toggleOn;  
  86.         takeEffect(animate);  
  87.           
  88.         if(listener != null){  
  89.             listener.onToggle(toggleOn);  
  90.         }  
  91.     }  
  92.       
  93.     public void toggleOn() {  
  94.         setToggleOn();  
  95.         if(listener != null){  
  96.             listener.onToggle(toggleOn);  
  97.         }  
  98.     }  
  99.       
  100.     public void toggleOff() {  
  101.         setToggleOff();  
  102.         if(listener != null){  
  103.             listener.onToggle(toggleOn);  
  104.         }  
  105.     }  
  106.       
  107.     /** 
  108.      * 设置显示成打开样式,不会触发toggle事件 
  109.      */  
  110.     public void setToggleOn() {  
  111.         setToggleOn(true);  
  112.     }  
  113.       
  114.     /** 
  115.      * @param animate 
  116.      */  
  117.     public void setToggleOn(boolean animate){  
  118.         toggleOn = true;  
  119.         takeEffect(animate);  
  120.     }  
  121.       
  122.     /** 
  123.      * 设置显示成关闭样式,不会触发toggle事件 
  124.      */  
  125.     public void setToggleOff() {  
  126.         setToggleOff(true);  
  127.     }  
  128.       
  129.     public void setToggleOff(boolean animate) {  
  130.         toggleOn = false;  
  131.         takeEffect(animate);  
  132.     }  
  133.       
  134.     private void takeEffect(boolean animate) {  
  135.         if(animate){  
  136.             slide();  
  137.         }else{  
  138.             calculateEffect(toggleOn ? 1 : 0);  
  139.         }  
  140.     }  
  141.       
  142.       
  143.     @Override  
  144.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  145.           
  146.         final int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
  147.         final int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
  148.           
  149.         int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
  150.         int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
  151.           
  152.         Resources r = Resources.getSystem();  
  153.         if(widthMode == MeasureSpec.UNSPECIFIED || widthMode == MeasureSpec.AT_MOST){  
  154.             widthSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, r.getDisplayMetrics());  
  155.             widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);  
  156.         }  
  157.           
  158.         if(heightMode == MeasureSpec.UNSPECIFIED || heightSize == MeasureSpec.AT_MOST){  
  159.             heightSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, r.getDisplayMetrics());  
  160.             heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);  
  161.         }  
  162.           
  163.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  164.     }  
  165.       
  166.       
  167.     @Override  
  168.     protected void onLayout(boolean changed, int left, int top, int right,  
  169.             int bottom) {  
  170.         super.onLayout(changed, left, top, right, bottom);  
  171.           
  172.         final int width = getWidth();  
  173.         final int height = getHeight();  
  174.           
  175.         radius = Math.min(width, height) * 0.5f;  
  176.         centerY = radius;  
  177.         startX = radius;  
  178.         endX = width - radius;  
  179.         spotMinX = startX + borderWidth;  
  180.         spotMaxX = endX - borderWidth;  
  181.         spotSize = height - 4 * borderWidth;  
  182.         spotX = toggleOn ? spotMaxX : spotMinX;  
  183.         offLineWidth = 0;  
  184.     }  
  185.       
  186.     private void slide(){  
  187.             Animation animation = new Animation() {  
  188.                 @Override  
  189.                 protected void applyTransformation(float interpolatedTime,  
  190.                         Transformation t) {  
  191.                     if(toggleOn){  
  192.                         calculateEffect(interpolatedTime);  
  193.                     }else{  
  194.                         calculateEffect(1-interpolatedTime);  
  195.                     }  
  196.                 }  
  197.             };  
  198.             animation.setDuration(200);  
  199.             clearAnimation();  
  200.             startAnimation(animation);  
  201.     }  
  202.   
  203.     private int clamp(int value, int low, int high) {  
  204.         return Math.min(Math.max(value, low), high);  
  205.     }  
  206.   
  207.       
  208.     @Override  
  209.     public void draw(Canvas canvas) {  
  210.         //  
  211.         rect.set(00, getWidth(), getHeight());  
  212.         paint.setColor(borderColor);  
  213.         canvas.drawRoundRect(rect, radius, radius, paint);  
  214.           
  215.         if(offLineWidth > 0){  
  216.             final float cy = offLineWidth * 0.5f;  
  217.             rect.set(spotX - cy, centerY - cy, endX + cy, centerY + cy);  
  218.             paint.setColor(offColor);  
  219.             canvas.drawRoundRect(rect, cy, cy, paint);  
  220.         }  
  221.           
  222.         rect.set(spotX - 1 - radius, centerY - radius, spotX + 1.1f + radius, centerY + radius);  
  223.         paint.setColor(borderColor);  
  224.         canvas.drawRoundRect(rect, radius, radius, paint);  
  225.           
  226.         final float spotR = spotSize * 0.5f;  
  227.         rect.set(spotX - spotR, centerY - spotR, spotX + spotR, centerY + spotR);  
  228.         paint.setColor(spotColor);  
  229.         canvas.drawRoundRect(rect, spotR, spotR, paint);  
  230.           
  231.     }  
  232.       
  233.     /** 
  234.      * @param value 
  235.      */  
  236.     private void calculateEffect(final double value) {  
  237.         final float mapToggleX = (float) mapValueFromRangeToRange(value, 01, spotMinX, spotMaxX);  
  238.         spotX = mapToggleX;  
  239.           
  240.         float mapOffLineWidth = (float) mapValueFromRangeToRange(1 - value, 0110, spotSize);  
  241.           
  242.         offLineWidth = mapOffLineWidth;  
  243.           
  244.         final int fb = Color.blue(onColor);  
  245.         final int fr = Color.red(onColor);  
  246.         final int fg = Color.green(onColor);  
  247.           
  248.         final int tb = Color.blue(offBorderColor);  
  249.         final int tr = Color.red(offBorderColor);  
  250.         final int tg = Color.green(offBorderColor);  
  251.           
  252.         int sb = (int) mapValueFromRangeToRange(1 - value, 01, fb, tb);  
  253.         int sr = (int) mapValueFromRangeToRange(1 - value, 01, fr, tr);  
  254.         int sg = (int) mapValueFromRangeToRange(1 - value, 01, fg, tg);  
  255.           
  256.         sb = clamp(sb, 0255);  
  257.         sr = clamp(sr, 0255);  
  258.         sg = clamp(sg, 0255);  
  259.           
  260.         borderColor = Color.rgb(sr, sg, sb);  
  261.           
  262.         postInvalidate();  
  263.     }  
  264.       
  265.     public interface OnToggleChanged{  
  266.         /** 
  267.          * @param on 
  268.          */  
  269.         public void onToggle(boolean on);  
  270.     }  
  271.   
  272.   
  273.     public void setOnToggleChanged(OnToggleChanged onToggleChanged) {  
  274.         listener = onToggleChanged;  
  275.     }  
  276.       
  277.     public boolean isAnimate() {  
  278.         return defaultAnimate;  
  279.     }  
  280.     public void setAnimate(boolean animate) {  
  281.         this.defaultAnimate = animate;  
  282.     }  
  283.       
  284.     /** 
  285.        * Map a value within a given range to another range. 
  286.        * @param value the value to map 
  287.        * @param fromLow the low end of the range the value is within 
  288.        * @param fromHigh the high end of the range the value is within 
  289.        * @param toLow the low end of the range to map to 
  290.        * @param toHigh the high end of the range to map to 
  291.        * @return the mapped value 
  292.        */  
  293.       public static double mapValueFromRangeToRange(  
  294.           double value,  
  295.           double fromLow,  
  296.           double fromHigh,  
  297.           double toLow,  
  298.           double toHigh) {  
  299.         double fromRangeSize = fromHigh - fromLow;  
  300.         double toRangeSize = toHigh - toLow;  
  301.         double valueScale = (value - fromLow) / fromRangeSize;  
  302.         return toLow + (valueScale * toRangeSize);  
  303.       }  
  304. }  



2.在项目res - values目录下 - 找到 attrs.xml 文件,在resource中间添加如下代码:
[html]  view plain  copy
 print ?
  1. <declare-styleable name="SwitchButton">  
  2.         <attr name="borderWidth" format="dimension">  
  3.         </attr>  
  4.         <attr name="offBorderColor" format="reference|color">  
  5.         </attr>  
  6.         <attr name="offColor" format="reference|color">  
  7.         </attr>  
  8.         <attr name="onColor" format="reference|color">  
  9.         </attr>  
  10.         <attr name="spotColor" format="reference|color">  
  11.         </attr>  
  12.         <attr name="animate" format="reference|boolean">  
  13.         </attr>  
  14.     </declare-styleable>  



3.在XML布局文件中使用控件(路径引用需改成SwitchButton类所在目录):
[html]  view plain  copy
 print ?
  1. <com.ui.widget.view.SwitchButton  
  2.             android:id="@+id/switchButton"  
  3.             android:layout_width="45dp"  
  4.             android:layout_height="25dp"  
  5.             app:borderWidth="0.8dp"  
  6.             app:offBorderColor="@color/light_gray"  
  7.             app:offColor="@color/common_white"  
  8.             app:onColor="@color/common_light_blue"  
  9.             app:spotColor="@color/common_white"  
  10.             app:animate="true"  
  11.             android:layout_margin="20dp" />  
 
  
   控件的属性介绍: 
  
      borderWidth  描边宽度
      offBorderColor 关闭状态下描边的颜色
      offColor 关闭状态的颜色
      onColor  打开状态的颜色
      spotColor 控件中间的圆点的颜色
      animate 是否打开动画效果

4.在Activity中使用:

4.1绑定控件:
如果使用ButterKinfe库的话:
@Bind(R.id.switchButton) SwitchButton switchButton;

findViewById的方式:
SwitchButton switchButton = (SwitchButton)findViewById(R.id.switchButton);

 4.2使用控件: 
  
[java]  view plain  copy
 print ?
  1. switchButton.setToggleOn(false);//默认打开。如果参数传false,则打开页面初始化时不会有动画效果(改变状态还是会有动画)   
  2. switchButton.setOnToggleChanged(new SwitchButton.OnToggleChanged(){        
  3.     @Override        
  4.     public void onToggle(boolean isOn) {        
  5.       //处理自己的逻辑       
  6.       showToast( "SwitchButton"+isOn);      
  7.     }   
  8.   });  
 
  


5.没有第五步了,已经大功告成~ 有问题 欢迎call我,一起讨论~

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值