Android自定義View-SwitchView(自定義開關)
Android自定義開關,效果圖如圖:
一個是關的狀態,一個是開的狀態,通過監聽回調將滑塊滑動狀態返回到調用界面。
實質上底層是一個背景,然後在這個背景之上增加了一個可移動滑塊,通過判斷滑塊滑動之後的位置,在背景圖中心點左邊表示開關是開,在右邊表示是關。
1.自定義view類
SwitchView:主要是用來繪製開關背景及滑塊,並通過滑塊移動位置來判斷狀態。
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.util.Log;
import android.view.MotionEvent;
import android.view.View;
/**
* @author M.xang
* @時間 2019年4月22日
* @描述 自定義開關控件
*/
public class SwitchView extends View {
private Bitmap switchBackgroupBitmap = null; // 背景圖片
private Bitmap slideButtonBitmap = null; // 滑塊圖片
private Paint paint = null; // 畫筆
private boolean mSwitchState = false; // 開關狀態, 默認false
private float currentX;
private int width_bg = 0, height_bg = 0;
@SuppressWarnings("unused")
private int width_slide = 0, height_slide = 0;
/**
* 用於代碼創建控件
*
* @param context
*/
public SwitchView(Context context) {
super(context);
init();
}
/**
* 用於在xml裏使用, 可指定自定義屬性
*
* @param context
* @param attrs
*/
public SwitchView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
// 獲取配置的自定義屬性
setSwitchBackgroundResource(R.drawable.switch_bg);// 開關背景
setSlideButtonResource(R.drawable.switch_control);// 滑塊背景
}
private void init() {
paint = new Paint();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 依據背景圖片來確定控件大小
width_bg = switchBackgroupBitmap.getWidth();
height_bg = switchBackgroupBitmap.getHeight();
if (slideButtonBitmap != null) {
width_slide = slideButtonBitmap.getWidth();
height_slide = slideButtonBitmap.getHeight();
}
setMeasuredDimension(width_bg, height_bg);
}
// Canvas 畫布, 畫板. 在上邊繪製的內容都會顯示到界面上.
@Override
protected void onDraw(Canvas canvas) {
// 1. 繪製背景
canvas.drawBitmap(switchBackgroupBitmap, 0, 0, paint);
// 2. 繪製滑塊
if (isTouchMode) {
// 根據當前用戶觸摸到的位置畫滑塊
// 讓滑塊向左移動自身一半大小的位置
float newLeft = currentX - width_slide / 2.0f;
int maxLeft = width_bg - width_slide;
// 限定滑塊範圍
if (newLeft < 0) {
newLeft = 0; // 左邊範圍
} else if (newLeft > maxLeft) {
newLeft = maxLeft; // 右邊範圍
}
canvas.drawBitmap(slideButtonBitmap, newLeft, 0, paint);
} else {
// 根據開關狀態boolean, 直接設置圖片位置
if (mSwitchState) {// 開,開關滑塊置於最右邊,顯示ON
canvas.drawBitmap(slideButtonBitmap, 0, 0, paint);
} else {// 關,開關滑塊置於最左邊,顯示OFF
int newLeft = width_bg - width_slide;
if (slideButtonBitmap != null) {
canvas.drawBitmap(slideButtonBitmap, newLeft, 0, paint);
}
}
}
}
boolean isTouchMode = false;
private OnSwitchStateUpdateListener onSwitchStateUpdateListener;
// 重寫觸摸事件, 響應用戶的觸摸.
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isTouchMode = true;
currentX = event.getX();
break;
case MotionEvent.ACTION_MOVE:
currentX = event.getX();
break;
case MotionEvent.ACTION_UP:
isTouchMode = false;
currentX = event.getX();
float center = width_bg / 2.0f;
// 根據當前按下的位置, 和控件中心的位置進行比較.
boolean state = currentX < center;// 離開位置小於中心位置,點擊了關,true
// 如果開關狀態變化了, 通知界面. 裏邊開關狀態更新了.
if (state != mSwitchState && onSwitchStateUpdateListener != null) {
// 把最新的boolean, 狀態傳出去了
onSwitchStateUpdateListener.onStateUpdate(state);
}
mSwitchState = state;
break;
}
// 重繪界面
invalidate(); // 會引發onDraw()被調用, 裏邊的變量會重新生效.界面會更新
return true; // 消費了用戶的觸摸事件, 纔可以收到其他的事件.
}
/**
* 設置背景圖
*
* @param switchBackground
*/
public void setSwitchBackgroundResource(int switchBackground) {
if (switchBackground > 0) {
switchBackgroupBitmap = BitmapFactory.decodeResource(getResources(), switchBackground);
} else {
Log.e("---SlideSwitchView--->", "沒有設置開關背景圖,應設置app:switch_background=\"@drawable/xxx\"");
}
}
/**
* 設置滑塊圖片資源
*
* @param slideButton
*/
public void setSlideButtonResource(int slideButton) {
if (slideButton > 0) {
slideButtonBitmap = BitmapFactory.decodeResource(getResources(), slideButton);
} else {
Log.e("---SlideSwitchView--->", "沒有設置滑動按鈕圖,應設置app:slide_button=\"@drawable/xxx\"");
}
}
/**
* 設置開關初始狀態
*
* @param mSwitchState true:默認開,false:默認關
*/
public void setSwitchState(boolean mSwitchState) {
this.mSwitchState = mSwitchState;
}
/**
* 設置開關初始狀態
*
* @param state true:開,false:關
*/
public void setSwitchOpen(boolean state) {
mSwitchState = state;
invalidate(); // 會引發onDraw()被調用, 裏邊的變量會重新生效.界面會更新
}
/**
* @author M.xang
* @時間 2019年4月22日
* @描述 開關狀態回調
*/
public interface OnSwitchStateUpdateListener {
/**
* 狀態回調, 開關當前狀態
*
* @param state
*/
void onStateUpdate(boolean state);
}
/**
** 將回調對象傳到控件
*
* @param onSwitchStateUpdateListener
*/
public void setOnSwitchStateUpdateListener(OnSwitchStateUpdateListener onSwitchStateUpdateListener) {
this.onSwitchStateUpdateListener = onSwitchStateUpdateListener;
}
}
2.使用
再佈局中的使用:
<com.view.SwitchView
android:id="@+id/sw_computer"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
在Activity中調用:
// 進行實例化
SwitchView sw_computer = sw_computer = UIUtil.findView(this, R.id.sw_computer);
// 設置開關的初始狀態,默認是關閉的
sw_computer.setSwitchState(false);
// 開關點擊事件
sw_computer.setOnSwitchStateUpdateListener(new OnSwitchStateUpdateListener() {
@Override
public void onStateUpdate(boolean state) {
// state 爲 true是開,false是關
}
});