Android View 仿iOS SwitchButton [Material Design]
来源:互联网 发布:知汇云软件 编辑:程序博客网 时间:2024/06/11 04:19
尊重原创转载请注明:http://blog.csdn.net/bfbx5173/article/details/45191147
我们在完成一个开关器按钮的时候,系统原生的太丑了,那我们想要的效果就是如下图:
话不多说,直接上代码:
public class SwitchView extends View { private final Paint paint = new Paint(); private final Path sPath = new Path(); private final Path bPath = new Path(); private final RectF bRectF = new RectF(); private float sAnim, bAnim; private RadialGradient shadowGradient; private final AccelerateInterpolator aInterpolator = 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 = MeasureSpec.getSize(heightMeasureSpec); 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; } private int openColor = Color.RED; private int offColor = 0xffe3e3e3; public void setOffColor(int offColor) { this.offColor = offColor; } public void setOpenColor(int openColor) { this.openColor = openColor; } @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); paint.setColor(isOn ? openColor : offColor); 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 ? openColor : offColor); canvas.drawPath(bPath, paint); canvas.restore(); paint.reset(); if (sAnim > 0 || bAnim > 0) invalidate(); } public boolean isEnable = true; public void setEnable(boolean enable) { isEnable = enable; } @Override public boolean onTouchEvent(MotionEvent event) { if (!isEnable) { return super.onTouchEvent(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 * <br><b>change state with animation</b> * * @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 OnStateChangedListener listener = 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 Parcelable onSaveInstanceState() { 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 SavedState extends 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); } }}
这里,对原博主的代码稍加完善,可以在主界面初始化完后,修改打开时的颜色和设置控件是否可用:
SwitchView switchview = (SwitchView) findViewById(R.id.switchview);switchview.setEnable(true);switchview.setOpenColor(Color.BLUE);
0 0
- Android View 仿iOS SwitchButton [Material Design]
- Android View 仿iOS SwitchButton [Material Design]
- Android View 仿iOS SwitchButton [Material Design]
- Android View 仿iOS SwitchButton [Material Design]
- Android View 仿iOS SwitchButton
- Android View 仿iOS SwitchButton和各种效果集合
- Android 仿iOS 开关SwitchButton
- 仿iOS自定义SwitchButton
- Android 自定义View(三)Material Design风格的ProgressBar
- Android Material Design动画 View state changes|视图状态改变
- SwitchButton 仿ios 按钮滑动效果
- Material Design - View state changes
- android仿苹果SwitchButton效果的实现
- Android Material Design尝鲜
- Android Material Design
- Android Material Design Dialog
- Android:Material Design详解
- Android Material Design:PopupMenu
- MQ基础
- ValueAnimator的使用
- Retrofit2源码解析(二)添加 JacksonConverterFactory转化器
- appium(win7 + android studio)环境搭建 + 测试小demo运行
- C++函数默认参数, 函数重载, 函数模板
- Android View 仿iOS SwitchButton [Material Design]
- mysql学习笔记之优化篇(二)之参数-磁盘-应用调优
- iOS 性能调优,成为一名合格iOS程序员必须掌握的技能
- 数据结构 图的基本操作实现
- onMeasure()方法及控件的绘制
- SSH Secure Shell Client简单实用方法
- php获取自然周自然月,周几,月,周第一天,最后一天总结
- 设计模式
- JZOJ4868. 【NOIP2016提高A组集训第9场11.7】Simple