标签:
可以拖动的开关,开关滑块随着手指拖动而变化。
使用 int 值来区分状态,相比使用boolean值区分状态使代码更加容易理解、更容易些代码。有三种状态: 白天、黑夜、滚动状态。
canvas画布在滚动状态时,如何画开、关两种状态。
供用户回调的监听器在哪设置?
点击事件、滑块没有滑满就松手、滑块滑满了。这三个地方添加回调方法。
自定义控件:先看构造方法:
public SlideSwitchButton(Context context) { this(context, null); } public SlideSwitchButton(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public SlideSwitchButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } private void init(Context context) { this.context = context; Resources resources = getResources(); switch_light = BitmapFactory.decodeResource(resources, R.drawable.switch_on); switch_night = BitmapFactory.decodeResource(resources, R.drawable.switch_off); switch_thumb = BitmapFactory.decodeResource(resources, R.drawable.switch_thumb); // 进行缩放 float sx = MyApplication.myApp.screenWidth / switch_light.getWidth() - 1; float sy = 1 + sx / 4; switch_light = BitmapUtil.toScaleBitmap(switch_light, sx, sy); switch_night = BitmapUtil.toScaleBitmap(switch_night, sx, sy); switch_thumb = BitmapUtil.toScaleBitmap(switch_thumb, sy, sy); switch_width = switch_light.getWidth(); switch_height = switch_light.getHeight(); thumb_width = switch_thumb.getWidth(); paint = new Paint(); paint.setColor(Color.GRAY); paint.setTextSize(DensityUtil.dip2px(context, 13)); paint.setTypeface(Typeface.DEFAULT_BOLD); }
该构造方法内初始化了我们需要的图片资源,看一下成员变量:
private Context context;// 上下文 private Bitmap switch_light;// 白天模式的图片 private Bitmap switch_night;// 夜间模式的图片 private String light_text = "白天"; private String night_text = "黑夜"; private static final int SWITCH_LIGHT = 0;// 白天状态 private static final int SWITCH_NIGHT = 1;// 夜间状态 private static final int SWITCH_SCROLL = 2;// 滚动状态 private int status = SWITCH_LIGHT;// 默认处于白天状态 private Paint paint; private Bitmap switch_thumb;// 按钮 private int thumb_width;// 按钮的宽度 private int switch_width;// 开关的宽度 private int switch_height;// 开关的高度 private int left;// 左边界 private int startX;// 按下起点横坐标 private int endX;// 滑动时、离开时横坐标 private boolean actionUp = false;// 手指离开 private OnSwitchListener listener;// 监听器
接下来就是自定义控件的三大方法,先看onMeasure方法:仅仅就是测量一下,可能不需要这个方法
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(switch_width, switch_height); left = switch_width - thumb_width; }
onDraw方法:是该自定义控件最核心方法
根据状态来绘画开关。白天、黑夜这两种比较好画。
滚动时,根据滑块滑动的位置,滑块左边画白天、右边画黑夜。
滑动距离小于指定值时,当做点击事件,开关切换状态。
滑块滑动时,大于指定值时,根据是否超过中间线而松手,超过了则状态切换,没超过则状态不变。
滑动满时,即从左往右滑,滑动了满格时,即一直滑到底,判断滑块位置是否到边界,到达则切换状态。
难点:
canvas.drawBitmap(bitmap,src,dst,paint)方法,src指要裁剪的区域,dst指要放在哪里。一般两个都一样。
屏幕适配问题。drawText(..)
@Override protected void onDraw(Canvas canvas) { if (status == SWITCH_LIGHT) { // 处于白天状态 canvas.drawBitmap(switch_light, 0, 0, paint); canvas.drawBitmap(switch_thumb, 0, 0, paint); canvas.drawText(light_text, thumb_width, DensityUtil.dip2px(context, 16), paint); } else if (status == SWITCH_NIGHT) { // 处于夜间状态 canvas.drawBitmap(switch_night, 0, 0, paint); canvas.drawBitmap(switch_thumb, left, 0, paint); canvas.drawText(night_text, DensityUtil.dip2px(context, 5), DensityUtil.dip2px(context, 16), paint); } else { // 滚动状态 if (actionUp) {//滑块在中间位置的哪边? if (endX < switch_width / 2) { status = SWITCH_LIGHT; } else { status = SWITCH_NIGHT; } actionUp = false; invalidate(); if (listener != null) { listener.onSwitched(this,status == SWITCH_LIGHT); } return; }
//使滑块滑动时手势居于中间 int start = endX - thumb_width / 2; if (start <= 0)// 左边界 start = 0; if (start > left)// 右边界 start = left; // 向右滑,由白天滑向黑夜 // 画黑夜 drawBitmap(canvas, new Rect(0, 0, endX, switch_height), new Rect(0, 0, endX, switch_height), switch_night); // 画白天 drawBitmap(canvas, new Rect(endX, 0, switch_width, switch_height), new Rect(endX, 0, switch_width, switch_height), switch_light); // TODO:滚动时画字,跟随着消息、展现 // canvas.drawText(night_text, 5, 16, paint); // canvas.drawText(light_text, thumb_width, 16, paint); // 画按钮 canvas.drawBitmap(switch_thumb, start, 0, paint); if (start == left) {// 已经滑到底 status = SWITCH_NIGHT; if (listener != null) { listener.onSwitched(this,false); } } else if (start == 0) { status = SWITCH_LIGHT; if (listener != null) { listener.onSwitched(this,true); } } } }
/** * 使用canvas画图,dst表示要显示在哪,src表示要切割的区域 * * @param canvas * @param src * @param dst * @param bitmap */ public void drawBitmap(Canvas canvas, Rect src, Rect dst, Bitmap bitmap) { dst = (dst == null ? new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()) : dst); canvas.drawBitmap(bitmap, src, dst, paint); }
onTouch方法:判断手指位置,不断重绘
判断点击事件
判断当前状态,是白天就不要再往白天滑,是黑夜就不要再往黑夜滑。
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: startX = (int) event.getX(); break; case MotionEvent.ACTION_MOVE: endX = (int) event.getX(); if (Math.abs(startX - endX) < 8) { return true; } if (startX < endX) { // 向右滑,由白天滑向黑夜 if (startX > switch_night.getWidth() - thumb_width) {// 已经是黑夜 status = SWITCH_NIGHT; return true; } } else { // 向左滑,由黑夜滑向白天 if (startX < thumb_width) {// 已经是白天 status = SWITCH_LIGHT; return true; } } status = SWITCH_SCROLL; break; case MotionEvent.ACTION_UP: endX = (int) event.getX(); if (Math.abs(startX - endX) < 8) { // 作为点击事件,切换状态, status = Math.abs(status - 1); if (listener != null) { listener.onSwitched(this,status == SWITCH_LIGHT); } break; } actionUp = true; break; } invalidate(); return true; }
设置监听器,比较简单啦:
/** * 设置监听器 * * @param listener */ public void setOnSwitchListener(OnSwitchListener listener) { this.listener = listener; } /** * 自定义接口回调 * * @author baiiu * */ public interface OnSwitchListener { /** * * @param slideSwitch * 本开关对象 * @param isLight * true,表示于白天。 false表示夜晚 */ public abstract void onSwitched(SlideSwitchButton slideSwitch, boolean isLight); }
设置方法,供用户调用:
/** * 设置开关状态,true表示白天 */ public void setSwitch(boolean isLight) { status = isLight ? SWITCH_LIGHT : SWITCH_NIGHT; }
标签:
原文地址:http://www.cnblogs.com/baiiu/p/4297409.html