天天看點

自定義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>