Android自定义开关按钮

这几天做自定义控件,发现一种很常用的开关按钮,效果图如下:

                                        

点击或者拖拽按钮可以实现“开”、“关”两种状态的切换,它的实现原理非常简单:

一个View对象展示在手机屏幕上的时候,它一般会执行以下几个步骤:

1)调用构造方法,创建对象 

2)测量view的大小,调用onMeasure方法

3)确定view的位置,view自身有一定的建议权,但是决定权在父view手中, onLayout方法 

4)绘制view,onDrow方法

依据view对象的执行步骤,我们分别重写view对象的这些方法,然后依次去实现。直接上代码

/**
 * 自定义的开关按钮 请访问 http://blog.csdn.net/qq_20889581?viewmode=contents 阅读详细信息
 * 
 * @author Administrator
 * 
 */
public class MyToggleButton extends View implements OnClickListener {
	// 作为开关按钮的背景图片
	private Bitmap backgroundBitmap;
	// 可以滑动或点击的按钮
	private Bitmap slide_button;
	private Paint paint;
	// 当前按钮的开关状态
	private boolean currState = false;
	// 滑动的按钮图片左边的距离
	private float slide_btn_left;
	// 手指第一次触摸屏幕的时候的X值,也就是down的时候的x值
	private float firstX;
	// 是否发生拖拽
	private boolean isDrag = false;
	private float lastX;

	/**
	 * 在代码中new对象的时候,使用此构造
	 * 
	 * @param context
	 */
	public MyToggleButton(Context context) {
		super(context);
		initView();
	}

	/**
	 * 在布局文件中声明的view,创建时,由系统调用此构造
	 * 
	 * @param context
	 *            上下文
	 * @param attrs
	 *            属性集
	 */
	public MyToggleButton(Context context, AttributeSet attrs) {
		super(context, attrs);
		initView();
	}

	/**
	 * 完成一些初始化的操作
	 */
	private void initView() {
		backgroundBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.background);
		slide_button = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button);
		paint = new Paint();
		// 打开抗矩齿
		paint.setAntiAlias(true);
		setOnClickListener(this);
	}

	/**
	 * 一个View对象显示在屏幕上需要完成的工作如下: 1、调用构造方法,创建对象 2、测量view的大小,调用onMeasure方法
	 * 3、确定view的位置,view自身有一定的建议权,但是决定权在父view手中, onLayout方法 4、绘制view,onDrow方法
	 */

	/**
	 * 测量view大小的时候的回调,设置当前view的大小 width :view的宽度 height :view的高度 (单位:像素)
	 */
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		setMeasuredDimension(backgroundBitmap.getWidth(), backgroundBitmap.getHeight());
	}

	// 确定位置的时候调用此方法
	// 自定义view的时候,作用不大
	// @Override
	// protected void onLayout(boolean changed, int left, int top, int right,
	// int bottom) {
	// super.onLayout(changed, left, top, right, bottom);
	// }

	@Override
	protected void onDraw(Canvas canvas) {
		// super.onDraw(canvas);
		// 绘制 背景
		/*
		 * canvas 画布 bitmap 要绘制的图片 left 图片的左边届 top 图片的上边届 paint 绘制图片要使用的画笔
		 */
		canvas.drawBitmap(backgroundBitmap, 0, 0, paint);
		// 绘制可滑动的按钮图片
		canvas.drawBitmap(slide_button, slide_btn_left, 0, paint);

	}

	@Override
	public void onClick(View v) {
		// 点击的时候,更改滑动按钮的开关状态
		if (!isDrag) {
			currState = !currState;
			// 刷新开关状态
			flushState();
		}
	}

	/**
	 * 要想实现拖拽的效果,需要重写手机屏幕的onTouchEvent方法
	 */
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {

		case MotionEvent.ACTION_DOWN:
			// 手指按下
			// 获得手指在屏幕上的位置,这里只需要X坐标
			firstX = lastX = event.getX();// 记录下当前的最开始在屏幕的值
			isDrag = false;// down的时候没有发生拖拽
			break;

		case MotionEvent.ACTION_MOVE:
			// 手指移动,表示发生了拖拽
			// 判断是否发生了拖拽(移动了5个像素表示发生了拖拽)
			if (Math.abs(event.getX() - firstX) > 5) {
				isDrag = true;
			}
			// 计算手指在屏幕上移动的距离
			float dis = event.getX() - lastX;
			// 手指移动过程中获得的x坐标就是最后的位置
			lastX = event.getX();
			// 设置滑动按钮左边的边界
			slide_btn_left = slide_btn_left + dis;
			break;
		case MotionEvent.ACTION_UP:
			// 手指抬起
			lastX = event.getX();
			if (lastX - firstX == 0) {
				// 这是点击事件
				if (!isDrag) {
					currState = !currState;
					// 刷新开关状态
					flushState();
				}
			} else {
				if (isDrag) {
					// slideBtn左边届最大值
					int maxLeft = backgroundBitmap.getWidth() - slide_button.getWidth();
					if (slide_btn_left > maxLeft / 2) {
						// 为打开状态
						currState = true;
					} else {
						currState = false;
					}
					flushState();
				}
			}

			break;
		}
		// 刷新试图
		flushView();
		return true;
	}

	/**
	 * 刷新开与关的两个状态
	 */
	private void flushState() {
		// currState表示当前的开关状态
		if (currState) {
			// 如果是开的状态
			slide_btn_left = backgroundBitmap.getWidth() - slide_button.getWidth();
		} else {
			slide_btn_left = 0;
		}
		// 刷新当前视图
		flushView();

	}

	private void flushView() {
		/*
		 * 对 slide_btn_left 的值进行判断 ,确保其在合理的位置 即 0<=slide_btn_left <= maxLeft
		 */

		int maxLeft = backgroundBitmap.getWidth() - slide_button.getWidth(); // slideBtn
																				// 左边届最大值

		// 确保 slideBtn_left >= 0
		slide_btn_left = (slide_btn_left > 0) ? slide_btn_left : 0;

		// 确保 slideBtn_left <=maxLeft
		slide_btn_left = (slide_btn_left < maxLeft) ? slide_btn_left : maxLeft;
		// 刷新当前视图,导致onDraw方法执行
		invalidate();
	}

}
值得注意的是,onClick方法和onTouchEvent方法会冲突,也就是说onClick方法不会执行,为了解决这个问题,在down和up的时候分别记录下手指在屏幕的位置,如果up的时候的位置相对于down的位置没有改变,则认为是执行了onClick事件。

点我下载源码



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值