Android实战简易教程<四十五>(SlideSwitch-好看又实用的开关按钮)

本文介绍了一款自定义滑动开关控件的实现方法,包括控件的绘制原理及触摸事件处理流程,并展示了如何在布局文件中配置及在Activity中监听其状态变化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

开关按钮也是在项目中经常用到的控件,github上有开源的项目,我们研究下它的使用方法:

1.SlideButton.java:

[java]  view plain copy
  1. /* 
  2.  * Copyright (C) 2015 Quinn Chen 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16. package com.leaking.slideswitch;  
  17.   
  18. import android.animation.Animator;  
  19. import android.animation.AnimatorListenerAdapter;  
  20. import android.animation.ValueAnimator;  
  21. import android.animation.ValueAnimator.AnimatorUpdateListener;  
  22. import android.content.Context;  
  23. import android.content.res.TypedArray;  
  24. import android.graphics.Canvas;  
  25. import android.graphics.Color;  
  26. import android.graphics.Paint;  
  27. import android.graphics.Rect;  
  28. import android.graphics.RectF;  
  29. import android.os.Bundle;  
  30. import android.os.Looper;  
  31. import android.os.Parcelable;  
  32. import android.support.v4.view.MotionEventCompat;  
  33. import android.util.AttributeSet;  
  34. import android.view.MotionEvent;  
  35. import android.view.View;  
  36. import android.view.animation.AccelerateDecelerateInterpolator;  
  37.   
  38. import com.example.slideswitch.R;  
  39.   
  40. public class SlideSwitch extends View {  
  41.   
  42.     public static final int SHAPE_RECT = 1;  
  43.     public static final int SHAPE_CIRCLE = 2;  
  44.     private static final int RIM_SIZE = 6;  
  45.     private static final int DEFAULT_COLOR_THEME = Color.parseColor("#ff00ee00");  
  46.     // 3 attributes  
  47.     private int color_theme;  
  48.     private boolean isOpen;  
  49.     private int shape;  
  50.     // varials of drawing  
  51.     private Paint paint;  
  52.     private Rect backRect;  
  53.     private Rect frontRect;  
  54.     private RectF frontCircleRect;  
  55.     private RectF backCircleRect;  
  56.     private int alpha;  
  57.     private int max_left;  
  58.     private int min_left;  
  59.     private int frontRect_left;  
  60.     private int frontRect_left_begin = RIM_SIZE;  
  61.     private int eventStartX;  
  62.     private int eventLastX;  
  63.     private int diffX = 0;  
  64.     private boolean slideable = true;  
  65.     private SlideListener listener;  
  66.   
  67.     public interface SlideListener {  
  68.         public void open();  
  69.   
  70.         public void close();  
  71.     }  
  72.   
  73.     public SlideSwitch(Context context, AttributeSet attrs, int defStyleAttr) {  
  74.         super(context, attrs, defStyleAttr);  
  75.         listener = null;  
  76.         paint = new Paint();  
  77.         paint.setAntiAlias(true);  
  78.         TypedArray a = context.obtainStyledAttributes(attrs,  
  79.                 R.styleable.slideswitch);  
  80.         color_theme = a.getColor(R.styleable.slideswitch_themeColor,  
  81.                 DEFAULT_COLOR_THEME);  
  82.         isOpen = a.getBoolean(R.styleable.slideswitch_isOpen, false);  
  83.         shape = a.getInt(R.styleable.slideswitch_shape, SHAPE_RECT);  
  84.         a.recycle();  
  85.     }  
  86.   
  87.     public SlideSwitch(Context context, AttributeSet attrs) {  
  88.         this(context, attrs, 0);  
  89.     }  
  90.   
  91.     public SlideSwitch(Context context) {  
  92.         this(context, null);  
  93.     }  
  94.   
  95.     @Override  
  96.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  97.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  98.         int width = measureDimension(280, widthMeasureSpec);  
  99.         int height = measureDimension(140, heightMeasureSpec);  
  100.         if (shape == SHAPE_CIRCLE) {  
  101.             if (width < height)  
  102.                 width = height * 2;  
  103.         }  
  104.         setMeasuredDimension(width, height);  
  105.         initDrawingVal();  
  106.     }  
  107.   
  108.     public void initDrawingVal() {  
  109.         int width = getMeasuredWidth();  
  110.         int height = getMeasuredHeight();  
  111.   
  112.         backCircleRect = new RectF();  
  113.         frontCircleRect = new RectF();  
  114.         frontRect = new Rect();  
  115.         backRect = new Rect(00, width, height);  
  116.         min_left = RIM_SIZE;  
  117.         if (shape == SHAPE_RECT)  
  118.             max_left = width / 2;  
  119.         else  
  120.             max_left = width - (height - 2 * RIM_SIZE) - RIM_SIZE;  
  121.         if (isOpen) {  
  122.             frontRect_left = max_left;  
  123.             alpha = 255;  
  124.         } else {  
  125.             frontRect_left = RIM_SIZE;  
  126.             alpha = 0;  
  127.         }  
  128.         frontRect_left_begin = frontRect_left;  
  129.     }  
  130.   
  131.     public int measureDimension(int defaultSize, int measureSpec) {  
  132.         int result;  
  133.         int specMode = MeasureSpec.getMode(measureSpec);  
  134.         int specSize = MeasureSpec.getSize(measureSpec);  
  135.         if (specMode == MeasureSpec.EXACTLY) {  
  136.             result = specSize;  
  137.         } else {  
  138.             result = defaultSize; // UNSPECIFIED  
  139.             if (specMode == MeasureSpec.AT_MOST) {  
  140.                 result = Math.min(result, specSize);  
  141.             }  
  142.         }  
  143.         return result;  
  144.     }  
  145.   
  146.     @Override  
  147.     protected void onDraw(Canvas canvas) {  
  148.         if (shape == SHAPE_RECT) {  
  149.             paint.setColor(Color.GRAY);  
  150.             canvas.drawRect(backRect, paint);  
  151.             paint.setColor(color_theme);  
  152.             paint.setAlpha(alpha);  
  153.             canvas.drawRect(backRect, paint);  
  154.             frontRect.set(frontRect_left, RIM_SIZE, frontRect_left  
  155.                     + getMeasuredWidth() / 2 - RIM_SIZE, getMeasuredHeight()  
  156.                     - RIM_SIZE);  
  157.             paint.setColor(Color.WHITE);  
  158.             canvas.drawRect(frontRect, paint);  
  159.         } else {  
  160.             // draw circle  
  161.             int radius;  
  162.             radius = backRect.height() / 2 - RIM_SIZE;  
  163.             paint.setColor(Color.GRAY);  
  164.             backCircleRect.set(backRect);  
  165.             canvas.drawRoundRect(backCircleRect, radius, radius, paint);  
  166.             paint.setColor(color_theme);  
  167.             paint.setAlpha(alpha);  
  168.             canvas.drawRoundRect(backCircleRect, radius, radius, paint);  
  169.             frontRect.set(frontRect_left, RIM_SIZE, frontRect_left  
  170.                     + backRect.height() - 2 * RIM_SIZE, backRect.height()  
  171.                     - RIM_SIZE);  
  172.             frontCircleRect.set(frontRect);  
  173.             paint.setColor(Color.WHITE);  
  174.             canvas.drawRoundRect(frontCircleRect, radius, radius, paint);  
  175.         }  
  176.     }  
  177.       
  178.     @Override  
  179.     public boolean onTouchEvent(MotionEvent event) {  
  180.         if (slideable == false)  
  181.             return super.onTouchEvent(event);  
  182.         int action = MotionEventCompat.getActionMasked(event);  
  183.         switch (action) {  
  184.         case MotionEvent.ACTION_DOWN:  
  185.             eventStartX = (int) event.getRawX();  
  186.             break;  
  187.         case MotionEvent.ACTION_MOVE:  
  188.             eventLastX = (int) event.getRawX();  
  189.             diffX = eventLastX - eventStartX;  
  190.             int tempX = diffX + frontRect_left_begin;  
  191.             tempX = (tempX > max_left ? max_left : tempX);  
  192.             tempX = (tempX < min_left ? min_left : tempX);  
  193.             if (tempX >= min_left && tempX <= max_left) {  
  194.                 frontRect_left = tempX;  
  195.                 alpha = (int) (255 * (float) tempX / (float) max_left);  
  196.                 invalidateView();  
  197.             }  
  198.             break;  
  199.         case MotionEvent.ACTION_UP:  
  200.             int wholeX = (int) (event.getRawX() - eventStartX);  
  201.             frontRect_left_begin = frontRect_left;  
  202.             boolean toRight;  
  203.             toRight = (frontRect_left_begin > max_left / 2 ? true : false);  
  204.             if (Math.abs(wholeX) < 3) {  
  205.                 toRight = !toRight;  
  206.             }  
  207.             moveToDest(toRight);  
  208.             break;  
  209.         default:  
  210.             break;  
  211.         }  
  212.         return true;  
  213.     }  
  214.   
  215.     /** 
  216.      * draw again 
  217.      */  
  218.     private void invalidateView() {  
  219.         if (Looper.getMainLooper() == Looper.myLooper()) {  
  220.             invalidate();  
  221.         } else {  
  222.             postInvalidate();  
  223.         }  
  224.     }  
  225.   
  226.     public void setSlideListener(SlideListener listener) {  
  227.         this.listener = listener;  
  228.     }  
  229.   
  230.     public void moveToDest(final boolean toRight) {  
  231.         ValueAnimator toDestAnim = ValueAnimator.ofInt(frontRect_left,  
  232.                 toRight ? max_left : min_left);  
  233.         toDestAnim.setDuration(500);  
  234.         toDestAnim.setInterpolator(new AccelerateDecelerateInterpolator());  
  235.         toDestAnim.start();  
  236.         toDestAnim.addUpdateListener(new AnimatorUpdateListener() {  
  237.   
  238.             @Override  
  239.             public void onAnimationUpdate(ValueAnimator animation) {  
  240.                 frontRect_left = (Integer) animation.getAnimatedValue();  
  241.                 alpha = (int) (255 * (float) frontRect_left / (float) max_left);  
  242.                 invalidateView();  
  243.             }  
  244.         });  
  245.         toDestAnim.addListener(new AnimatorListenerAdapter() {  
  246.             @Override  
  247.             public void onAnimationEnd(Animator animation) {  
  248.                 if (toRight) {  
  249.                     isOpen = true;  
  250.                     if (listener != null)  
  251.                         listener.open();  
  252.                     frontRect_left_begin = max_left;  
  253.                 } else {  
  254.                     isOpen = false;  
  255.                     if (listener != null)  
  256.                         listener.close();  
  257.                     frontRect_left_begin = min_left;  
  258.                 }  
  259.             }  
  260.         });  
  261.     }  
  262.   
  263.     public void setState(boolean isOpen) {  
  264.         this.isOpen = isOpen;  
  265.         initDrawingVal();  
  266.         invalidateView();  
  267.         if (listener != null)  
  268.             if (isOpen == true) {  
  269.                 listener.open();  
  270.             } else {  
  271.                 listener.close();  
  272.             }  
  273.     }  
  274.   
  275.     public void setShapeType(int shapeType) {  
  276.         this.shape = shapeType;  
  277.     }  
  278.   
  279.     public void setSlideable(boolean slideable) {  
  280.         this.slideable = slideable;  
  281.     }  
  282.   
  283.     @Override  
  284.     protected void onRestoreInstanceState(Parcelable state) {  
  285.         if (state instanceof Bundle) {  
  286.             Bundle bundle = (Bundle) state;  
  287.             this.isOpen = bundle.getBoolean("isOpen");  
  288.             state = bundle.getParcelable("instanceState");  
  289.         }  
  290.         super.onRestoreInstanceState(state);  
  291.     }  
  292.   
  293.     @Override  
  294.     protected Parcelable onSaveInstanceState() {  
  295.         Bundle bundle = new Bundle();  
  296.         bundle.putParcelable("instanceState"super.onSaveInstanceState());  
  297.         bundle.putBoolean("isOpen"this.isOpen);  
  298.         return bundle;  
  299.     }  
  300. }  

使用方法:

1.在布局文件中引用控件:

[html]  view plain copy
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     xmlns:slideswitch="http://schemas.android.com/apk/res/com.example.testlibs"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.     android:background="#ffffffff"  
  7.     android:gravity="center_horizontal"  
  8.     android:orientation="vertical"  
  9.     android:padding="10dip"  
  10.     tools:context="com.example.testlibs.MainActivity" >  
  11.   
  12.     <com.leaking.slideswitch.SlideSwitch  
  13.         android:id="@+id/swit"  
  14.         android:layout_width="150dip"  
  15.         android:layout_height="60dip"  
  16.         slideswitch:isOpen="true"  
  17.         slideswitch:shape="rect"  
  18.         slideswitch:themeColor="#ffee3a00" >  
  19.     </com.leaking.slideswitch.SlideSwitch>  
  20.   
  21.     <TextView  
  22.         android:id="@+id/txt"  
  23.         android:layout_width="wrap_content"  
  24.         android:layout_height="wrap_content" />  
  25.   
  26.     <com.leaking.slideswitch.SlideSwitch  
  27.         android:id="@+id/swit2"  
  28.         android:layout_width="190dip"  
  29.         android:layout_height="100dip"  
  30.         android:layout_marginTop="10dip"  
  31.         slideswitch:isOpen="true"  
  32.         slideswitch:shape="circle"  
  33.         slideswitch:themeColor="#ff0a5a00" >  
  34.     </com.leaking.slideswitch.SlideSwitch>  
  35.   
  36.     <com.leaking.slideswitch.SlideSwitch  
  37.         android:id="@+id/swit3"  
  38.         android:layout_width="20dip"  
  39.         android:layout_height="50dip"  
  40.         android:layout_marginTop="10dip"  
  41.         slideswitch:isOpen="true"  
  42.         slideswitch:shape="circle"  
  43.         slideswitch:themeColor="#ff73aa00" >  
  44.     </com.leaking.slideswitch.SlideSwitch>  
  45.   
  46.     <com.leaking.slideswitch.SlideSwitch  
  47.         android:id="@+id/swit4"  
  48.         android:layout_width="100dip"  
  49.         android:layout_height="120dip"  
  50.         android:layout_marginTop="10dip"  
  51.         slideswitch:isOpen="false"  
  52.         slideswitch:shape="circle"  
  53.         slideswitch:themeColor="#f200aa96" >  
  54.     </com.leaking.slideswitch.SlideSwitch>  
  55.   
  56.     <com.leaking.slideswitch.SlideSwitch  
  57.         android:id="@+id/swit5"  
  58.         android:layout_width="90dip"  
  59.         android:layout_height="50dip"  
  60.         android:layout_marginTop="10dip"  
  61.         slideswitch:isOpen="true"  
  62.         slideswitch:shape="rect"  
  63.         slideswitch:themeColor="#f23331a0" >  
  64.     </com.leaking.slideswitch.SlideSwitch>  
  65.   
  66. </LinearLayout>  

MainActivity.java:

[java]  view plain copy
  1. /* 
  2.  * Copyright (C) 2015 Quinn Chen 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16. package com.example.testlibs;  
  17.   
  18. import android.app.Activity;  
  19. import android.os.Bundle;  
  20. import android.widget.TextView;  
  21. import android.widget.Toast;  
  22.   
  23. import com.leaking.slideswitch.SlideSwitch;  
  24. import com.leaking.slideswitch.SlideSwitch.SlideListener;  
  25.   
  26. public class MainActivity extends Activity implements SlideListener {  
  27.   
  28.     TextView txt;  
  29.     SlideSwitch slide;  
  30.     SlideSwitch slide2;  
  31.   
  32.     @Override  
  33.     protected void onCreate(Bundle savedInstanceState) {  
  34.         super.onCreate(savedInstanceState);  
  35.         setContentView(R.layout.activity_main);  
  36.         slide = (SlideSwitch) findViewById(R.id.swit);  
  37.         slide2 = (SlideSwitch) findViewById(R.id.swit2);  
  38.   
  39.         slide.setState(false);  
  40.         txt = (TextView) findViewById(R.id.txt);  
  41.         slide.setSlideListener(this);  
  42.     }  
  43.   
  44.     @Override  
  45.     public void open() {  
  46.         txt.setText("first switch is opend, and set the second one is 'slideable'");  
  47.         Toast.makeText(this"红色打开!蓝色按钮可调!", Toast.LENGTH_LONG).show();  
  48.         slide2.setSlideable(true);  
  49.     }  
  50.   
  51.     @Override  
  52.     public void close() {  
  53.         txt.setText("first switch is closed,and set the second one is 'unslideable'");  
  54.         Toast.makeText(this"红色关闭!蓝色按钮不可调!", Toast.LENGTH_LONG).show();  
  55.         slide2.setSlideable(false);  
  56.     }  
  57. }  

运行代码如下:




下载源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值