在講解自定義SwitchView之前? 先講解一下自定義View的基本步驟
1 .有些自定義需要定義View的屬性如:背景顏色? 字體大小? 字體顏色? 需要用到typeArray 大概步驟是在value包下創建attrs文件定義 declare-styleable 名稱 主要是定義屬性的format 主要知道自定義屬性對應的format就可以了 dimension 尺寸值 color 顏色? integer整數值 ? boolean布爾值 reference 資源文件ID float浮點值 string字符串值 fraction百分數 enum 枚舉值 flag 位或運算 因為我這里沒用用到自定義屬性所以就不過多的介紹了 有興趣的同學可以下來了解具體的使用
2.onMeasure定義View的大小? 這里介紹下MeasureSpec中的三種模式:分別是UNSPECIFIED ? AT_MOST ?? EXACTLY
1).精確模式MeasureSpec.EXACTLY 這種情況下是確定該控件的具體大小值? 如10dp
2).最大模式 MeasureSpec.AT_MOST 這個也就是父組件,能夠給出最大的控件,當前組件的寬高大小只能為這么大,當然也可以比這個小 如wrap_content
3).未指定模式 MeasureSpec.EXACTLY 當前組件,可以隨便使用控件,不受限制 MATCH_PARENT?
3.onSizeChanged 用以顯示當前View的位置和寬高設置 w h 分別表示當前的寬和高 oldw oldh分別表示改變之前的寬和高
4 onDraw繪制View? 定義畫布canvas 然后定義paint 最后就是在畫布上繪制View等一些邏輯上的操作?
5.onTouchEvent觸摸事件操作?
講了這么多 還不如直接上代碼自行研究
public class SwitchViewextends View {
private final Paintpaint =new Paint();
? private final PathsPath =new Path();
? private final PathbPath =new Path();
? private final RectFbRectF =new RectF();
? private float sAnim, bAnim;
? private RadialGradientshadowGradient;
? private final AccelerateInterpolatoraInterpolator =new AccelerateInterpolator(2);
? /**
* state switch on
*/
? public static final int STATE_SWITCH_ON =4;
? /**
* state prepare to off
*/
? public static final int STATE_SWITCH_ON2 =3;
? /**
* state prepare to on
*/
? public static final int STATE_SWITCH_OFF2 =2;
? /**
* state prepare to off
*/
? public static final int STATE_SWITCH_OFF =1;
? /**
* current state
*/
? private int state =STATE_SWITCH_OFF;
? /**
* last state
*/
? private int lastState =state;
? private boolean isOpened =false;
? private int mWidth, mHeight;
? private float sWidth, sHeight;
? private float sLeft, sTop, sRight, sBottom;
? private float sCenterX, sCenterY;
? private float sScale;
? private float bOffset;
? private float bRadius, bStrokeWidth;
? private float bWidth;
? private float bLeft, bTop, bRight, bBottom;
? private float bOnLeftX, bOn2LeftX, bOff2LeftX, bOffLeftX;
? private float shadowHeight;
? public SwitchView(Context context) {
this(context, null);
? }
public SwitchView(Context context, AttributeSet attrs) {
super(context, attrs);
? ? ? setLayerType(LAYER_TYPE_SOFTWARE, null);
? }
@Override
? protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
? ? ? int heightSize = (int) (widthSize *0.65f);
? ? ? setMeasuredDimension(widthSize, heightSize);
? }
@Override
? protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
? ? ? mWidth = w;
? ? ? mHeight = h;
? ? ? sLeft =sTop =0;
? ? ? sRight =mWidth;
? ? ? sBottom =mHeight *0.91f;
? ? ? sWidth =sRight -sLeft;
? ? ? sHeight =sBottom -sTop;
? ? ? sCenterX = (sRight +sLeft) /2;
? ? ? sCenterY = (sBottom +sTop) /2;
? ? ? shadowHeight =mHeight -sBottom;
? ? ? bLeft =bTop =0;
? ? ? bRight =bBottom =sBottom;
? ? ? bWidth =bRight -bLeft;
? ? ? final float halfHeightOfS = (sBottom -sTop) /2;
? ? ? bRadius = halfHeightOfS *0.95f;
? ? ? bOffset =bRadius *0.2f;
? ? ? bStrokeWidth = (halfHeightOfS -bRadius) *2;
? ? ? bOnLeftX =sWidth -bWidth;
? ? ? bOn2LeftX =bOnLeftX -bOffset;
? ? ? bOffLeftX =0;
? ? ? bOff2LeftX =0;
? ? ? sScale =1 -bStrokeWidth /sHeight;
? ? ? RectF sRectF =new RectF(sLeft, sTop, sBottom, sBottom);
? ? ? sPath.arcTo(sRectF, 90, 180);
? ? ? sRectF.left =sRight -sBottom;
? ? ? sRectF.right =sRight;
? ? ? sPath.arcTo(sRectF, 270, 180);
? ? ? sPath.close();
? ? ? bRectF.left =bLeft;
? ? ? bRectF.right =bRight;
? ? ? bRectF.top =bTop +bStrokeWidth /2;
? ? ? bRectF.bottom =bBottom -bStrokeWidth /2;
? ? ? shadowGradient =new RadialGradient(bWidth /2, bWidth /2, bWidth /2, 0xff000000, 0x00000000, Shader.TileMode.CLAMP);
? }
private void calcBPath(float percent) {
bPath.reset();
? ? ? bRectF.left =bLeft +bStrokeWidth /2;
? ? ? bRectF.right =bRight -bStrokeWidth /2;
? ? ? bPath.arcTo(bRectF, 90, 180);
? ? ? bRectF.left =bLeft + percent *bOffset +bStrokeWidth /2;
? ? ? bRectF.right =bRight + percent *bOffset -bStrokeWidth /2;
? ? ? bPath.arcTo(bRectF, 270, 180);
? ? ? bPath.close();
? }
private float calcBTranslate(float percent) {
float result =0;
? ? ? int wich =state -lastState;
? ? ? switch (wich) {
case 1:
// off -> off2
? ? ? ? ? ? if (state ==STATE_SWITCH_OFF2) {
result =bOff2LeftX - (bOff2LeftX -bOffLeftX) * percent;
? ? ? ? ? ? }
// on2 -> on
? ? ? ? ? ? else if (state ==STATE_SWITCH_ON) {
result =bOnLeftX - (bOnLeftX -bOn2LeftX) * percent;
? ? ? ? ? ? }
break;
? ? ? ? case 2:
// off2 -> on
? ? ? ? ? ? if (state ==STATE_SWITCH_ON) {
result =bOnLeftX - (bOnLeftX -bOff2LeftX) * percent;
? ? ? ? ? ? }
// off -> on2
? ? ? ? ? ? else if (state ==STATE_SWITCH_ON) {
result =bOn2LeftX - (bOn2LeftX -bOffLeftX) * percent;
? ? ? ? ? ? }
break;
? ? ? ? case 3:// off -> on
? ? ? ? ? ? result =bOnLeftX - (bOnLeftX -bOffLeftX) * percent;
break;
? ? ? ? case -1:
// on -> on2
? ? ? ? ? ? if (state ==STATE_SWITCH_ON2) {
result =bOn2LeftX + (bOnLeftX -bOn2LeftX) * percent;
? ? ? ? ? ? }
// off2 -> off
? ? ? ? ? ? else if (state ==STATE_SWITCH_OFF) {
result =bOffLeftX + (bOff2LeftX -bOffLeftX) * percent;
? ? ? ? ? ? }
break;
? ? ? ? case -2:
// on2 -> off
? ? ? ? ? ? if (state ==STATE_SWITCH_OFF) {
result =bOffLeftX + (bOn2LeftX -bOffLeftX) * percent;
? ? ? ? ? ? }
// on -> off2
? ? ? ? ? ? else if (state ==STATE_SWITCH_OFF2) {
result =bOff2LeftX + (bOnLeftX -bOff2LeftX) * percent;
? ? ? ? ? ? }
break;
? ? ? ? case -3:// on -> off
? ? ? ? ? ? result =bOffLeftX + (bOnLeftX -bOffLeftX) * percent;
break;
? ? ? }
return result -bOffLeftX;
? }
@Override
? protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
? ? ? paint.setAntiAlias(true);
? ? ? final boolean isOn = (state ==STATE_SWITCH_ON ||state ==STATE_SWITCH_ON2);
? ? ? // draw background
? ? ? paint.setStyle(Style.FILL);
? ? ? paint.setColor(isOn ?0xff4bd763 :0xffe3e3e3);
? ? ? canvas.drawPath(sPath, paint);
? ? ? sAnim =sAnim -0.1f >0 ?sAnim -0.1f :0;
? ? ? bAnim =bAnim -0.1f >0 ?bAnim -0.1f :0;
? ? ? final float dsAnim =aInterpolator.getInterpolation(sAnim);
? ? ? final float dbAnim =aInterpolator.getInterpolation(bAnim);
? ? ? // draw background animation
? ? ? final float scale =sScale * (isOn ? dsAnim :1 - dsAnim);
? ? ? final float scaleOffset = (bOnLeftX +bRadius -sCenterX) * (isOn ?1 - dsAnim : dsAnim);
? ? ? canvas.save();
? ? ? canvas.scale(scale, scale, sCenterX + scaleOffset, sCenterY);
? ? ? paint.setColor(0xffffffff);
? ? ? canvas.drawPath(sPath, paint);
? ? ? canvas.restore();
? ? ? // draw center bar
? ? ? canvas.save();
? ? ? canvas.translate(calcBTranslate(dbAnim), shadowHeight);
? ? ? final boolean isState2 = (state ==STATE_SWITCH_ON2 ||state ==STATE_SWITCH_OFF2);
? ? ? calcBPath(isState2 ?1 - dbAnim : dbAnim);
? ? ? // draw shadow
? ? ? paint.setStyle(Style.FILL);
? ? ? paint.setColor(0xff333333);
? ? ? paint.setShader(shadowGradient);
? ? ? canvas.drawPath(bPath, paint);
? ? ? paint.setShader(null);
? ? ? canvas.translate(0, -shadowHeight);
? ? ? canvas.scale(0.98f, 0.98f, bWidth /2, bWidth /2);
? ? ? paint.setStyle(Style.FILL);
? ? ? paint.setColor(0xffffffff);
? ? ? canvas.drawPath(bPath, paint);
? ? ? paint.setStyle(Style.STROKE);
? ? ? paint.setStrokeWidth(bStrokeWidth *0.5f);
? ? ? paint.setColor(isOn ?0xff4ada60 :0xffbfbfbf);
? ? ? canvas.drawPath(bPath, paint);
? ? ? canvas.restore();
? ? ? paint.reset();
? ? ? if (sAnim >0 ||bAnim >0) invalidate();
? }
@Override
? public boolean onTouchEvent(MotionEvent event) {
if ((state ==STATE_SWITCH_ON ||state ==STATE_SWITCH_OFF) && (sAnim *bAnim ==0)) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
return true;
? ? ? ? ? ? case MotionEvent.ACTION_UP:
lastState =state;
? ? ? ? ? ? ? if (state ==STATE_SWITCH_OFF) {
refreshState(STATE_SWITCH_OFF2);
? ? ? ? ? ? ? }else if (state ==STATE_SWITCH_ON) {
refreshState(STATE_SWITCH_ON2);
? ? ? ? ? ? ? }
bAnim =1;
? ? ? ? ? ? ? invalidate();
? ? ? ? ? ? ? if (state ==STATE_SWITCH_OFF2) {
listener.toggleToOn(this);
? ? ? ? ? ? ? }else if (state ==STATE_SWITCH_ON2) {
listener.toggleToOff(this);
? ? ? ? ? ? ? }
break;
? ? ? ? }
}
return super.onTouchEvent(event);
? }
private void refreshState(int newState) {
if (!isOpened && newState ==STATE_SWITCH_ON) {
isOpened =true;
? ? ? }else if (isOpened && newState ==STATE_SWITCH_OFF) {
isOpened =false;
? ? ? }
lastState =state;
? ? ? state = newState;
? ? ? postInvalidate();
? }
/**
? ? * @return the state of switch view
*/
? public boolean isOpened() {
return isOpened;
? }
/**
* if set true , the state change to on;
* if set false, the state change to off
*
? ? * @param isOpened
? ? */
? public void setOpened(boolean isOpened) {
refreshState(isOpened ?STATE_SWITCH_ON :STATE_SWITCH_OFF);
? }
/**
* if set true , the state change to on;
* if set false, the state change to off
? ? *
change state with animation
? ? *
? ? * @param isOpened
? ? */
? public void toggleSwitch(final boolean isOpened) {
this.isOpened = isOpened;
? ? ? postDelayed(new Runnable() {
@Override
? ? ? ? public void run() {
toggleSwitch(isOpened ?STATE_SWITCH_ON :STATE_SWITCH_OFF);
? ? ? ? }
}, 300);
? }
private synchronized void toggleSwitch(int wich) {
if (wich ==STATE_SWITCH_ON || wich ==STATE_SWITCH_OFF) {
if ((wich ==STATE_SWITCH_ON && (lastState ==STATE_SWITCH_OFF ||lastState ==STATE_SWITCH_OFF2))
|| (wich ==STATE_SWITCH_OFF && (lastState ==STATE_SWITCH_ON ||lastState ==STATE_SWITCH_ON2))) {
sAnim =1;
? ? ? ? }
bAnim =1;
? ? ? ? refreshState(wich);
? ? ? }
}
public interface OnStateChangedListener {
void toggleToOn(View view);
? ? ? void toggleToOff(View view);
? }
private OnStateChangedListenerlistener =new OnStateChangedListener() {
@Override
? ? ? public void toggleToOn(View view) {
toggleSwitch(STATE_SWITCH_ON);
? ? ? }
@Override
? ? ? public void toggleToOff(View view) {
toggleSwitch(STATE_SWITCH_OFF);
? ? ? }
};
? public void setOnStateChangedListener(OnStateChangedListener listener) {
if (listener ==null)throw new IllegalArgumentException("empty listener");
? ? ? this.listener = listener;
? }
@Override
? public ParcelableonSaveInstanceState() {
Parcelable superState =super.onSaveInstanceState();
? ? ? SavedState ss =new SavedState(superState);
? ? ? ss.isOpened =isOpened;
? ? ? return ss;
? }
@Override
? public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
? ? ? super.onRestoreInstanceState(ss.getSuperState());
? ? ? this.isOpened = ss.isOpened;
? ? ? this.state =this.isOpened ?STATE_SWITCH_ON :STATE_SWITCH_OFF;
? }
static final class SavedStateextends BaseSavedState {
private boolean isOpened;
? ? ? SavedState(Parcelable superState) {
super(superState);
? ? ? }
private SavedState(Parcel in) {
super(in);
? ? ? ? isOpened =1 == in.readInt();
? ? ? }
@Override
? ? ? public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
? ? ? ? out.writeInt(isOpened ?1 :0);
? ? ? }
}
}