首页 > 代码库 > 自定义控件--自定义ToggleButton
自定义控件--自定义ToggleButton
- 自定义类继承View,并复写三个构造方法
- 在构造方法中对背景图片,按钮图片,按钮滑动最大距离进行初始化,设置点击事件
- 在点击事件中,对开关状态进行反向操作,并使用invalidate(),重新调用onDraw(),在onDraw()中改变按钮图片的位置,实现开关状态的效果
- 实现触摸事件,(注意要调用父类的触摸事件,将事件继续向下转发,在处理结束后要返回true已表示事件已经被消耗)在第一次按下按下控件的时候记录初始的X的坐标,在移动过程中不断的根据X坐标的偏移量刷新视图,在抬起动作中对X坐标进行判断,超过最大滑动距离的一半,滑动到最大距离,不超过,滑动到最小0的位置上
- 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
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。