自定义控件--自定义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();

	}



}

你可能感兴趣的:(button)