码迷,mamicode.com
首页 > 其他好文 > 详细

自定义控件,可拖动的开关:SlideSwitch

时间:2015-02-22 13:26:01      阅读:362      评论:0      收藏:0      [点我收藏+]

标签:

可以拖动的开关,开关滑块随着手指拖动而变化。

  使用 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;
    }

 

自定义控件,可拖动的开关:SlideSwitch

标签:

原文地址:http://www.cnblogs.com/baiiu/p/4297409.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!