注:源自傳智播客視訊教程
現整理此案例,以供日後自己或大家學習、參考:
android自定義view實作的簡單demo
實作效果:
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>