先来看效果
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