這幾天做自定義控件,發現一種很常用的開關按鈕,效果圖如下:
點擊或者拖拽按鈕可以實現“開”、“關”兩種狀態的切換,它的實現原理非常簡單:
一個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事件。