滑动开关按钮SlideSwich

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 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.  * 组件分为三种状态:打开、关闭、正在滑动
     
  21.  * 使用方法:         
  22.  * 
    SlideSwitch slideSwitch = new SlideSwitch(this); 
  23.  *slideSwitch.setOnSwitchChangedListener(onSwitchChangedListener); 
  24.  *linearLayout.addView(slideSwitch); 
  25.  
  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 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.   

你可能感兴趣的:(android入门)