前几天在看蘑菇街上有个开关按钮:

就在想是怎样实现的,于是反编译了它的源码,但是这时得到了下面的几张图片:

图片对应的名称:

无色长条:switch_frame;

白色圆点:switch_btn_pressed;
左白右红的长条:switch_bottom;
黑色长条:switch_mask.

那我们就用这几张图片来实现类似的效果吧。

代码:

SwitchButton类:

  1. package com.example.switchbutton;
  2. import android.content.Context;
  3. import android.graphics.Bitmap;
  4. import android.graphics.BitmapFactory;
  5. import android.graphics.Canvas;
  6. import android.graphics.Paint;
  7. import android.graphics.PorterDuff.Mode;
  8. import android.graphics.PorterDuffXfermode;
  9. import android.graphics.Rect;
  10. import android.graphics.RectF;
  11. import android.util.AttributeSet;
  12. import android.view.MotionEvent;
  13. import android.view.View;
  14. public class SwitchButton extends View implements android.view.View.OnClickListener{
  15. private Bitmap mSwitchBottom, mSwitchThumb, mSwitchFrame, mSwitchMask;
  16. private float mCurrentX = 0;
  17. private boolean mSwitchOn = true;//开关默认是开着的
  18. private int mMoveLength;//最大移动距离
  19. private float mLastX = 0;//第一次按下的有效区域
  20. private Rect mDest = null;//绘制的目标区域大小
  21. private Rect mSrc = null;//截取源图片的大小
  22. private int mDeltX = 0;//移动的偏移量
  23. private Paint mPaint = null;
  24. private OnChangeListener mListener = null;
  25. private boolean mFlag = false;
  26. public SwitchButton(Context context) {
  27. this(context, null);
  28. // TODO Auto-generated constructor stub
  29. }
  30. public SwitchButton(Context context, AttributeSet attrs) {
  31. this(context, attrs, 0);
  32. // TODO Auto-generated constructor stub
  33. }
  34. public SwitchButton(Context context, AttributeSet attrs, int defStyle) {
  35. super(context, attrs, defStyle);
  36. // TODO Auto-generated constructor stub
  37. init();
  38. }
  39. /**
  40. * 初始化相关资源
  41. */
  42. public void init() {
  43. mSwitchBottom = BitmapFactory.decodeResource(getResources(),
  44. R.drawable.switch_bottom);
  45. mSwitchThumb = BitmapFactory.decodeResource(getResources(),
  46. R.drawable.switch_btn_pressed);
  47. mSwitchFrame = BitmapFactory.decodeResource(getResources(),
  48. R.drawable.switch_frame);
  49. mSwitchMask = BitmapFactory.decodeResource(getResources(),
  50. R.drawable.switch_mask);
  51. setOnClickListener(this);
  52. setOnTouchListener(new OnTouchListener() {
  53. @Override
  54. public boolean onTouch(View v, MotionEvent event) {
  55. // TODO Auto-generated method stub
  56. return false;
  57. }
  58. });
  59. mMoveLength = mSwitchBottom.getWidth() - mSwitchFrame.getWidth();
  60. mDest = new Rect(0, 0, mSwitchFrame.getWidth(), mSwitchFrame.getHeight());
  61. mSrc = new Rect();
  62. mPaint = new Paint();
  63. mPaint.setAntiAlias(true);
  64. mPaint.setAlpha(255);
  65. mPaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
  66. }
  67. @Override
  68. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  69. // TODO Auto-generated method stub
  70. setMeasuredDimension(mSwitchFrame.getWidth(), mSwitchFrame.getHeight());
  71. }
  72. @Override
  73. protected void onDraw(Canvas canvas) {
  74. // TODO Auto-generated method stub
  75. super.onDraw(canvas);
  76. if (mDeltX > 0 || mDeltX == 0 && mSwitchOn) {
  77. if(mSrc != null) {
  78. mSrc.set(mMoveLength - mDeltX, 0, mSwitchBottom.getWidth()
  79. - mDeltX, mSwitchFrame.getHeight());
  80. }
  81. } else if(mDeltX < 0 || mDeltX == 0 && !mSwitchOn){
  82. if(mSrc != null) {
  83. mSrc.set(-mDeltX, 0, mSwitchFrame.getWidth() - mDeltX,
  84. mSwitchFrame.getHeight());
  85. }
  86. }
  1. <span style="white-space:pre">        </span>//这儿是离屏缓冲,自己感觉类似双缓冲机制吧
  2. int count = canvas.saveLayer(new RectF(mDest), null, Canvas.MATRIX_SAVE_FLAG
  3. | Canvas.CLIP_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG
  4. | Canvas.FULL_COLOR_LAYER_SAVE_FLAG
  5. | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
  6. canvas.drawBitmap(mSwitchBottom, mSrc, mDest, null);
  7. canvas.drawBitmap(mSwitchThumb, mSrc, mDest, null);
  8. canvas.drawBitmap(mSwitchFrame, 0, 0, null);
  9. canvas.drawBitmap(mSwitchMask, 0, 0, mPaint);
  10. canvas.restoreToCount(count);
  11. }
  12. @Override
  13. public boolean onTouchEvent(MotionEvent event) {
  14. // TODO Auto-generated method stub
  15. switch (event.getAction()) {
  16. case MotionEvent.ACTION_DOWN:
  17. mLastX = event.getX();
  18. break;
  19. case MotionEvent.ACTION_MOVE:
  20. mCurrentX = event.getX();
  21. mDeltX = (int) (mCurrentX - mLastX);
  22. // 如果开关开着向左滑动,或者开关关着向右滑动(这时候是不需要处理的)
  23. if ((mSwitchOn && mDeltX < 0) || (!mSwitchOn && mDeltX > 0)) {
  24. mFlag = true;
  25. mDeltX = 0;
  26. }
  27. if (Math.abs(mDeltX) > mMoveLength) {
  28. mDeltX = mDeltX > 0 ? mMoveLength : - mMoveLength;
  29. }
  30. invalidate();
  31. return true;
  32. case MotionEvent.ACTION_UP:
  33. if (Math.abs(mDeltX) > 0 && Math.abs(mDeltX) < mMoveLength / 2) {
  34. mDeltX = 0;
  35. invalidate();
  36. return true;
  37. } else if (Math.abs(mDeltX) > mMoveLength / 2 && Math.abs(mDeltX) <= mMoveLength) {
  38. mDeltX = mDeltX > 0 ? mMoveLength : -mMoveLength;
  39. mSwitchOn = !mSwitchOn;
  40. if(mListener != null) {
  41. mListener.onChange(this, mSwitchOn);
  42. }
  43. invalidate();
  44. mDeltX = 0;
  45. return true;
  46. } else if(mDeltX == 0 && mFlag) {
  47. //这时候得到的是不需要进行处理的,因为已经move过了
  48. mDeltX = 0;
  49. mFlag = false;
  50. return true;
  51. }
  52. return super.onTouchEvent(event);
  53. default:
  54. break;
  55. }
  56. invalidate();
  57. return super.onTouchEvent(event);
  58. }
  59. public void setOnChangeListener(OnChangeListener listener) {
  60. mListener = listener;
  61. }
  62. public interface OnChangeListener {
  63. public void onChange(SwitchButton sb, boolean state);
  64. }
  65. @Override
  66. public void onClick(View v) {
  67. // TODO Auto-generated method stub
  68. mDeltX = mSwitchOn ? mMoveLength : -mMoveLength;
  69. mSwitchOn = !mSwitchOn;
  70. if(mListener != null) {
  71. mListener.onChange(this, mSwitchOn);
  72. }
  73. invalidate();
  74. mDeltX = 0;
  75. }
  76. }

MainActivity:

  1. package com.example.switchbutton;
  2. import com.example.switchbutton.SwitchButton.OnChangeListener;
  3. import android.app.Activity;
  4. import android.os.Bundle;
  5. import android.util.Log;
  6. import android.view.Menu;
  7. import android.widget.Toast;
  8. public class MainActivity extends Activity {
  9. @Override
  10. protected void onCreate(Bundle savedInstanceState) {
  11. super.onCreate(savedInstanceState);
  12. setContentView(R.layout.activity_main);
  13. SwitchButton sb = (SwitchButton) findViewById(R.id.wiperSwitch1);
  14. sb.setOnChangeListener(new OnChangeListener() {
  15. @Override
  16. public void onChange(SwitchButton sb, boolean state) {
  17. // TODO Auto-generated method stub
  18. Log.d("switchButton", state ? "开":"关");
  19. Toast.makeText(MainActivity.this, state ? "开":"关", Toast.LENGTH_SHORT).show();
  20. }
  21. });
  22. }
  23. @Override
  24. public boolean onCreateOptionsMenu(Menu menu) {
  25. // Inflate the menu; this adds items to the action bar if it is present.
  26. getMenuInflater().inflate(R.menu.main, menu);
  27. return true;
  28. }
  29. }

activity_main.xml:

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:orientation="vertical"
  6. android:paddingBottom="@dimen/activity_vertical_margin"
  7. android:paddingLeft="@dimen/activity_horizontal_margin"
  8. android:paddingRight="@dimen/activity_horizontal_margin"
  9. android:paddingTop="@dimen/activity_vertical_margin" >
  10. <ImageView
  11. android:id="@+id/imageView"
  12. android:layout_width="wrap_content"
  13. android:layout_height="wrap_content" />
  14. <com.example.switchbutton.SwitchButton
  15. android:id="@+id/wiperSwitch1"
  16. android:layout_width="wrap_content"
  17. android:layout_height="wrap_content"
  18. android:layout_marginLeft="100dip" />
  19. </LinearLayout>

主要的代码在switchbutton类中,代码有什么不足还希望大家多提提意见,自己很少写。

参考:http://stackoverflow.com/questions/11838022/how-to-paint-with-alpha

http://blog.csdn.net/zenip/article/details/8766263

Android 自定义实现switch开关按钮的更多相关文章

  1. Android ToggleButton(开关函数)与switch (开关按钮)

    1.ToggleButton (1)介绍 (2)组件形状 (3)xml文件设置 <?xml version="1.0" encoding="utf-8"? ...

  2. 自定义常用input表单元素三:纯css实现自定义Switch开关按钮

    自定义常用input表单元素的第三篇,自定义一个Switch开关,表面上看是和input没关系,其实这里采用的是checkbox的checked值的切换.同样,采用css伪类和"+" ...

  3. Android 自定义 view(四)—— onMeasure 方法理解

    前言: 前面我们已经学过<Android 自定义 view(三)-- onDraw 方法理解>,那么接下我们还需要继续去理解自定义view里面的onMeasure 方法 推荐文章: htt ...

  4. Android 自定义view(二) —— attr 使用

    前言: attr 在前一篇文章<Android 自定义view -- attr理解>已经简单的进行了介绍和创建,那么这篇文章就来一步步说说attr的简单使用吧 自定义view简单实现步骤 ...

  5. Android 自定义View及其在布局文件中的使用示例(三):结合Android 4.4.2_r1源码分析onMeasure过程

    转载请注明出处 http://www.cnblogs.com/crashmaker/p/3549365.html From crash_coder linguowu linguowu0622@gami ...

  6. Android 自定义title 之Action Bar

    Android 自定义title 之Action Bar 2014-06-29  飞鹰飞龙...  摘自 博客园  阅 10519  转 25 转藏到我的图书馆   微信分享:   Action Ba ...

  7. Android自定义View (二) 进阶

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/24300125 继续自定义View之旅,前面已经介绍过一个自定义View的基础的例 ...

  8. Android自定义View

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/24252901 很多的Android入门程序猿来说对于Android自定义View ...

  9. Android 自定义ScrollView ListView 体验各种纵向滑动的需求

      分类: [android 进阶之路]2014-08-31 12:59 6190人阅读 评论(10) 收藏 举报 Android自定义ScrollView纵向拖动     转载请标明出处:http: ...

随机推荐

  1. perl文件句柄的传递

    perl 返回文件句柄的2种方式 1.使用 \* #!/usr/bin/perl use strict; sub openfile() { my $path=shift; open(FILE,&quo ...

  2. Effective C++ -----条款34:区分接口继承和实现继承

    接口继承和实现继承不同.在public继承之下,derived classes总是继承base class的接口. pure virtual函数只具体指定接口继承. 简朴的(非纯)impure vir ...

  3. 【leetcode】Number of 1 Bits (easy)

    做太多遍了,秒杀. class Solution { public: int hammingWeight(uint32_t n) { ; ), num++); return num; } };

  4. 【python】入门学习(九)

    面向对象编程 class 定义类,类的值可以修改 _ _init_ _(self) 初始化函数,创建类时自动调用 self 指向对象本身,可以用其他的名字 但不建议 #person.py class ...

  5. 【python】pymongo查找某一时间段的数据

    python中实现: 下面代码就是查找2016-09-26 00:00:00 ~ 2016-09-27 00:00:00 时间段的数据 from datetime import datetimefor ...

  6. swift学习记录之代理

    /// 访客视图的协议 protocol VisitorLoginViewDelegate: NSObjectProtocol { func visitorLoginViewWillRegister( ...

  7. August 29th 2016 Week 36th Monday

    Every has the capital to dream. 每个人都有做梦的本钱. Your vision, our mission. That is an advertisment of UMo ...

  8. AJAX XML返回类型

    例题 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.o ...

  9. MVC学习笔记---各种上下文context

    0  前言 AspNet MVC中比较重要的上下文,有如下: 核心的上下文有HttpContext(请求上下文),ControllerContext(控制器上下文) 过滤器有关有五个的上下文Actio ...

  10. CLR via C#(09)-扩展方法

    对于一些现成的类,如果我们想添加一些新的方法来完善功能,但是不想改变已有的封装,也不想使用派生类,那么该怎么办呢?这里我们可以使用扩展方法. 一见钟情--初识扩展 扩展方法使您能够向现有类型“添加”方 ...