Android之自定义开关控件

先看效果图咯~


我们可以看到一个很常见的开关按钮,那就来分析一下。

这是由两张图片构成的:

①一张为有开和关的背景图片

②一张为控制开和关的滑动按钮图片

代码编写的步骤:

1.写个类继承View,并重写几个方法:

第一个为构造函数,重写一个参数的函数和两个参数的函数就够了,因为两个参数的函数能够使用自定义属性

第二个为控制控件的大小–>protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {}

第三个为绘制控件的方法–>protected void onDraw(Canvas canvas) {}

2.将用户指定的两张图片加载进来,写俩个方法--setToggleBackground和setToggleSolid方法:

       //设置背景图片
	public void setToggleBackground(int resId){
		toggleBackground=BitmapFactory.decodeResource(getResources(), resId);
	}
	//设置滑块图片
	public void setToggleSolid(int resId){
		toggleSolid=BitmapFactory.decodeResource(getResources(), resId);
	}
3. onMeasure方法来控制控件的大小,我们可以看到这个开关按钮的大小就跟背景的大小一样大,只需要设置为背景的大小:

	//控制控件的大小
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		// TODO Auto-generated method stub
		
		if(toggleBackground!=null){
			//指定控件宽高
			setMeasuredDimension(toggleBackground.getWidth(), toggleBackground.getHeight());
		}else{
			super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		}
	}
4.既然这个开关按钮需要通过点击或移动来控制控件的开和关,所以就需要实现 onTouchEvent方法,当然应该有三个事件会触发:按下、移动和抬起的时候,每次点击、移动或抬起都需要重绘,我们先来分析下滑块的状态有哪些(应该有四种状态)一开始默认状态为空:

1.点击的时候
2.移动的时候
3.抬起的时候
4.空的时候(即什么都没干的时候)

先分析下点击的时候的情况:

①当按下或移动的坐标大于滑块宽度一半时将滑块右移

②当按下或移动的坐标小于滑块宽度一半时滑块不动

注:防止滑块移至背景外面,最大是滑块右边和背景右边对齐(即最大剩余宽度等于滑块离左边-为背景宽度-滑块宽度)

再来分析移动的时候的情况,可以发现应该是和点击的时候是一样的。

再来分析抬起的时候的情况:

①如果开关状态是打开的就将滑块移动至右边

②如果开关状态是关闭的就将滑块移动至左边

那怎么判断什么时候是打开状态和关闭状态呢??

①抬起的坐标大于背景宽度一半的时候设为打开状态

②抬起的坐标小于背景宽度坐标一 半的时候设为关闭状态

再来分析下空的时候,可以发现它和抬起的时候的情况是一样的。

5.在onDraw方法中将背景和滑块绘制出来。刚才分析了onTouchEvent方法,这次是一样的,滑块的四个状态分别处理,前面onTouchEvent方法中滑块的状态改变,然后通过invalidate()方法来通知系统重绘。

        //控制控件的样式
	@Override
	protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		if(toggleBackground!=null){
			canvas.drawBitmap(toggleBackground, 0, 0, paint);
		}
		
		switch (state) {
		case DOWN:
		case MOVE:
			if(currentX>toggleSolid.getWidth()/2f){
				//让滑块向右移动(重新绘制滑块的位置)
				leftX=currentX-toggleSolid.getWidth()/2f;
				maxLeft=toggleBackground.getWidth()-toggleSolid.getWidth();
				if(leftX>maxLeft){
					leftX=maxLeft;
				}
				canvas.drawBitmap(toggleSolid, leftX, 0, paint);
			}else{
				//滑块不动
				canvas.drawBitmap(toggleSolid, 0, 0, paint);
			}
			break;
		case NONE:
		case UP:
			if(isOpen){
				canvas.drawBitmap(toggleSolid, maxLeft, 0, paint);
			}else {
				canvas.drawBitmap(toggleSolid, 0, 0, paint);
			}
			break;
		
		}
	}


6.我们做这个自定义控件是为了让用户使用的,现在这个是没有什么用的,用户用不了的,所以可以通过设置监听器来对外提供接口。

        onToggleListener listener;
	public void setOnToggleListener(onToggleListener listener){
		this.listener=listener;
	}
	//接口
	public interface onToggleListener{
		void onToggleChanged(boolean isOpen);
	}
这个监听器中的boolean值需要赋值,那在什么时候赋值呢,应该是在抬起或空的状态的时候给它赋值,因为那个时候才真正确定开关按钮是打开的还是关闭的。

7.在布局文件中把自定义的这个控件定义出来~

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:orientation="vertical">
    <com.example.zidingyi.ToggleVie
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/toggle">
        </com.example.zidingyi.ToggleVie>     
</LinearLayout>
8.自定义的View-ToggleVie:

public class ToggleVie extends View{
	
	public ToggleVie(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
	}
	public static final int DOWN=1;
	public static final int MOVE=2;
	public static final int UP=3;
	public static final int NONE=0;
	
	//标记状态
	private int state=NONE;
	
	boolean isOpen;

	private Bitmap toggleBackground,toggleSolid;
	float currentX,leftX,maxLeft;
	private Paint paint=new Paint();
	//控制控件的大小
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		// TODO Auto-generated method stub
		
		if(toggleBackground!=null){
			//指定控件宽高
			setMeasuredDimension(toggleBackground.getWidth(), toggleBackground.getHeight());
		}else{
			super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		}
	}
	//控制控件的样式
	@Override
	protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		if(toggleBackground!=null){
			canvas.drawBitmap(toggleBackground, 0, 0, paint);
		}
		
		switch (state) {
		case DOWN:
		case MOVE:
			if(currentX>toggleSolid.getWidth()/2f){
				//让滑块向右移动(重新绘制滑块的位置)
				leftX=currentX-toggleSolid.getWidth()/2f;
				maxLeft=toggleBackground.getWidth()-toggleSolid.getWidth();
				if(leftX>maxLeft){
					leftX=maxLeft;
				}
				canvas.drawBitmap(toggleSolid, leftX, 0, paint);
			}else{
				//滑块不动
				canvas.drawBitmap(toggleSolid, 0, 0, paint);
			}
			break;
		case NONE:
		case UP:
			if(isOpen){
				canvas.drawBitmap(toggleSolid, maxLeft, 0, paint);
			}else {
				canvas.drawBitmap(toggleSolid, 0, 0, paint);
			}
			break;
		
		}
	}
	
	public ToggleVie(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}
   //设置背景图片
	public void setToggleBackground(int resId){
		toggleBackground=BitmapFactory.decodeResource(getResources(), resId);
	}
	//设置滑块图片
	public void setToggleSolid(int resId){
		toggleSolid=BitmapFactory.decodeResource(getResources(), resId);
	}
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		// TODO Auto-generated method stub
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			
			state=DOWN;//修改标记位
			
			//通知界面重新绘制
			invalidate();
//			postInvalidate();  //在子线程的
			break;
		case MotionEvent.ACTION_UP:
			currentX=event.getX();
			state=UP;
			if(currentX>toggleBackground.getWidth()/2f){
				//滑块在右边
				isOpen=true;
				if(listener!=null){
					listener.onToggleChanged(true);
				}
			}else{
				//滑块在左边
				isOpen=false;
				if(listener!=null){
					listener.onToggleChanged(false);
				}
			}
			invalidate();
			break;
		case MotionEvent.ACTION_MOVE:
			currentX=event.getX();
			state=MOVE;
			invalidate();
			break;
			
		default:
			break;
		}
		return true;
	}
	onToggleListener listener;
	public void setOnToggleListener(onToggleListener listener){
		this.listener=listener;
	}
	//接口
	public interface onToggleListener{
		void onToggleChanged(boolean isOpen);
	}
}
9.MainActivity:

public class MainActivity extends Activity {
	private ToggleVie toggle;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		toggle=(ToggleVie) findViewById(R.id.toggle);
		toggle.setToggleBackground(R.drawable.switch_background);
		toggle.setToggleSolid(R.drawable.slide_button_background);
		
		toggle.setOnToggleListener(new onToggleListener() {
			
			@Override
			public void onToggleChanged(boolean isOpen) {
				// TODO Auto-generated method stub
				if(isOpen){
					Toast.makeText(MainActivity.this, "开启", 0).show();
				}else {
					Toast.makeText(MainActivity.this, "关闭", 0).show();
				}
			}
		});
	}
}

源码








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值