2013-12-28 17:25:01
网上看到一篇关于可滑动的ToogleButton的文章,有代码,觉得挺好,但是不符合我的要求,因此在他的代码基础上改了一些。(作者看到了勿喷啊,实在找不到原文了,只好把代码下载地址贴出来。)
源码下载地址: http://download.csdn.net/detail/zshq280017423/4240703
先来两张效果图:
然后上代码:
最主要的类是SlipButton.java
1 package com.util; 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.Matrix; 8 import android.graphics.Paint; 9 import android.util.AttributeSet; 10 import android.util.Log; 11 import android.view.MotionEvent; 12 import android.view.View; 13 import android.view.View.OnTouchListener; 14 15 import com.view.SlipButton.R; 16 17 public class SlipButton extends View implements OnTouchListener { 18 19 private float DownX, NowX;// 按下时的x,当前的x 20 private float btn_on_left = 0; 21 private float btn_off_left = 0; 22 23 private boolean NowChoose = false;// 记录当前按钮是否打开,true为打开,flase为关闭 24 private boolean isChecked; 25 private boolean OnSlip = false;// 记录用户是否在滑动的变量 26 private boolean isChgLsnOn = false; 27 28 private OnChangedListener ChgLsn; 29 private Bitmap bg_on; 30 private Bitmap bg_off; 31 private Bitmap slip_btn; 32 33 public SlipButton(Context context) { 34 super(context); 35 init(); 36 } 37 38 public SlipButton(Context context, AttributeSet attrs) { 39 super(context, attrs); 40 init(); 41 } 42 43 private void init() { // 初始化 44 bg_on = BitmapFactory.decodeResource(getResources(), 45 R.drawable.split_left_1); 46 bg_off = BitmapFactory.decodeResource(getResources(), 47 R.drawable.split_right_1); 48 slip_btn = BitmapFactory.decodeResource(getResources(), 49 R.drawable.split_1); 50 51 btn_off_left = bg_off.getWidth() - slip_btn.getWidth(); 52 53 setOnTouchListener(this); // 设置监听器,也可以直接复写OnTouchEvent 54 } 55 56 @Override 57 protected void onDraw(Canvas canvas) {// 绘图函数 58 super.onDraw(canvas); 59 60 Matrix matrix = new Matrix(); 61 Paint paint = new Paint(); 62 float x; 63 64 if (NowX < (bg_on.getWidth() / 2)) { // 滑动到前半段与后半段的背景不同,在此做判断 65 x = NowX - slip_btn.getWidth() / 2; 66 canvas.drawBitmap(bg_off, matrix, paint);// 画出关闭时的背景 67 } else { 68 x = bg_on.getWidth() - slip_btn.getWidth() / 2; 69 canvas.drawBitmap(bg_on, matrix, paint);// 画出打开时的背景 70 } 71 72 if (OnSlip) {// 是否是在滑动状态, 73 if (NowX >= bg_on.getWidth()) {// 是否划出指定范围,不能让游标跑到外头,必须做这个判断 74 x = bg_on.getWidth() - slip_btn.getWidth() / 2;// 减去游标1/2的长度... 75 } else if (NowX < 0) { 76 x = 0; 77 } else { 78 x = NowX - slip_btn.getWidth() / 2; 79 } 80 } else {// 非滑动状态 81 if (NowChoose) {// 根据现在的开关状态设置画游标的位置 82 x = btn_off_left; 83 canvas.drawBitmap(bg_on, matrix, paint);// 初始状态为true时应该画出打开状态图片 84 } else { 85 x = btn_on_left; 86 } 87 } 88 if (isChecked) { 89 canvas.drawBitmap(bg_on, matrix, paint); 90 x = btn_off_left; 91 isChecked = !isChecked; 92 } 93 94 if (x < 0) {// 对游标位置进行异常判断... 95 x = 0; 96 } else if (x > bg_on.getWidth() - slip_btn.getWidth()) { 97 x = bg_on.getWidth() - slip_btn.getWidth(); 98 } 99 canvas.drawBitmap(slip_btn, x, 0, paint);// 画出游标. 100 101 } 102 103 public boolean onTouch(View v, MotionEvent event) { 104 switch (event.getAction()) {// 根据动作来执行代码 105 case MotionEvent.ACTION_DOWN:// 按下 106 if (event.getX() > bg_on.getWidth() 107 || event.getY() > bg_on.getHeight()) { 108 return false; 109 } 110 OnSlip = true; 111 DownX = event.getX(); 112 NowX = DownX; 113 break; 114 115 case MotionEvent.ACTION_MOVE:// 滑动 116 Log.d("David", "event.getX = " + event.getX()); 117 Log.d("David", "event.getY = " + event.getY()); 118 NowX = event.getX(); 119 boolean LastChoose = NowChoose; 120 121 if (NowX >= (bg_on.getWidth() / 2)) { 122 NowChoose = true; 123 } else { 124 NowChoose = false; 125 } 126 127 if (isChgLsnOn && (LastChoose != NowChoose)) { // 如果设置了监听器,就调用其方法.. 128 ChgLsn.OnChanged(NowChoose); 129 } 130 break; 131 132 case MotionEvent.ACTION_UP:// 松开 133 OnSlip = false; 134 break; 135 default: 136 } 137 invalidate();// 重画控件 138 return true; 139 } 140 141 public void SetOnChangedListener(OnChangedListener l) {// 设置监听器,当状态修改的时候 142 isChgLsnOn = true; 143 ChgLsn = l; 144 } 145 146 public interface OnChangedListener { 147 abstract void OnChanged(boolean CheckState); 148 } 149 150 public void setCheck(boolean isChecked) { 151 this.isChecked = isChecked; 152 NowChoose = isChecked; 153 } 154 155 @Override 156 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 157 158 int measuredHeight = measureHeight(heightMeasureSpec); 159 int measuredWidth = measureWidth(widthMeasureSpec); 160 setMeasuredDimension(measuredWidth, measuredHeight); 161 } 162 163 private int measureHeight(int measureSpec) { 164 165 /*int specMode = MeasureSpec.getMode(measureSpec); 166 int specSize = MeasureSpec.getSize(measureSpec); 167 168 // Default size if no limits are specified. 169 170 int result = 500; 171 if (specMode == MeasureSpec.AT_MOST){ 172 // Calculate the ideal size of your 173 // control within this maximum size. 174 // If your control fills the available 175 // space return the outer bound. 176 177 result = specSize; 178 } else if (specMode == MeasureSpec.EXACTLY){ 179 // If your control can fit within these bounds return that value. 180 result = specSize; 181 }*/ 182 return bg_on.getHeight(); 183 } 184 185 private int measureWidth(int measureSpec) { 186 187 /*int specMode = MeasureSpec.getMode(measureSpec); 188 int specSize = MeasureSpec.getSize(measureSpec); 189 190 // Default size if no limits are specified. 191 int result = 500; 192 if (specMode == MeasureSpec.AT_MOST){ 193 // Calculate the ideal size of your control 194 // within this maximum size. 195 // If your control fills the available space 196 // return the outer bound. 197 result = specSize; 198 } else if (specMode == MeasureSpec.EXACTLY){ 199 // If your control can fit within these bounds return that value. 200 result = specSize; 201 }*/ 202 return bg_on.getWidth(); 203 } 204 }
代码比较简单,而且注释比较详细。
说一些几个问题:
1. onTouch()方法中得到的event.getX()和event.getY()是什么值?取值范围是多少?
刚开始我以为不管咱们的SlipButton放在什么位置,得到的event.getX()值因该是相对屏幕的坐标值,~~其实是错误的,event.getX()只有你的触摸点在当前SlipButton view的布局范围之内才会取到值的。但是值的范围可不仅仅是你的SlipButton view的大小哦,因为一旦你的触摸点在view范围之内触摸到,那么触摸点就可以移到View之外的任何地方了,所以取值范围应该是全屏哦,因此我们在代码里做了如下判断:
1 if (event.getX() > bg_on.getWidth() 2 || event.getY() > bg_on.getHeight()) { 3 return false; 4 }
2. measureWidth()和measureHeight()为什么会返回一个固定值?
首先根据用途,我们自定义的SlipButton View根本没有必要去由调用者调整大小,因为这个ToggleButton本身就是起到开关作用的,在应用中应该是一致的,所以我让这两个方法返回固定值。关于onMeasure()方法根详细的描述,请看我的另一篇文章:http://www.cnblogs.com/wlrhnh/p/3479928.html
下载源码,请猛戳这里。