天天看点

自定义view_开关按钮

注:源自传智播客视频教程

现整理此案例,以供日后自己或大家学习、参考:

android自定义view实现的简单demo

实现效果:

自定义view_开关按钮
自定义view_开关按钮
自定义view_开关按钮
自定义view_开关按钮
自定义view_开关按钮
自定义view_开关按钮

1.点击按钮可以改变开关的状态

2.拖动按钮可以改变开关的状态

此为完整代码下载链接:

http://download.csdn.net/detail/wang725/8734937

1.新建一个实现View的MyToggleButton类

<pre name="code" class="java">package com.example.togglebtn;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;

public class MyToggleButton extends View implements OnClickListener {

	/**
	 * 作为背景的图片
	 */
	private Bitmap backgroudBitmap;

	/**
	 * 可以滑动的图片
	 */
	private Bitmap slideBtm;

	private Paint paint;

	/**
	 * 滑动按钮的左边值
	 */
	private float slideBtn_left;
	
	/**
	 * @param context
	 * @param attrs
	 * @param defStyleAttr
	 */
	public MyToggleButton(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		// TODO Auto-generated constructor stub
	}

	/**
	 * 在布局文件中声明的view,创建时系统自动调用
	 * @param context
	 * @param attrs
	 */
	public MyToggleButton(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
	}

	/**
	 * 在代码里面创建对象的时候使用此构造方法
	 * @param context
	 */
	public MyToggleButton(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}
	
	/**
	 * view对象显示在屏幕上 有几个重要步骤
	 * 1、构造方法创建对象
	 * 2、测量view的大小 onMeasure
	 * 3、确定view的位置,view自身有一些建议权,决定权在父view手中 onLayout();
	 * 4、绘制view的内容 onDraw(Canvas);
	 */
	
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		initView();
		/**
		 * 设置当前view的大小
		 * width : 当前view的宽度(单位:像素)
		 * height : 当前view的高度(单位:像素)
		 */
		setMeasuredDimension(backgroudBitmap.getWidth(), backgroudBitmap.getHeight());
	}

	/**
	 * 自定义view的时候 作用不大
	 * 确定位置的时候调用此方法
	 */
//	@Override
//	protected void onLayout(boolean changed, int left, int top, int right,
//			int bottom) {
//		super.onLayout(changed, left, top, right, bottom);
//	}

	/** 
	 * 当前开关状态
	 */
	private boolean currentState = false;
	
	/**
	 * 绘制当前view的内容
	 */
	@Override
	protected void onDraw(Canvas canvas) {
//		super.onDraw(canvas);
		// 绘制背景
		/**
		 * backgroundBitmap 要绘制的图片
		 * left 图片的左边界
		 * top 图片的上边届
		 * paint 绘制图片使用的画笔
		 */
		canvas.drawBitmap(backgroudBitmap, 0, 0, paint);
		// 绘制可滑动的按钮
		canvas.drawBitmap(slideBtm, slideBtn_left, 0, paint);
	}
	
	/**
	 * 初始化
	 */
	private void initView() {
		backgroudBitmap  = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background);
		slideBtm = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button);
		
		// 画笔
		paint = new Paint();
		paint.setAntiAlias(true);// 打开抗锯齿
		
		// 添加点击事件监听
		setOnClickListener(this);
	}

	/**
	 * 判断是否发生拖动 
	 * 若拖动了,就不再响应onlick事件
	 */
	private boolean isDrag = false;
	
	/**
	 * 点击事件
	 * onclick事件在view.onTouchEvent事件中被解析
	 * 系统对onclick事件的解析 过于简陋 只要有down时间 和 up事件 系统即认为发生了click事件
	 */
	@Override
	public void onClick(View v) {
		// 若果没有拖动才执行改变状态的动作
		if(!isDrag) {
			currentState = !currentState;
			flushState();
		}
	}

	/**
	 * 刷新当前状态
	 */
	private void flushState() {
		if(currentState) {
			slideBtn_left = backgroudBitmap.getWidth() -  slideBtm.getWidth();
		} else {
			slideBtn_left = 0;
		}
		/**
		 * 刷新当前view 会导致onDraw方法的执行
		 * Invalidate the whole view. If the view is visible,
		 *	onDraw(android.graphics.Canvas) will be called at some point in the future. This must be called from a UI thread. 
		 *	To call from a non-UI thread, call postInvalidate().
		 */
		invalidate();
	}
	
	/**
	 * down 事件时的x值
	 */
	private int firstX;

	/**
	 * touch 事件的上一个x的值
	 */
	private int lastX;
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		super.onTouchEvent(event);
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			firstX = lastX = (int) event.getX();
			isDrag =false;
			break;
			
		case MotionEvent.ACTION_MOVE:
			// 判断是否发生拖动
			if(Math.abs(event.getX() - firstX) > 5) {
				isDrag = true;
			}
			
			// 计算手指在屏幕上移动的距离
			int dis = (int) (event.getX() - lastX);
			// 将本次的位置 设置给lastX
			lastX = (int) event.getX();
//			if(lastX >= backgroudBitmap.getWidth() - )

			// 根据手指移动的距离,改变图片slideBtn_left的位置
			slideBtn_left = slideBtn_left + dis;
			break;
		case MotionEvent.ACTION_UP:
			// 在发生拖动的情况下,根据最后的位置,判断当前开关的状态
			if(isDrag) {
				if(slideBtn_left > (backgroudBitmap.getWidth() - slideBtm.getWidth())/2) {
					slideBtn_left = backgroudBitmap.getWidth() - slideBtm.getWidth();
					currentState = true;
				} else {
					slideBtn_left = 0;
					currentState = false;
				}
			}
			break;
		default:
			break;
		}
		flushView();
		return true;
	}
	
	/**
	 * 刷新当前view
	 */
	private void flushView() {
		/**
		 * 对slideBtn_left 的值进行判断,确保其在河里的位置  0 <= slideBtn_left <= maxLeft
		 */
		int maxLeft = backgroudBitmap.getWidth() - slideBtm.getWidth();
		// 1 确保slideBtn_left 大于0
		slideBtn_left = (slideBtn_left>0)?slideBtn_left:0;
		// 2 确保slideBtn_left 小于 maxLeft
		slideBtn_left = (slideBtn_left<maxLeft)?slideBtn_left:maxLeft;
		// 刷新当前视图 导致 onDraw方法执行
		invalidate();
	}
}
           

2.activity_main.xml,在不居中引用自定义的view

<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.togglebtn.MainActivity" >

    <com.example.togglebtn.MyToggleButton
        android:id="@+id/mytogglebtn"
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</RelativeLayout>