先來看效果
android界面繪製流程 measure->layout->draw
也就是 測量->擺放->繪製
public class SwitchView extends View {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//這裏測量畫view的大小
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
public SwitchView(Context context) {
super(context);
}
//如果在xml文件中定義了資源 就會走到這個方法裏 需要我們手動的將資源加載進來
public SwitchView(Context context, AttributeSet attrs) {
super(context, attrs);
}
//如果設置了樣式 style 則走此方法
public SwitchView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
//在這裏面 畫button
}
直接上帶碼
public class SwitchView extends View {
//畫筆
private Paint paint;
private Bitmap mButton;
private Bitmap mSwitchBackground;
private Bitmap mSliderBackground;
private Bitmap mMask;
private PorterDuffXfermode mfermode;
//開關狀態
private boolean mState;
private float mCurrentX;
private OnStateChangeListener mOnStateChangeListener;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(mSwitchBackground.getWidth(), mSliderBackground.getHeight());
}
public SwitchView(Context context) {
super(context);
init();
}
public SwitchView(Context context, AttributeSet attrs) {
super(context, attrs);
//命名空間
String namespace = "http://schemas.android.com/apk/res-auto";
//默認的開關狀態
boolean state = attrs.getAttributeBooleanValue(namespace, "state", true);
//滑動背景
int slider_background = attrs.getAttributeResourceValue(namespace, "slider_background", -1);
//開關背景
int switch_background = attrs.getAttributeResourceValue(namespace, "switch_background", -1);
//按鈕
int button = attrs.getAttributeResourceValue(namespace, "button", -1);
//遮罩
int mask = attrs.getAttributeResourceValue(namespace, "mask", -1);
setSwitchBackground(switch_background);
setSliderBackground(slider_background);
setButton(button);
setMask(mask);
init();
}
public SwitchView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
//初始化畫筆
paint = new Paint();
//初始化圖像合成類
mfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
}
@Override
protected void onDraw(Canvas canvas) {
int saveFlags = Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG | Canvas
.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.FULL_COLOR_LAYER_SAVE_FLAG | Canvas
.CLIP_TO_LAYER_SAVE_FLAG;
canvas.saveLayer(0, 0, mSwitchBackground.getWidth(), mSwitchBackground.getHeight(), null,
saveFlags);
canvas.drawBitmap(mMask, 0, 0, paint);
paint.setXfermode(mfermode);
//定義滑塊可到達的最大值 最小值
float btnMaxLeft = - mButton.getWidth() / 2.0f + (mButton.getHeight()-15)/2.0f;
float btnMaxRight = 0;
float sliderMaxLeft = - mSliderBackground.getWidth() / 2.0f + (mButton.getHeight()-15)/2.0f;
float sliderMaxRight = 0;
if(isSlideMode){
//在滑動狀態下面 計算當前位置
float btnNewLeft = mCurrentX - mButton.getWidth() / 2.0f;
float sliderNewLeft = mCurrentX - mSliderBackground.getWidth() / 2.0f;
// 限定滑塊範圍
if(btnNewLeft < btnMaxLeft){
btnNewLeft = btnMaxLeft; // 左邊範圍
}else if (btnNewLeft > btnMaxRight) {
btnNewLeft = btnMaxRight; // 右邊範圍
}
if(sliderNewLeft < sliderMaxLeft){
sliderNewLeft = sliderMaxLeft; // 左邊範圍
}else if (sliderNewLeft > sliderMaxRight) {
sliderNewLeft = sliderMaxRight; // 右邊範圍
}
canvas.drawBitmap(mSliderBackground, sliderNewLeft, 0, paint);
paint.setXfermode(null);
canvas.drawBitmap(mSwitchBackground,0,0,paint);
//最後畫開關
canvas.drawBitmap(mButton, btnNewLeft, 0, paint);
}else {
if(mState){
canvas.drawBitmap(mSliderBackground, sliderMaxLeft, 0, paint);
paint.setXfermode(null);
canvas.drawBitmap(mSwitchBackground,0,0,paint);
canvas.drawBitmap(mButton, btnMaxLeft, 0, paint);
}else {
canvas.drawBitmap(mSliderBackground, sliderMaxRight, 0, paint);
paint.setXfermode(null);
canvas.drawBitmap(mSwitchBackground,0,0,paint);
canvas.drawBitmap(mButton, btnMaxRight, 0, paint);
}
}
canvas.restore();
}
private boolean isSlideMode = false;
private float upX ;
private float startX ;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mCurrentX = event.getX();
startX = mCurrentX;
break;
case MotionEvent.ACTION_MOVE:
if(Math.abs(startX-event.getX())>2){
isSlideMode = true;
mCurrentX = event.getX(); }
break;
case MotionEvent.ACTION_UP:
isSlideMode = false;
float upX = event.getX();
if(upX == startX){
mState = !mState;
mOnStateChangeListener.onStateChange(mState);
break;
}
float center = mSwitchBackground.getWidth() / 2.0f;
boolean oldState = mState;
mState = center > mCurrentX;
if(oldState != mState){
mOnStateChangeListener.onStateChange(mState);
}
break;
}
//重繪界面
invalidate();
//消費掉該事件 纔可以接收其它事件
return true;
}
/**
* 設置switch背景
* @param id
*/
public void setSwitchBackground(int id) {
mSwitchBackground = BitmapFactory.decodeResource(getResources(), id);
}
/**
* 設置滑動條背景
* @param id
*/
public void setSliderBackground(int id) {
mSliderBackground = BitmapFactory.decodeResource(getResources(), id);
}
/**
* 設置按鈕
* @param id
*/
public void setButton(int id) {
mButton = BitmapFactory.decodeResource(getResources(), id);
}
/**
* 設置遮罩
* @param id
*/
public void setMask(int id) {
mMask = BitmapFactory.decodeResource(getResources(), id);
}
//定義回調接口
public interface OnStateChangeListener{
void onStateChange(boolean state);
}
//暴露接口一個可以設置監聽
public void setOnStateChangeListener(OnStateChangeListener onStateChangeListener){
this.mOnStateChangeListener = onStateChangeListener;
}
}
別忘了根佈局定義命名空間
xmlns:reige=”http://schemas.android.com/apk/res-auto”
<com.example.reige.switchview.view.SwitchView
android:id="@+id/switch_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
reige:button="@mipmap/switch_button"
reige:slider_background="@mipmap/slider_background"
reige:switch_background="@mipmap/switch_background"
reige:mask="@mipmap/bitmap_mask"
/>
源碼地址 ->> github
圖像合成相關 http://www.tuicool.com/articles/6RjE3m