iphone上有开关控件,很漂亮,其实android4.0以后也有switch控件,但是只能用在4.0以后的系统中,这就失去了其使用价值,而且我觉得它的界面也不是很好看。最近看到了百度魔拍上面的一个控件,觉得很漂亮啊,然后反编译了下,尽管没有混淆过,但是还是不好读,然后就按照自己的想法写了个,功能和百度魔拍类似。

下面是百度魔拍的效果和SlideSwitch的效果


滑动开关按钮SlideSwich_控件

滑动开关按钮SlideSwich_ide_02

apk下载地址:http://home.ustc.edu.cn/~voa/res/HelloJni.apk

2.原理


继承自view类,override其onDraw函数,把两个背景图(一个灰的一个红的)和一个开关图(圆开关)通过canvas画出来;同时override其onTouchEvent函数,实现滑动效果;最后开启一个线程做动画,实现缓慢滑动的效果。


3. 代码

//SlideSwitch.java

[java]  ​​view plain​​ ​​copy​​


  1. package com.example.hellojni;  
  2.   
  3. import android.content.Context;  
  4. import android.content.res.Resources;  
  5. import android.graphics.Bitmap;  
  6. import android.graphics.BitmapFactory;  
  7. import android.graphics.Canvas;  
  8. import android.graphics.Color;  
  9. import android.graphics.Paint;  
  10. import android.graphics.Rect;  
  11. import android.graphics.Typeface;  
  12. import android.util.AttributeSet;  
  13. import android.util.Log;  
  14. import android.view.MotionEvent;  
  15. import android.view.View;  
  16. import android.view.ViewGroup.LayoutParams;  
  17.   
  18. /**
  19.  * SlideSwitch 仿iphone滑动开关组件,仿百度魔图滑动开关组件
  20.  * 组件分为三种状态:打开、关闭、正在滑动<br/>
  21.  * 使用方法:        
  22.  * <pre>SlideSwitch slideSwitch = new SlideSwitch(this);
  23.  *slideSwitch.setOnSwitchChangedListener(onSwitchChangedListener);
  24.  *linearLayout.addView(slideSwitch);
  25. </pre>
  26. 注:也可以加载在xml里面使用
  27.  * @author scott
  28.  *
  29.  */  
  30. public class SlideSwitch extends View  
  31. {  
  32. public static final String TAG = "SlideSwitch";  
  33. public static final int SWITCH_OFF = 0;//关闭状态  
  34. public static final int SWITCH_ON = 1;//打开状态  
  35. public static final int SWITCH_SCROLING = 2;//滚动状态  
  36.       
  37. //用于显示的文本  
  38. private String mOnText = "打开";  
  39. private String mOffText = "关闭";  
  40.   
  41. private int mSwitchStatus = SWITCH_OFF;  
  42.   
  43. private boolean mHasScrolled = false;//表示是否发生过滚动  
  44.   
  45. private int mSrcX = 0, mDstX = 0;  
  46.       
  47. private int mBmpWidth = 0;  
  48. private int mBmpHeight = 0;  
  49. private int mThumbWidth = 0;  
  50.   
  51. private     Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  52.       
  53. private OnSwitchChangedListener mOnSwitchChangedListener = null;  
  54.   
  55. //开关状态图  
  56.     Bitmap mSwitch_off, mSwitch_on, mSwitch_thumb;  
  57.   
  58. public SlideSwitch(Context context)   
  59.     {  
  60. this(context, null);  
  61.     }  
  62.   
  63. public SlideSwitch(Context context, AttributeSet attrs)   
  64.     {  
  65. super(context, attrs);  
  66.         init();  
  67.     }  
  68.   
  69. public SlideSwitch(Context context, AttributeSet attrs, int defStyle)  
  70.     {  
  71. super(context, attrs, defStyle);  
  72.         init();  
  73.     }  
  74.   
  75. //初始化三幅图片  
  76. private void init()  
  77.     {  
  78.         Resources res = getResources();  
  79.         mSwitch_off = BitmapFactory.decodeResource(res, R.drawable.bg_switch_off);  
  80.         mSwitch_on = BitmapFactory.decodeResource(res, R.drawable.bg_switch_on);  
  81.         mSwitch_thumb = BitmapFactory.decodeResource(res, R.drawable.switch_thumb);  
  82.         mBmpWidth = mSwitch_on.getWidth();  
  83.         mBmpHeight = mSwitch_on.getHeight();  
  84.         mThumbWidth = mSwitch_thumb.getWidth();  
  85.     }  
  86.   
  87. @Override  
  88. public void setLayoutParams(LayoutParams params)   
  89.     {  
  90.         params.width = mBmpWidth;  
  91.         params.height = mBmpHeight;  
  92. super.setLayoutParams(params);  
  93.     }  
  94.       
  95. /**
  96.      * 为开关控件设置状态改变监听函数
  97.      * @param onSwitchChangedListener 参见 {@link OnSwitchChangedListener}
  98.      */  
  99. public void setOnSwitchChangedListener(OnSwitchChangedListener onSwitchChangedListener)  
  100.     {  
  101.         mOnSwitchChangedListener = onSwitchChangedListener;  
  102.     }  
  103.       
  104. /**
  105.      * 设置开关上面的文本
  106.      * @param onText  控件打开时要显示的文本
  107.      * @param offText  控件关闭时要显示的文本
  108.      */  
  109. public void setText(final String onText, final String offText)  
  110.     {  
  111.         mOnText = onText;  
  112.         mOffText =offText;  
  113.         invalidate();  
  114.     }  
  115.       
  116. /**
  117.      * 设置开关的状态
  118.      * @param on 是否打开开关 打开为true 关闭为false
  119.      */  
  120. public void setStatus(boolean on)  
  121.     {  
  122.         mSwitchStatus = ( on ? SWITCH_ON : SWITCH_OFF);  
  123.     }  
  124.       
  125. @Override  
  126. public boolean onTouchEvent(MotionEvent event)  
  127.     {  
  128. int action = event.getAction();  
  129. "onTouchEvent  x="  + event.getX());  
  130. switch (action) {  
  131. case MotionEvent.ACTION_DOWN:  
  132. int) event.getX();  
  133. break;  
  134. case MotionEvent.ACTION_MOVE:  
  135. int) event.getX(), 10);  
  136. 62);  
  137. if(mSrcX == mDstX)  
  138. return true;  
  139. true;  
  140. new AnimationTransRunnable(mSrcX, mDstX, 0);  
  141. new Thread(aTransRunnable).start();  
  142.             mSrcX = mDstX;  
  143. break;  
  144. case MotionEvent.ACTION_UP:  
  145. if(mHasScrolled == false)//如果没有发生过滑动,就意味着这是一次单击过程  
  146.             {  
  147. 1);  
  148. int xFrom = 10, xTo = 62;  
  149. if(mSwitchStatus == SWITCH_OFF)  
  150.                 {  
  151. 62;  
  152. 10;  
  153.                 }  
  154. new AnimationTransRunnable(xFrom, xTo, 1);  
  155. new Thread(runnable).start();  
  156.             }  
  157. else  
  158.             {  
  159.                 invalidate();  
  160. false;  
  161.             }  
  162. //状态改变的时候 回调事件函数  
  163. if(mOnSwitchChangedListener != null)  
  164.             {  
  165. this, mSwitchStatus);  
  166.             }  
  167. break;  
  168.   
  169. default:  
  170. break;  
  171.         }  
  172. return true;  
  173.     }  
  174.   
  175. @Override  
  176. protected void onSizeChanged(int w, int h, int oldw, int oldh)  
  177.     {  
  178. super.onSizeChanged(w, h, oldw, oldh);  
  179.     }  
  180.   
  181. @Override  
  182. protected void onDraw(Canvas canvas)  
  183.     {  
  184. super.onDraw(canvas);  
  185. //绘图的时候 内部用到了一些数值的硬编码,其实不太好,  
  186. //主要是考虑到图片的原因,图片周围有透明边界,所以要有一定的偏移  
  187. //硬编码的数值只要看懂了代码,其实可以理解其含义,可以做相应改进。  
  188. 14);  
  189.         mPaint.setTypeface(Typeface.DEFAULT_BOLD);  
  190.           
  191. if(mSwitchStatus == SWITCH_OFF)  
  192.         {  
  193. null, null, mSwitch_off);  
  194. null, null, mSwitch_thumb);  
  195. 105, 105, 105));  
  196. 0);  
  197. 0, 20, mPaint);  
  198.         }  
  199. else if(mSwitchStatus == SWITCH_ON)  
  200.         {  
  201. null, null, mSwitch_on);  
  202. int count = canvas.save();  
  203. 0);  
  204. null, null, mSwitch_thumb);  
  205.             mPaint.setColor(Color.WHITE);  
  206.             canvas.restoreToCount(count);  
  207. 17, 20, mPaint);  
  208.         }  
  209. else //SWITCH_SCROLING  
  210.         {  
  211. 35 ? SWITCH_ON : SWITCH_OFF;  
  212. new Rect(0, 0, mDstX, mBmpHeight), new Rect(0, 0, (int)mDstX, mBmpHeight), mSwitch_on);  
  213.             mPaint.setColor(Color.WHITE);  
  214. 17, 20, mPaint);  
  215.   
  216. int count = canvas.save();  
  217. 0);  
  218. new Rect(mDstX, 0, mBmpWidth, mBmpHeight),  
  219. new Rect(0, 0, mBmpWidth - mDstX, mBmpHeight), mSwitch_off);  
  220.             canvas.restoreToCount(count);  
  221.   
  222.             count = canvas.save();  
  223. 0, mBmpWidth, mBmpHeight);  
  224. 0);  
  225. 105, 105, 105));  
  226. 0, 20, mPaint);  
  227.             canvas.restoreToCount(count);  
  228.   
  229.             count = canvas.save();  
  230. 2, 0);  
  231. null, null, mSwitch_thumb);  
  232.             canvas.restoreToCount(count);  
  233.         }  
  234.   
  235.     }  
  236.   
  237. public void drawBitmap(Canvas canvas, Rect src, Rect dst, Bitmap bitmap)  
  238.     {  
  239. null ? new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()) : dst);  
  240. new Paint();  
  241.         canvas.drawBitmap(bitmap, src, dst, paint);  
  242.     }  
  243.   
  244. /**
  245.      * AnimationTransRunnable 做滑动动画所使用的线程
  246.      */  
  247. private class AnimationTransRunnable implements Runnable  
  248.     {  
  249. private int srcX, dstX;  
  250. private int duration;  
  251.   
  252. /**
  253.          * 滑动动画
  254.          * @param srcX 滑动起始点
  255.          * @param dstX 滑动终止点
  256.          * @param duration 是否采用动画,1采用,0不采用
  257.          */  
  258. public AnimationTransRunnable(float srcX, float dstX, final int duration)  
  259.         {  
  260. this.srcX = (int)srcX;  
  261. this.dstX = (int)dstX;  
  262. this.duration = duration;  
  263.         }  
  264.   
  265. @Override  
  266. public void run()   
  267.         {  
  268. final int patch = (dstX > srcX ? 5 : -5);  
  269. if(duration == 0)  
  270.             {  
  271. this.mSwitchStatus = SWITCH_SCROLING;  
  272. this.postInvalidate();  
  273.             }  
  274. else  
  275.             {  
  276. "start Animation: [ " + srcX + " , " + dstX + " ]");  
  277. int x = srcX + patch;  
  278. while (Math.abs(x-dstX) > 5)   
  279.                 {  
  280.                     mDstX = x;  
  281. this.mSwitchStatus = SWITCH_SCROLING;  
  282. this.postInvalidate();  
  283.                     x += patch;  
  284. try   
  285.                     {  
  286. 10);  
  287.                     }   
  288. catch (InterruptedException e)  
  289.                     {  
  290.                         e.printStackTrace();  
  291.                     }  
  292.                 }  
  293.                 mDstX = dstX;  
  294. this.mSwitchStatus = mDstX > 35 ? SWITCH_ON : SWITCH_OFF;  
  295. this.postInvalidate();  
  296.             }  
  297.         }  
  298.   
  299.     }  
  300.   
  301. public static interface OnSwitchChangedListener  
  302.     {  
  303. /**
  304.          * 状态改变 回调函数
  305.          * @param status  SWITCH_ON表示打开 SWITCH_OFF表示关闭
  306.          */  
  307. public abstract void onSwitchChanged(SlideSwitch obj, int status);  
  308.     }  
  309.   
  310. }  


// layout xml

[html]  ​​view plain​​ ​​copy​​


  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3. android:layout_width="fill_parent"  
  4. android:layout_height="fill_parent"  
  5. android:background="#fdfdfd"  
  6. android:orientation="vertical"  
  7. android:paddingLeft="10dip"  
  8. android:paddingRight="10dip" >  
  9.   
  10. <ImageView  
  11. android:id="@+id/imageView1"  
  12. android:layout_width="fill_parent"  
  13. android:layout_height="wrap_content"  
  14. android:src="@drawable/top" />  
  15.   
  16. <RelativeLayout  
  17. android:layout_width="fill_parent"  
  18. android:layout_height="wrap_content" >  
  19.   
  20. <TextView  
  21. android:id="@+id/textView1"  
  22. android:layout_width="wrap_content"  
  23. android:layout_height="wrap_content"  
  24. android:layout_alignParentLeft="true"  
  25. android:layout_centerVertical="true"  
  26. android:text="网络构图"  
  27. android:textSize="15sp" />  
  28.   
  29. <com.example.hellojni.SlideSwitch  
  30. android:id="@+id/slideSwitch1"  
  31. android:layout_width="116dip"  
  32. android:layout_height="46dip"  
  33. android:layout_alignParentRight="true"  
  34. android:layout_centerVertical="true" />  
  35. </RelativeLayout>  
  36.   
  37. <RelativeLayout  
  38. android:layout_width="fill_parent"  
  39. android:layout_height="wrap_content" >  
  40.   
  41. <TextView  
  42. android:id="@+id/textView2"  
  43. android:layout_width="wrap_content"  
  44. android:layout_height="wrap_content"  
  45. android:layout_alignParentLeft="true"  
  46. android:layout_centerVertical="true"  
  47. android:text="保留原图"  
  48. android:textSize="15sp" />  
  49.   
  50. <com.example.hellojni.SlideSwitch  
  51. android:id="@+id/slideSwitch2"  
  52. android:layout_width="116dip"  
  53. android:layout_height="46dip"  
  54. android:layout_alignParentRight="true"  
  55. android:layout_centerVertical="true" />  
  56. </RelativeLayout>  
  57.   
  58. <RelativeLayout  
  59. android:layout_width="fill_parent"  
  60. android:layout_height="wrap_content" >  
  61.   
  62. <TextView  
  63. android:id="@+id/textView3"  
  64. android:layout_width="wrap_content"  
  65. android:layout_height="wrap_content"  
  66. android:layout_alignParentLeft="true"  
  67. android:layout_centerVertical="true"  
  68. android:text="拍照声音"  
  69. android:textSize="15sp" />  
  70.   
  71. <com.example.hellojni.SlideSwitch  
  72. android:id="@+id/slideSwitch3"  
  73. android:layout_width="116px"  
  74. android:layout_height="46px"  
  75. android:layout_alignParentRight="true"  
  76. android:layout_centerVertical="true" />  
  77. </RelativeLayout>  
  78.   
  79. <TextView  
  80. android:id="@+id/textViewTip"  
  81. android:layout_width="fill_parent"  
  82. android:layout_height="wrap_content"  
  83. android:gravity="center"  
  84. android:text="TextView" />  
  85.