可滑动的ToggleButton(开关)

2013-12-28 17:25:01

网上看到一篇关于可滑动的ToogleButton的文章,有代码,觉得挺好,但是不符合我的要求,因此在他的代码基础上改了一些。(作者看到了勿喷啊,实在找不到原文了,只好把代码下载地址贴出来。)

源码下载地址: http://download.csdn.net/detail/zshq280017423/4240703

先来两张效果图:

可滑动的ToggleButton(开关)

可滑动的ToggleButton(开关)

然后上代码:

最主要的类是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

下载源码,请猛戳这里。

你可能感兴趣的:(button)