滑动开关按钮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

 package com.example.hellojni;  
  
 import android.content.Context;  
 import android.content.res.Resources;  
 import android.graphics.Bitmap;  
 import android.graphics.BitmapFactory;  
 import android.graphics.Canvas;  
 import android.graphics.Color;  
 import android.graphics.Paint;  
 import android.graphics.Rect;  
 import android.graphics.Typeface;  
 import android.util.AttributeSet;  
 import android.util.Log;  
 import android.view.MotionEvent;  
 import android.view.View;  
 import android.view.ViewGroup.LayoutParams;  
  
 /** 
  * SlideSwitch 仿iphone滑动开关组件,仿百度魔图滑动开关组件 
  * 组件分为三种状态:打开、关闭、正在滑动<br/> 
  * 使用方法:         
  * <pre>SlideSwitch slideSwitch = new SlideSwitch(this); 
  *slideSwitch.setOnSwitchChangedListener(onSwitchChangedListener); 
  *linearLayout.addView(slideSwitch); 
 </pre> 
 注:也可以加载在xml里面使用 
  * @author scott 
  * 
  */ 
 public class SlideSwitch extends View  
 {  
  public static final String TAG = "SlideSwitch";  
  public static final int SWITCH_OFF = 0;//关闭状态 
  public static final int SWITCH_ON = 1;//打开状态 
  public static final int SWITCH_SCROLING = 2;//滚动状态 
  
  //用于显示的文本 
  private String mOnText = "打开";  
  private String mOffText = "关闭";  
  
  private int mSwitchStatus = SWITCH_OFF;  
  
  private boolean mHasScrolled = false;//表示是否发生过滚动 
  
  private int mSrcX = 0, mDstX = 0;  
  
  private int mBmpWidth = 0;  
  private int mBmpHeight = 0;  
  private int mThumbWidth = 0;  
  
  private     Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
  
  private OnSwitchChangedListener mOnSwitchChangedListener = null;  
  
  //开关状态图 
     Bitmap mSwitch_off, mSwitch_on, mSwitch_thumb;  
  
  public SlideSwitch(Context context)   
     {  
  this(context, null);  
     }  
  
  public SlideSwitch(Context context, AttributeSet attrs)   
     {  
  super(context, attrs);  
         init();  
     }  
  
  public SlideSwitch(Context context, AttributeSet attrs, int defStyle)  
     {  
  super(context, attrs, defStyle);  
         init();  
     }  
  
  //初始化三幅图片 
  private void init()  
     {  
         Resources res = getResources();  
         mSwitch_off = BitmapFactory.decodeResource(res, R.drawable.bg_switch_off);  
         mSwitch_on = BitmapFactory.decodeResource(res, R.drawable.bg_switch_on);  
         mSwitch_thumb = BitmapFactory.decodeResource(res, R.drawable.switch_thumb);  
         mBmpWidth = mSwitch_on.getWidth();  
         mBmpHeight = mSwitch_on.getHeight();  
         mThumbWidth = mSwitch_thumb.getWidth();  
     }  
  
  @Override 
  public void setLayoutParams(LayoutParams params)   
     {  
         params.width = mBmpWidth;  
         params.height = mBmpHeight;  
  super.setLayoutParams(params);  
     }  
  
  /** 
      * 为开关控件设置状态改变监听函数 
      * @param onSwitchChangedListener 参见 {@link OnSwitchChangedListener} 
      */ 
  public void setOnSwitchChangedListener(OnSwitchChangedListener onSwitchChangedListener)  
     {  
         mOnSwitchChangedListener = onSwitchChangedListener;  
     }  
  
  /** 
      * 设置开关上面的文本 
      * @param onText  控件打开时要显示的文本 
      * @param offText  控件关闭时要显示的文本 
      */ 
  public void setText(final String onText, final String offText)  
     {  
         mOnText = onText;  
         mOffText =offText;  
         invalidate();  
     }  
  
  /** 
      * 设置开关的状态 
      * @param on 是否打开开关 打开为true 关闭为false 
      */ 
  public void setStatus(boolean on)  
     {  
         mSwitchStatus = ( on ? SWITCH_ON : SWITCH_OFF);  
     }  
  
  @Override 
  public boolean onTouchEvent(MotionEvent event)  
     {  
  int action = event.getAction();  
         Log.d(TAG, "onTouchEvent  x="  + event.getX());  
  switch (action) {  
  case MotionEvent.ACTION_DOWN:  
             mSrcX = (int) event.getX();  
  break;  
  case MotionEvent.ACTION_MOVE:  
             mDstX = Math.max( (int) event.getX(), 10);  
             mDstX = Math.min( mDstX, 62);  
  if(mSrcX == mDstX)  
  return true;  
             mHasScrolled = true;  
             AnimationTransRunnable aTransRunnable = new AnimationTransRunnable(mSrcX, mDstX, 0);  
  new Thread(aTransRunnable).start();  
             mSrcX = mDstX;  
  break;  
  case MotionEvent.ACTION_UP:  
  if(mHasScrolled == false)//如果没有发生过滑动,就意味着这是一次单击过程 
             {  
                 mSwitchStatus = Math.abs(mSwitchStatus-1);  
  int xFrom = 10, xTo = 62;  
  if(mSwitchStatus == SWITCH_OFF)  
                 {  
                     xFrom = 62;  
                     xTo = 10;  
                 }  
                 AnimationTransRunnable runnable = new AnimationTransRunnable(xFrom, xTo, 1);  
  new Thread(runnable).start();  
             }  
  else 
             {  
                 invalidate();  
                 mHasScrolled = false;  
             }  
  //状态改变的时候 回调事件函数 
  if(mOnSwitchChangedListener != null)  
             {  
                 mOnSwitchChangedListener.onSwitchChanged(this, mSwitchStatus);  
             }  
  break;  
  
  default:  
  break;  
         }  
  return true;  
     }  
  
  @Override 
  protected void onSizeChanged(int w, int h, int oldw, int oldh)  
     {  
  super.onSizeChanged(w, h, oldw, oldh);  
     }  
  
  @Override 
  protected void onDraw(Canvas canvas)  
     {  
  super.onDraw(canvas);  
  //绘图的时候 内部用到了一些数值的硬编码,其实不太好, 
  //主要是考虑到图片的原因,图片周围有透明边界,所以要有一定的偏移 
  //硬编码的数值只要看懂了代码,其实可以理解其含义,可以做相应改进。 
         mPaint.setTextSize(14);  
         mPaint.setTypeface(Typeface.DEFAULT_BOLD);  
  
  if(mSwitchStatus == SWITCH_OFF)  
         {  
             drawBitmap(canvas, null, null, mSwitch_off);  
             drawBitmap(canvas, null, null, mSwitch_thumb);  
             mPaint.setColor(Color.rgb(105, 105, 105));  
             canvas.translate(mSwitch_thumb.getWidth(), 0);  
             canvas.drawText(mOffText, 0, 20, mPaint);  
         }  
  else if(mSwitchStatus == SWITCH_ON)  
         {  
             drawBitmap(canvas, null, null, mSwitch_on);  
  int count = canvas.save();  
             canvas.translate(mSwitch_on.getWidth() - mSwitch_thumb.getWidth(), 0);  
             drawBitmap(canvas, null, null, mSwitch_thumb);  
             mPaint.setColor(Color.WHITE);  
             canvas.restoreToCount(count);  
             canvas.drawText(mOnText, 17, 20, mPaint);  
         }  
  else //SWITCH_SCROLING 
         {  
             mSwitchStatus = mDstX > 35 ? SWITCH_ON : SWITCH_OFF;  
             drawBitmap(canvas, new Rect(0, 0, mDstX, mBmpHeight), new Rect(0, 0, (int)mDstX, mBmpHeight), mSwitch_on);  
             mPaint.setColor(Color.WHITE);  
             canvas.drawText(mOnText, 17, 20, mPaint);  
  
  int count = canvas.save();  
             canvas.translate(mDstX, 0);  
             drawBitmap(canvas, new Rect(mDstX, 0, mBmpWidth, mBmpHeight),  
  new Rect(0, 0, mBmpWidth - mDstX, mBmpHeight), mSwitch_off);  
             canvas.restoreToCount(count);  
  
             count = canvas.save();  
             canvas.clipRect(mDstX, 0, mBmpWidth, mBmpHeight);  
             canvas.translate(mThumbWidth, 0);  
             mPaint.setColor(Color.rgb(105, 105, 105));  
             canvas.drawText(mOffText, 0, 20, mPaint);  
             canvas.restoreToCount(count);  
  
             count = canvas.save();  
             canvas.translate(mDstX - mThumbWidth / 2, 0);  
             drawBitmap(canvas, null, null, mSwitch_thumb);  
             canvas.restoreToCount(count);  
         }  
  
     }  
  
  public void drawBitmap(Canvas canvas, Rect src, Rect dst, Bitmap bitmap)  
     {  
         dst = (dst == null ? new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()) : dst);  
         Paint paint = new Paint();  
         canvas.drawBitmap(bitmap, src, dst, paint);  
     }  
  
  /** 
      * AnimationTransRunnable 做滑动动画所使用的线程 
      */ 
  private class AnimationTransRunnable implements Runnable  
     {  
  private int srcX, dstX;  
  private int duration;  
  
  /** 
          * 滑动动画 
          * @param srcX 滑动起始点 
          * @param dstX 滑动终止点 
          * @param duration 是否采用动画,1采用,0不采用 
          */ 
  public AnimationTransRunnable(float srcX, float dstX, final int duration)  
         {  
  this.srcX = (int)srcX;  
  this.dstX = (int)dstX;  
  this.duration = duration;  
         }  
  
  @Override 
  public void run()   
         {  
  final int patch = (dstX > srcX ? 5 : -5);  
  if(duration == 0)  
             {  
                 SlideSwitch.this.mSwitchStatus = SWITCH_SCROLING;  
                 SlideSwitch.this.postInvalidate();  
             }  
  else 
             {  
                 Log.d(TAG, "start Animation: [ " + srcX + " , " + dstX + " ]");  
  int x = srcX + patch;  
  while (Math.abs(x-dstX) > 5)   
                 {  
                     mDstX = x;  
                     SlideSwitch.this.mSwitchStatus = SWITCH_SCROLING;  
                     SlideSwitch.this.postInvalidate();  
                     x += patch;  
  try 
                     {  
                         Thread.sleep(10);  
                     }   
  catch (InterruptedException e)  
                     {  
                         e.printStackTrace();  
                     }  
                 }  
                 mDstX = dstX;  
                 SlideSwitch.this.mSwitchStatus = mDstX > 35 ? SWITCH_ON : SWITCH_OFF;  
                 SlideSwitch.this.postInvalidate();  
             }  
         }  
  
     }  
  
  public static interface OnSwitchChangedListener  
     {  
  /** 
          * 状态改变 回调函数 
          * @param status  SWITCH_ON表示打开 SWITCH_OFF表示关闭 
          */ 
  public abstract void onSwitchChanged(SlideSwitch obj, int status);  
     }  
  
 }  

// layout xml

 <?xml version="1.0" encoding="utf-8"?> 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  android:layout_width="fill_parent" 
  android:layout_height="fill_parent" 
  android:background="#fdfdfd" 
  android:orientation="vertical" 
  android:paddingLeft="10dip" 
  android:paddingRight="10dip" > 
  
  <ImageView 
  android:id="@+id/imageView1" 
  android:layout_width="fill_parent" 
  android:layout_height="wrap_content" 
  android:src="@drawable/top" /> 
  
  <RelativeLayout 
  android:layout_width="fill_parent" 
  android:layout_height="wrap_content" > 
  
  <TextView 
  android:id="@+id/textView1" 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:layout_alignParentLeft="true" 
  android:layout_centerVertical="true" 
  android:text="网络构图" 
  android:textSize="15sp" /> 
  
  <com.example.hellojni.SlideSwitch 
  android:id="@+id/slideSwitch1" 
  android:layout_width="116dip" 
  android:layout_height="46dip" 
  android:layout_alignParentRight="true" 
  android:layout_centerVertical="true" /> 
  </RelativeLayout> 
  
  <RelativeLayout 
  android:layout_width="fill_parent" 
  android:layout_height="wrap_content" > 
  
  <TextView 
  android:id="@+id/textView2" 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:layout_alignParentLeft="true" 
  android:layout_centerVertical="true" 
  android:text="保留原图" 
  android:textSize="15sp" /> 
  
  <com.example.hellojni.SlideSwitch 
  android:id="@+id/slideSwitch2" 
  android:layout_width="116dip" 
  android:layout_height="46dip" 
  android:layout_alignParentRight="true" 
  android:layout_centerVertical="true" /> 
  </RelativeLayout> 
  
  <RelativeLayout 
  android:layout_width="fill_parent" 
  android:layout_height="wrap_content" > 
  
  <TextView 
  android:id="@+id/textView3" 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:layout_alignParentLeft="true" 
  android:layout_centerVertical="true" 
  android:text="拍照声音" 
  android:textSize="15sp" /> 
  
  <com.example.hellojni.SlideSwitch 
  android:id="@+id/slideSwitch3" 
  android:layout_width="116px" 
  android:layout_height="46px" 
  android:layout_alignParentRight="true" 
  android:layout_centerVertical="true" /> 
  </RelativeLayout> 
  
  <TextView 
  android:id="@+id/textViewTip" 
  android:layout_width="fill_parent" 
  android:layout_height="wrap_content" 
  android:gravity="center" 
  android:text="TextView" /> 
  

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android干货

Android项目实战(三十二):圆角对话框Dialog

3766
来自专栏Android源码框架分析

三句代码创建全屏Dialog或者DialogFragment:带你从源码角度实现全屏Dialog

Dialog是APP开发中常用的控件,同Activity类似,拥有独立的Window窗口,但是Dialog跟Activity还是有一定区别的,最明显的就是:默认...

3694
来自专栏androidBlog

你真的了解View的坐标吗?

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/details/...

822
来自专栏向治洪

android Titlebar一行代码实现沉浸式效果

github地址 一个简单易用的导航栏TitleBar,可以轻松实现IOS导航栏的各种效果  整个代码全部集中在TitleBar.java中,所有控件都动...

2088
来自专栏向治洪

android 仿音悦台页面交互效果

概述 新版的音悦台 APP 播放页面交互非常有意思,可以把播放器往下拖动,然后在底部悬浮一个小框,还可以左右拖动,然后回弹的时候也会有相应的效果,这种交互效果在...

2567
来自专栏向治洪

listview滑动删除

今天还是给大家带来自定义控件的编写,自定义一个ListView的左右滑动删除Item的效果,这个效果之前已经实现过了,有兴趣的可以看下Android 使用Scr...

2557
来自专栏程序员叨叨叨

听说你想用ViewPager实现这样的效果?

此图盗于https://github.com/smallnew/FuCardPager

1023
来自专栏向治洪

SliferMenu详解

SlidingMenu简介: SlidingMenu的是一种比较新的设置界面或配置界面效果,在主界面左滑或者右滑出现设置界面,能方便的进行各种操作.目前有大...

2005
来自专栏Android干货

Android开发实战(二十一):浅谈android:clipChildren属性

2623
来自专栏everhad

[BOT]自定义ViewPagerStripIndicator

效果图 app中下面这样的控件很常见,像默认的TabHost表现上不够灵活,下面就简单写一个可以结合ViewPager切换内容显示,提供底部“滑动条”指示所显示...

2205

扫码关注云+社区

领取腾讯云代金券