自定义View之自定义开关

看了自定义view的教学视频,今天来总结一下

首先来看一下效果

这是开的效果

自定义View之自定义开关


这是关的效果

自定义View之自定义开关


接下来看看代码:

1.先把开关图形弄出来

首先这是自定义view中的继承自view的一种,所以我们要新建一个类继承自view,重写里面的构造方法,构造方法有几个,这里我们先选两个参数的,这个构造方法会在我们在布局文件中生成时调用,同时我们可以在里面写一些初始化的工作

switchBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background);
scrollBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button);
scrollMax = switchBitmap.getWidth() - scrollBitmap.getWidth();
paint = new Paint();
paint.setAntiAlias(true);//抗锯齿
这里我们两个资源文件作为背景,因为我们的自定义开关是这样的,一张开和关的背景图,然后一张滑动的开关图覆盖在上面,当我们点击或者拖动的时候就会动起来,所以我们这里先把这两个资源文件转化成bitmap

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawBitmap(switchBitmap, 0, 0, paint);
    canvas.drawBitmap(scrollBitmap, scrollLeft, 0, paint);
}
然后在ondraw方法中画出来,注意我们要滑动就需要上面的图片动,所以这里的left应该是一个变量。这时运行程序,可以发现开关的图形已经出来了,只是现在点击或者滑动还没有反应

2.点击切换开关

我们需要点击开关时开关能够切换,所以我们需要在点击事件中来处理,定义一个boolean型变量来控制开和关

this.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        if (!isScroll){
            if (!isOpen) {
                scrollLeft = scrollMax;
                isOpen = true;
            } else {
                scrollLeft = 0;
                isOpen = false;
            }
            invalidate();
        }
    }
});
当开关是开时就关,关时就开,改变我们刚才设置的left变量,然后invalidate去重新绘制画面


3.滑动切换开关

要滑动的话,我们自然就要想到触摸事件了,这时我们重写onTouchEvent方法,代码如下:

@Override
public boolean onTouchEvent(MotionEvent event) {
    super.onTouchEvent(event);
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            startX = event.getX();
            isScroll = false;
            break;
        case MotionEvent.ACTION_MOVE:
            isScroll = true;
            float moveX = event.getX();
            float dx = moveX - startX;
            startX = moveX;
            scrollLeft += dx;

            if (scrollLeft <= 0) {
                scrollLeft = 0;
            } else if (scrollLeft >= scrollMax) {
                scrollLeft = scrollMax;
            }
            break;
        case MotionEvent.ACTION_UP:
            if (isScroll) {      //如果当前事件为滑动事件,那么就执行up
                if (scrollLeft <= scrollMax / 2) {
                    scrollLeft = 0;
                    isOpen = false;
                } else if (scrollLeft > scrollMax / 2) {
                    scrollLeft = scrollMax;
                    isOpen = true;
                }
            }
            break;
    }
    invalidate();
    return true;
}

这里有一些其余的代码,因为当你仅仅只做了滑动事件之后,你会发现存在着bug,因为此时的开关是可以滑到中间的位置的,显然这是不符合常理的,所以我们要设置,当left小于最大滑动的2/1时,把scrollLeft设置为0(关的状态),否则就设置为最大滑动状态(开的状态),当然不要忘了boolean值也要变,此时运行程序,会发现并不能实现想要的效果,为什么呢?因为触摸事件和点击事件发生了冲突了,正常来说当我们down的时候会触发down的事件然后up时再触发up的事件,最后执行onclick事件,所以程序就出现了bug,那么要怎样解决这个bug?这里可以用一个boolean值isScroll来解决(以后我们遇到类似的冲突问题也可以这样做),当我们down的时候把isScroll = false,如果我们滑动了,就是调用了move方法,那么在这方法中再把isscroll = true,表示正在滑动的状态,最后up时就判断状态是否为true,如果是就执行up里面的代码,前面说了执行完up后就执行onclick事件,此时我们在onclick事件中判断状态是否为滑动,不是的话就执行。


4.设置接口

这里没有写出代码,其实就是定义一个接口,然后外部再去实现其接口的具体方法就可以了。