Android自定義開關按鈕

這幾天做自定義控件,發現一種很常用的開關按鈕,效果圖如下:

                                        

點擊或者拖拽按鈕可以實現“開”、“關”兩種狀態的切換,它的實現原理非常簡單:

一個View對象展示在手機屏幕上的時候,它一般會執行以下幾個步驟:

1)調用構造方法,創建對象 

2)測量view的大小,調用onMeasure方法

3)確定view的位置,view自身有一定的建議權,但是決定權在父view手中, onLayout方法 

4)繪製view,onDrow方法

依據view對象的執行步驟,我們分別重寫view對象的這些方法,然後依次去實現。直接上代碼

/**
 * 自定義的開關按鈕 請訪問 http://blog.csdn.net/qq_20889581?viewmode=contents 閱讀詳細信息
 * 
 * @author Administrator
 * 
 */
public class MyToggleButton extends View implements OnClickListener {
	// 作爲開關按鈕的背景圖片
	private Bitmap backgroundBitmap;
	// 可以滑動或點擊的按鈕
	private Bitmap slide_button;
	private Paint paint;
	// 當前按鈕的開關狀態
	private boolean currState = false;
	// 滑動的按鈕圖片左邊的距離
	private float slide_btn_left;
	// 手指第一次觸摸屏幕的時候的X值,也就是down的時候的x值
	private float firstX;
	// 是否發生拖拽
	private boolean isDrag = false;
	private float lastX;

	/**
	 * 在代碼中new對象的時候,使用此構造
	 * 
	 * @param context
	 */
	public MyToggleButton(Context context) {
		super(context);
		initView();
	}

	/**
	 * 在佈局文件中聲明的view,創建時,由系統調用此構造
	 * 
	 * @param context
	 *            上下文
	 * @param attrs
	 *            屬性集
	 */
	public MyToggleButton(Context context, AttributeSet attrs) {
		super(context, attrs);
		initView();
	}

	/**
	 * 完成一些初始化的操作
	 */
	private void initView() {
		backgroundBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.background);
		slide_button = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button);
		paint = new Paint();
		// 打開抗矩齒
		paint.setAntiAlias(true);
		setOnClickListener(this);
	}

	/**
	 * 一個View對象顯示在屏幕上需要完成的工作如下: 1、調用構造方法,創建對象 2、測量view的大小,調用onMeasure方法
	 * 3、確定view的位置,view自身有一定的建議權,但是決定權在父view手中, onLayout方法 4、繪製view,onDrow方法
	 */

	/**
	 * 測量view大小的時候的回調,設置當前view的大小 width :view的寬度 height :view的高度 (單位:像素)
	 */
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		setMeasuredDimension(backgroundBitmap.getWidth(), backgroundBitmap.getHeight());
	}

	// 確定位置的時候調用此方法
	// 自定義view的時候,作用不大
	// @Override
	// protected void onLayout(boolean changed, int left, int top, int right,
	// int bottom) {
	// super.onLayout(changed, left, top, right, bottom);
	// }

	@Override
	protected void onDraw(Canvas canvas) {
		// super.onDraw(canvas);
		// 繪製 背景
		/*
		 * canvas 畫布 bitmap 要繪製的圖片 left 圖片的左邊屆 top 圖片的上邊屆 paint 繪製圖片要使用的畫筆
		 */
		canvas.drawBitmap(backgroundBitmap, 0, 0, paint);
		// 繪製可滑動的按鈕圖片
		canvas.drawBitmap(slide_button, slide_btn_left, 0, paint);

	}

	@Override
	public void onClick(View v) {
		// 點擊的時候,更改滑動按鈕的開關狀態
		if (!isDrag) {
			currState = !currState;
			// 刷新開關狀態
			flushState();
		}
	}

	/**
	 * 要想實現拖拽的效果,需要重寫手機屏幕的onTouchEvent方法
	 */
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {

		case MotionEvent.ACTION_DOWN:
			// 手指按下
			// 獲得手指在屏幕上的位置,這裏只需要X座標
			firstX = lastX = event.getX();// 記錄下當前的最開始在屏幕的值
			isDrag = false;// down的時候沒有發生拖拽
			break;

		case MotionEvent.ACTION_MOVE:
			// 手指移動,表示發生了拖拽
			// 判斷是否發生了拖拽(移動了5個像素表示發生了拖拽)
			if (Math.abs(event.getX() - firstX) > 5) {
				isDrag = true;
			}
			// 計算手指在屏幕上移動的距離
			float dis = event.getX() - lastX;
			// 手指移動過程中獲得的x座標就是最後的位置
			lastX = event.getX();
			// 設置滑動按鈕左邊的邊界
			slide_btn_left = slide_btn_left + dis;
			break;
		case MotionEvent.ACTION_UP:
			// 手指擡起
			lastX = event.getX();
			if (lastX - firstX == 0) {
				// 這是點擊事件
				if (!isDrag) {
					currState = !currState;
					// 刷新開關狀態
					flushState();
				}
			} else {
				if (isDrag) {
					// slideBtn左邊屆最大值
					int maxLeft = backgroundBitmap.getWidth() - slide_button.getWidth();
					if (slide_btn_left > maxLeft / 2) {
						// 爲打開狀態
						currState = true;
					} else {
						currState = false;
					}
					flushState();
				}
			}

			break;
		}
		// 刷新試圖
		flushView();
		return true;
	}

	/**
	 * 刷新開與關的兩個狀態
	 */
	private void flushState() {
		// currState表示當前的開關狀態
		if (currState) {
			// 如果是開的狀態
			slide_btn_left = backgroundBitmap.getWidth() - slide_button.getWidth();
		} else {
			slide_btn_left = 0;
		}
		// 刷新當前視圖
		flushView();

	}

	private void flushView() {
		/*
		 * 對 slide_btn_left 的值進行判斷 ,確保其在合理的位置 即 0<=slide_btn_left <= maxLeft
		 */

		int maxLeft = backgroundBitmap.getWidth() - slide_button.getWidth(); // slideBtn
																				// 左邊屆最大值

		// 確保 slideBtn_left >= 0
		slide_btn_left = (slide_btn_left > 0) ? slide_btn_left : 0;

		// 確保 slideBtn_left <=maxLeft
		slide_btn_left = (slide_btn_left < maxLeft) ? slide_btn_left : maxLeft;
		// 刷新當前視圖,導致onDraw方法執行
		invalidate();
	}

}
值得注意的是,onClick方法和onTouchEvent方法會衝突,也就是說onClick方法不會執行,爲了解決這個問題,在down和up的時候分別記錄下手指在屏幕的位置,如果up的時候的位置相對於down的位置沒有改變,則認爲是執行了onClick事件。

點我下載源碼



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章