1 <RelativeLayout
2 xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:tools="http://schemas.android.com/tools"
4 android:layout_width="match_parent"
5 android:layout_height="match_parent" >
6
7 <com.example.testdemo.MyToggleButton
8 android:id="@+id/my_toggle_btn"
9 android:layout_width="wrap_content"
10 android:layout_height="wrap_content"
11 android:layout_centerHorizontal="true"
12 android:layout_centerVertical="true" />
13
14 </RelativeLayout>
1 package com.example.testdemo; 2 3 import android.content.Context; 4 import android.graphics.Bitmap; 5 import android.graphics.BitmapFactory; 6 import android.graphics.Canvas; 7 import android.graphics.Paint; 8 import android.util.AttributeSet; 9 import android.view.MotionEvent; 10 import android.view.View; 11 import android.view.View.OnClickListener; 12 13 public class MyToggleButton extends View implements OnClickListener { 14 15 // 做为背景的图片 16 private Bitmap backgroundBitmap; 17 // 可以滑动的图片 18 private Bitmap slideBtn; 19 private Paint paint; 20 // 滑动按钮的左边届 21 private float slideBtn_left; 22 // down 事件时的x值 23 private int firstX; 24 // touch 事件的上一个x值 25 private int lastX; 26 // 当前开关的状态 true 为开 27 private boolean currState = false; 28 // 判断是否发生拖动, 如果拖动了,就不再响应 onclick 事件 29 private boolean isDrag = false; 30 31 /** 32 * 在代码里面创建对象的时候,使用此构造方法 33 */ 34 public MyToggleButton(Context context) { 35 super(context); 36 // TODO Auto-generated constructor stub 37 } 38 39 /** 40 * 在布局文件中声名的view,创建时由系统自动调用。 41 * 42 * @param context 43 * 上下文对象 44 * @param attrs 45 * 属性集 46 */ 47 public MyToggleButton(Context context, AttributeSet attrs) { 48 super(context, attrs); 49 50 initView(); 51 } 52 53 /** 54 * 初始化 55 */ 56 private void initView() { 57 58 // 初始化图片 59 backgroundBitmap = BitmapFactory.decodeResource(getResources(), 60 R.drawable.switch_background); 61 slideBtn = BitmapFactory.decodeResource(getResources(), 62 R.drawable.slide_button); 63 64 // 初始化 画笔 65 paint = new Paint(); 66 paint.setAntiAlias(true); // 打开抗矩齿 67 68 // 添加onclick事件监听 69 setOnClickListener(this); 70 } 71 72 /* 73 * view 对象显示的屏幕上,有几个重要步骤: 74 * 1、构造方法 创建 对象。 75 * 2、测量view的大小。 onMeasure(int,int); 76 * 3、确定view的位置 ,view自身有一些建议权,决定权在 父view手中。 onLayout(); 77 * 4、绘制 view 的内容 。 78 * onDraw(Canvas) 79 */ 80 81 @Override 82 /** 83 * 测量尺寸时的回调方法 84 */ 85 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 86 // super.onMeasure(widthMeasureSpec, heightMeasureSpec); 87 // 设置当前view的大小 width :view的宽度 height :view的高度 (单位:像素) 88 setMeasuredDimension(backgroundBitmap.getWidth(), 89 backgroundBitmap.getHeight()); 90 } 91 92 // 确定位置的时候调用此方法 93 // 自定义view的时候,作用不大 94 // @Override 95 // protected void onLayout(boolean changed, int left, int top, int right, 96 // int bottom) { 97 // super.onLayout(changed, left, top, right, bottom); 98 // } 99 100 @Override 101 /** 102 * 绘制当前view的内容 103 */ 104 protected void onDraw(Canvas canvas) { 105 // super.onDraw(canvas); 106 107 // 绘制 背景 108 /* 109 * backgroundBitmap 要绘制的图片 left 图片的左边届 top 图片的上边届 paint 绘制图片要使用的画笔 110 */ 111 canvas.drawBitmap(backgroundBitmap, 0, 0, paint); 112 113 // 绘制 可滑动的按钮 114 canvas.drawBitmap(slideBtn, slideBtn_left, 0, paint); 115 } 116 117 118 @Override 119 /** 120 * onclick 事件在View.onTouchEvent 中被解析。 121 * 系统对onclick 事件的解析,过于简陋,只要有down 事件 up 事件, 122 * 系统即认为 发生了click 事件 123 */ 124 public void onClick(View v) { 125 // 如果没有拖动,才执行改变状态的动作 126 if (!isDrag) { 127 currState = !currState; 128 flushState(); 129 } 130 } 131 132 @Override 133 public boolean onTouchEvent(MotionEvent event) { 134 super.onTouchEvent(event); 135 136 switch (event.getAction()) { 137 case MotionEvent.ACTION_DOWN: 138 firstX = lastX = (int) event.getX(); 139 isDrag = false; 140 141 break; 142 case MotionEvent.ACTION_MOVE: 143 144 // 判断是否发生拖动 145 if (Math.abs(event.getX() - firstX) > 5) { 146 isDrag = true; 147 } 148 149 // 计算 手指在屏幕上移动的距离 150 int dis = (int) (event.getX() - lastX); 151 152 // 将本次的位置 设置给lastX 153 lastX = (int) event.getX(); 154 155 // 根据手指移动的距离,改变slideBtn_left 的值 156 slideBtn_left = slideBtn_left + dis; 157 break; 158 case MotionEvent.ACTION_UP: 160 // 在发生拖动的情况下,根据最后的位置,判断当前开关的状态 161 if (isDrag) { 162 // alideBtn 左边界最大值 163 int maxLeft = backgroundBitmap.getWidth() - slideBtn.getWidth(); 165 /* 166 * 根据 slideBtn_left 判断,当前应是什么状态 167 */ 168 if (slideBtn_left > maxLeft / 2) { // 此时应为 打开的状态 169 currState = true; 170 } else { 171 currState = false; 172 } 174 flushState(); 175 } 176 break; 177 } 179 flushView(); 181 return true; 182 } 183 184 /** 185 * 刷新当前状态 186 */ 187 private void flushState() { 188 if (currState) { 189 slideBtn_left = backgroundBitmap.getWidth() - slideBtn.getWidth(); 190 } else { 191 slideBtn_left = 0; 192 } 194 flushView(); 195 } 196 197 /** 198 * 刷新当前视力 199 */ 200 private void flushView() { 201 /* 202 * 对 slideBtn_left 的值进行判断 ,确保其在合理的位置 即 0<=slideBtn_left <= maxLeft 203 */ 205 int maxLeft = backgroundBitmap.getWidth() - slideBtn.getWidth(); 207 208 // 确保 slideBtn_left >= 0 209 slideBtn_left = (slideBtn_left > 0) ? slideBtn_left : 0; 210 211 // 确保 slideBtn_left <=maxLeft 212 slideBtn_left = (slideBtn_left < maxLeft) ? slideBtn_left : maxLeft; 213 214 /* 215 * 刷新当前视图 导致 执行onDraw执行 216 */ 217 invalidate(); 218 } 219 220 }
DEMO下载地址:http://pan.baidu.com/s/1qWjXEhQ