首页 > 代码库 > 自定义控件--自定义ToggleButton

自定义控件--自定义ToggleButton

  1. 自定义类继承View,并复写三个构造方法
  2. 在构造方法中对背景图片,按钮图片,按钮滑动最大距离进行初始化,设置点击事件
  3. 在点击事件中,对开关状态进行反向操作,并使用invalidate(),重新调用onDraw(),在onDraw()中改变按钮图片的位置,实现开关状态的效果
  4. 实现触摸事件,(注意要调用父类的触摸事件,将事件继续向下转发,在处理结束后要返回true已表示事件已经被消耗)在第一次按下按下控件的时候记录初始的X的坐标,在移动过程中不断的根据X坐标的偏移量刷新视图,在抬起动作中对X坐标进行判断,超过最大滑动距离的一半,滑动到最大距离,不超过,滑动到最小0的位置上
  5. bug解决

bug描述

在移动按钮过程中,不管是否移动超过最大滑动距离的一半,都会导致按钮状态往反方向变化.

bug原因

当前view在响应触摸事件的同时,又响应的点击事件,原因是调用super.onTouchEvent(event);,按照安卓系统的事件传递机制,在触摸事件响应时将触摸事件传递给点击事件,使得在滑动按钮过程中按照触摸事件响应,在抬起时,又响应了点击事件,点击事件导致按钮开关状态的反向.

bug解决

增加开关变量,用于表示在抬起时,点击事件对按钮开关状态的控制权,true表示点击事件获得对按钮开关状态的控制权,false表示点击事件放弃按钮开关状态的控制权

在触摸事件处理中:

按下时将开关置为true,表示此时点击事件与触摸事件都有权控制按钮的开关状态

移动时,如果移动距离超过一定值,触摸事件接管,将开关置为false,让点击事件放弃控制权

抬起时,对开关进行判断,如果触摸事件有控制权,则刷新按钮状态,否则什么也不做,交给点击事件去处理

 

 

代码实现

注:里面有两张图片,需要导入到工程中

import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;public class MyToggleButton extends View implements View.OnClickListener {	private Bitmap backgroundBitmap;	private Bitmap slideBitmap;	private Paint paint;	// 图片距离左边的距离	private float maxSlide;	/**	 * 距离左边的最大距离	 */	private int maxLeft ;	/**	 * 按钮的开关状态 true:开 flase:关	 */	private boolean isStatus = true;	// 测量view	@Override	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {		// 设置测量的值宽和高,采用背景图片的宽高		setMeasuredDimension(backgroundBitmap.getWidth(),				backgroundBitmap.getHeight());	}	// 绘制View	@Override	protected void onDraw(Canvas canvas) {                // 画出背景		canvas.drawBitmap(backgroundBitmap, 0, 0, paint);                //画出滑动按钮		canvas.drawBitmap(slideBitmap, maxSlide, 0, paint);	}		private void initView() {		paint = new Paint();		paint.setColor(Color.RED);		// 设置抗锯齿		paint.setAntiAlias(true);                //初始化背景图片		backgroundBitmap = BitmapFactory.decodeResource(getResources(),				R.drawable.switch_background);                //初始化按钮图片		slideBitmap = BitmapFactory.decodeResource(getResources(),				R.drawable.slide_button);                //设置按钮距离左边最大滑动距离		maxLeft = backgroundBitmap.getWidth() - slideBitmap.getWidth();		// 设置点击事件		 setOnClickListener(this);	}	//第一次按下的X轴坐标	private float startX;	//第一次按下的X轴坐标的历史记录	private float lastX;	/**点击事件是否生效,如果生效滑动事件就无效	 * true生效	 * false无效	 */	private boolean isClicked = false;	@Override	public boolean onTouchEvent(MotionEvent event) {                //调用父类的方法,将点击事件进行转发		super.onTouchEvent(event);		switch (event.getAction()) {		case MotionEvent.ACTION_DOWN:// 按下			//1.第一次按下坐标			lastX = startX = event.getX();			isClicked = true;			break;		case MotionEvent.ACTION_MOVE:// 移动			//2.来到新的X轴			float newX = event.getX();			//3.计算偏移量			float dX = newX - startX;					//4.更新控件的位置			maxSlide += dX;			if(Math.abs(event.getX()-lastX)>5){				isClicked = false;			}			flushView();			//5.重新记录X轴坐标			startX = event.getX();			break;		case MotionEvent.ACTION_UP:// 离开						if(!isClicked){					if(maxSlide >maxLeft/2){					//开					isStatus = true;				}else if(maxSlide <=maxLeft/2){					//关					isStatus = false;				}				flushStatus();			}				break;		}		return true;	}	private void flushView() {		//屏蔽非法值		if(maxSlide <=0){			maxSlide = 0;		}				if(maxSlide >=maxLeft){			maxSlide = maxLeft;		}		//导致onDraw执行		invalidate();	}	// 设置自己的样式的时候用到	public MyToggleButton(Context context, AttributeSet attrs, int defStyle) {		super(context, attrs, defStyle);		initView();	}	// Android系统通过布局文件使用全类名使用控件的时候,使用这个构造方法对控件进行实例化	public MyToggleButton(Context context, AttributeSet attrs) {		super(context, attrs);		initView();	}	// 在代码中使用创建对象的时候使用	public MyToggleButton(Context context) {		super(context);		initView();	}	@Override	public void onClick(View v) {		if(isClicked){			isStatus = !isStatus;			flushStatus();		}		}	private void flushStatus() {				if (isStatus) {			// 开			maxSlide = maxLeft;		} else {			// 关			maxSlide = 0;		}		flushView();	}}

自定义控件--自定义ToggleButton