android自定义选择开关按钮,能够实现滑动改变状态操作,点击改变状态操作,对外提供监听器可以捕获改变后的状态和当前状态的名称,可以在xml中指定初始时的状态,也可以在代码中动态设置开关的状态
具体的效果图如下(开关下方是textview用于显示改变后的状态和状态的文字名称)
(1)关闭状态下
(2)开启状态
(3)滑动过程中
滑动过程中不响应状态改变,监听器不会有反应,只有松开按钮时才会响应:当开关滑动的距离大于宽度的1/2时状态才会发生变化,否则会重新滑动回原来的状态
相关的代码(只贴相对重要的代码,具体的工程源代码会上传到我的资源中去,需要的可以自己去下载)
view的onDraw()方法中分两种情况进行绘制
分四个区域绘制:左边半圆形,右边半圆形,中间的矩形其实是分两段的这么做主要是为了处理滑动过程中颜色填充的问题(左边显示一种色调,右边显示一种)
开关当前处于开启状态时绘制:根据movX判断移动的距离绘制圆形按钮的所在位置
if (state){//开启状态 RadioX=canvas.getWidth() - height / 2; if (movX<=0&&movX>height-canvas.getWidth()){ paint.setColor(switchColorTrue); } else { paint.setColor(switchColorFalse); } RectF rectleft=new RectF(0,0,height,height); canvas.drawArc(rectleft,90,180,true,paint); RectF rectRight=new RectF(canvas.getWidth()-height,0,canvas.getWidth(),height); if (movX==0){ paint.setColor(switchColorTrue); } else { paint.setColor(switchColorFalse); } canvas.drawArc(rectRight, 270, 180, true, paint); //右矩形 paint.setColor(switchColorFalse); canvas.drawRect(canvas.getWidth() - height / 2 + movX, 0, canvas.getWidth() - height / 2+1, height, paint); //左矩形 // if (movX<=3*height/2-canvas.getWidth()){ // paint.setColor(switchColorFalse); // } // 3*height/2-canvas.getWidth(); // else { // paint.setColor(switchColorTrue); // } paint.setColor(switchColorTrue); canvas.drawRect(height/2, 0, canvas.getWidth() - height/2+movX, height, paint); paint.setColor(ballColor); canvas.drawCircle(RadioX+movX, height / 2, height / 2-1, paint); paint.setColor(textColor); paint.setTextSize(textSize); String text=""; Typeface typeface= Typeface.create(Typeface.DEFAULT_BOLD,Typeface.BOLD); paint.setTypeface(typeface); paint.setTextAlign(Paint.Align.CENTER); Paint.FontMetrics metrics=paint.getFontMetrics(); float des=metrics.descent; float asent=metrics.ascent; float he=(des+asent)/2; if (movX<=-height/2){ text=falseText; canvas.drawText(text,0,text.length(),(canvas.getWidth()-height)/2+height, canvas.getHeight()/2-he,paint); }//闭合状态下的绘制思路大致与开启时相同,只是初始的圆心坐标不一样,就不再贴出来了
2.重写onTouchEvent响应滑动以及点击事件
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: movX=0; eveX=event.getX(); float eventY=event.getY(); if (eveX>=0&&eveX<=getWidth()&&eventY>=0&&eventY<=getHeight()){ isMove=true; } else { isMove=false; } Log.i("isMove======",""+isMove); break; case MotionEvent.ACTION_MOVE: if (isMove){//按下去的点在当前空间范围内响应操作 movX=event.getX()-eveX;//X轴上移动的距离 if (state){//开启状态 if (movX<=0){ if (movX<height-getWidth()){ movX=height-getWidth(); } } else { movX=0; } } else {//关闭状态 if (movX>=0){ if (movX>getWidth()-height){ movX=getWidth()-height; } } else { movX=0; } } } invalidate(); break; 在滑动和点击过程中不响应状态改变,只有松开时对状态判断然后响应状态改变 如果是滑动后抬起,对移动的距离和抬起的位置进行判断,然后决定是否改变开关状态 case MotionEvent.ACTION_UP: // 处理点击事件 float X=event.getX(); float Y=event.getY(); float mvX=X-eveX; movX=0; if (isMove){//滑动事件 isMove=false; if (state){//开启状态 if (mvX<=-height/2){ state=!state; //关闭状态 if (listener!=null){ listener.change(state,falseText); } } else if (mvX==0){//点击事件 state=!state; //关闭状态 if (listener!=null){ if (state){ listener.change(state,trueText);//监听器响应监听回调 } else { listener.change(state,falseText); } } } } else {//关闭状态 if (mvX>=height/2){ state=!state; if (listener!=null){ listener.change(state,trueText); } } else if (mvX==0){//点击事件 state=!state; //关闭状态 if (listener!=null){ if (state){ listener.change(state,trueText); } else { listener.change(state,falseText); } } } } invalidate(); } break; } return true; }
监听器:实现的原理就是接口回调,定义一个接口在该类中由接口的实现对象进行响应操作,然后该对象获取到操作时的参数(其实就是虚拟一个不存在的实例化该接口的对象,由该对象调用相关的方法,然后该对象获取到需要的数据)
public void setSwitchChangerListener(switchChangeListener li){ this.listener=li; } interface switchChangeListener{ void change(Boolean sta,String text); }