android自定義開關控制元件-SlideSwitch

yangxi_001發表於2014-10-09

轉自:http://blog.csdn.net/singwhatiwanna/article/details/9254309

1.效果

iphone上有開關控制元件,很漂亮,其實android4.0以後也有switch控制元件,但是隻能用在4.0以後的系統中,這就失去了其使用價值,而且我覺得它的介面也不是很好看。最近看到了百度魔拍上面的一個控制元件,覺得很漂亮啊,然後反編譯了下,儘管沒有混淆過,但是還是不好讀,然後就按照自己的想法寫了個,功能和百度魔拍類似。

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


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

2.原理

繼承自view類,override其onDraw函式,把兩個背景圖(一個灰的一個紅的)和一個開關圖(圓開關)通過canvas畫出來;同時override其onTouchEvent函式,實現滑動效果;最後開啟一個執行緒做動畫,實現緩慢滑動的效果。

3. 程式碼

//SlideSwitch.java
[java] view plaincopy
  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.         Log.d(TAG, "onTouchEvent  x="  + event.getX());  
  130.         switch (action) {  
  131.         case MotionEvent.ACTION_DOWN:  
  132.             mSrcX = (int) event.getX();  
  133.             break;  
  134.         case MotionEvent.ACTION_MOVE:  
  135.             mDstX = Math.max( (int) event.getX(), 10);  
  136.             mDstX = Math.min( mDstX, 62);  
  137.             if(mSrcX == mDstX)  
  138.                 return true;  
  139.             mHasScrolled = true;  
  140.             AnimationTransRunnable aTransRunnable = 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.                 mSwitchStatus = Math.abs(mSwitchStatus-1);  
  148.                 int xFrom = 10, xTo = 62;  
  149.                 if(mSwitchStatus == SWITCH_OFF)  
  150.                 {  
  151.                     xFrom = 62;  
  152.                     xTo = 10;  
  153.                 }  
  154.                 AnimationTransRunnable runnable = new AnimationTransRunnable(xFrom, xTo, 1);  
  155.                 new Thread(runnable).start();  
  156.             }  
  157.             else  
  158.             {  
  159.                 invalidate();  
  160.                 mHasScrolled = false;  
  161.             }  
  162.             //狀態改變的時候 回撥事件函式  
  163.             if(mOnSwitchChangedListener != null)  
  164.             {  
  165.                 mOnSwitchChangedListener.onSwitchChanged(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.         mPaint.setTextSize(14);  
  189.         mPaint.setTypeface(Typeface.DEFAULT_BOLD);  
  190.           
  191.         if(mSwitchStatus == SWITCH_OFF)  
  192.         {  
  193.             drawBitmap(canvas, nullnull, mSwitch_off);  
  194.             drawBitmap(canvas, nullnull, mSwitch_thumb);  
  195.             mPaint.setColor(Color.rgb(105105105));  
  196.             canvas.translate(mSwitch_thumb.getWidth(), 0);  
  197.             canvas.drawText(mOffText, 020, mPaint);  
  198.         }  
  199.         else if(mSwitchStatus == SWITCH_ON)  
  200.         {  
  201.             drawBitmap(canvas, nullnull, mSwitch_on);  
  202.             int count = canvas.save();  
  203.             canvas.translate(mSwitch_on.getWidth() - mSwitch_thumb.getWidth(), 0);  
  204.             drawBitmap(canvas, nullnull, mSwitch_thumb);  
  205.             mPaint.setColor(Color.WHITE);  
  206.             canvas.restoreToCount(count);  
  207.             canvas.drawText(mOnText, 1720, mPaint);  
  208.         }  
  209.         else //SWITCH_SCROLING  
  210.         {  
  211.             mSwitchStatus = mDstX > 35 ? SWITCH_ON : SWITCH_OFF;  
  212.             drawBitmap(canvas, new Rect(00, mDstX, mBmpHeight), new Rect(00, (int)mDstX, mBmpHeight), mSwitch_on);  
  213.             mPaint.setColor(Color.WHITE);  
  214.             canvas.drawText(mOnText, 1720, mPaint);  
  215.   
  216.             int count = canvas.save();  
  217.             canvas.translate(mDstX, 0);  
  218.             drawBitmap(canvas, new Rect(mDstX, 0, mBmpWidth, mBmpHeight),  
  219.                           new Rect(00, mBmpWidth - mDstX, mBmpHeight), mSwitch_off);  
  220.             canvas.restoreToCount(count);  
  221.   
  222.             count = canvas.save();  
  223.             canvas.clipRect(mDstX, 0, mBmpWidth, mBmpHeight);  
  224.             canvas.translate(mThumbWidth, 0);  
  225.             mPaint.setColor(Color.rgb(105105105));  
  226.             canvas.drawText(mOffText, 020, mPaint);  
  227.             canvas.restoreToCount(count);  
  228.   
  229.             count = canvas.save();  
  230.             canvas.translate(mDstX - mThumbWidth / 20);  
  231.             drawBitmap(canvas, nullnull, mSwitch_thumb);  
  232.             canvas.restoreToCount(count);  
  233.         }  
  234.   
  235.     }  
  236.   
  237.     public void drawBitmap(Canvas canvas, Rect src, Rect dst, Bitmap bitmap)  
  238.     {  
  239.         dst = (dst == null ? new Rect(00, bitmap.getWidth(), bitmap.getHeight()) : dst);  
  240.         Paint paint = 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.                 SlideSwitch.this.mSwitchStatus = SWITCH_SCROLING;  
  272.                 SlideSwitch.this.postInvalidate();  
  273.             }  
  274.             else  
  275.             {  
  276.                 Log.d(TAG, "start Animation: [ " + srcX + " , " + dstX + " ]");  
  277.                 int x = srcX + patch;  
  278.                 while (Math.abs(x-dstX) > 5)   
  279.                 {  
  280.                     mDstX = x;  
  281.                     SlideSwitch.this.mSwitchStatus = SWITCH_SCROLING;  
  282.                     SlideSwitch.this.postInvalidate();  
  283.                     x += patch;  
  284.                     try   
  285.                     {  
  286.                         Thread.sleep(10);  
  287.                     }   
  288.                     catch (InterruptedException e)  
  289.                     {  
  290.                         e.printStackTrace();  
  291.                     }  
  292.                 }  
  293.                 mDstX = dstX;  
  294.                 SlideSwitch.this.mSwitchStatus = mDstX > 35 ? SWITCH_ON : SWITCH_OFF;  
  295.                 SlideSwitch.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 plaincopy
  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.   
  86. </LinearLayout>

相關文章