package com.ydl.ydlcommon.view; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Cap; import android.graphics.Paint.Style; import android.graphics.RectF; import android.os.Looper; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; import android.view.animation.OvershootInterpolator; import com.ydl.ydlcommon.R; /** * Created by harvie on 2017/6/10 0010. */ public class MyToggleButton extends View{ private float radius; // 开启颜色 private int onColor; // 关闭颜色 private int offColor; // 灰色带颜色 private int offBorderColor; // 手柄颜色 private int spotColor; // 边框颜色 private int borderColor; // 画笔 private Paint paint ; // 开关状态 private boolean toggleOn = false; // 边框大小 默认为2px private int borderWidth = 1; // 垂直中心 private float centerY; // 按钮的开始和结束位置 private float startX, endX; // 手柄X位置的最小和最大值 private float spotMinX, spotMaxX; // 手柄大小 private int spotSize ; /// 手柄X位置 private float spotX; // 关闭时内部灰色带高度 private float offLineWidth; private RectF rect = new RectF(); //开关切换监听器 private OnToggleChanged listener; //属性动画 private ValueAnimator animation; public MyToggleButton(Context context) { this(context, null); } public MyToggleButton(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MyToggleButton(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } public void init() { initPaint(); initColor(); initAnimation(); this.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { toggle(); } }); } private void initPaint() { //初始化画笔(抗抖动) paint = new Paint(Paint.ANTI_ALIAS_FLAG); //绘制风格为填充 paint.setStyle(Style.FILL); //笔触风格为圆角 paint.setStrokeCap(Cap.ROUND); } private void initColor() { onColor = getResources().getColor(R.color.platform_google_green); offColor = getResources().getColor(R.color.platform_gray7); offBorderColor = getResources().getColor(R.color.platform_gray7); spotColor = getResources().getColor(R.color.platform_white); //因为开始为关闭状态,所以这里边框背景色初始化为关闭状态颜色 borderColor = offColor; } @SuppressLint("NewApi") private void initAnimation() { animation = new ValueAnimator(); animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { calculateToggleEffect(Double.parseDouble(animation.getAnimatedValue().toString())); } }); //OvershootInterpolator : 结束时会超过给定数值,但是最后一定返回给定值 animation.setInterpolator(new OvershootInterpolator(1.5f)); animation.setDuration(500); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int widthMode = MeasureSpec.getMode(widthMeasureSpec); final int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); Resources r = Resources.getSystem(); if(widthMode == MeasureSpec.UNSPECIFIED || widthMode == MeasureSpec.AT_MOST){ widthSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 55, r.getDisplayMetrics()); widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY); } if(heightMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.AT_MOST){ heightSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, r.getDisplayMetrics()); heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); final int width = getWidth(); final int height = getHeight(); //由宽高计算圆角的半径 radius = Math.min(width, height) * 0.5f; centerY = radius; startX = radius; endX = width - radius; spotMinX = startX + borderWidth; spotMaxX = endX - borderWidth; spotSize = height - 4 * borderWidth; spotX = toggleOn ? spotMaxX : spotMinX; offLineWidth = 0; } private int clamp(int value, int low, int high) { return Math.min(Math.max(value, low), high); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //1.绘制最外层边框背景-圆角矩形 //绘制圆角矩形背景大小为测量的宽高 rect.set(0, 0, getWidth(), getHeight()); paint.setColor(borderColor); canvas.drawRoundRect(rect, radius, radius, paint); if(offLineWidth > 0){ //1.1绘制整个开关区域中除手柄外的白色区域带 final float cy = offLineWidth * 0.5f; rect.set(spotX - cy, centerY - cy, endX + cy, centerY + cy); paint.setColor(offBorderColor); canvas.drawRoundRect(rect, cy, cy, paint); } //2.绘制圆形手柄边框区域 rect.set(spotX - 1 - radius, centerY - radius, spotX + 1.1f + radius, centerY + radius); paint.setColor(borderColor); canvas.drawRoundRect(rect, radius, radius, paint); //3.绘制圆形手柄区域 //圆形手柄的半径大小(不包含边框) final float spotR = spotSize * 0.5f; rect.set(spotX - spotR, centerY - spotR, spotX + spotR, centerY + spotR); paint.setColor(spotColor); canvas.drawRoundRect(rect, spotR, spotR, paint); } private double mapValueFromRangeToRange(double value, double fromLow, double fromHigh, double toLow, double toHigh) { double fromRangeSize = fromHigh - fromLow; double toRangeSize = toHigh - toLow; double valueScale = (value - fromLow) / fromRangeSize; return toLow + (valueScale * toRangeSize); } /** * @param value */ private void calculateToggleEffect(final double value) { final float mapToggleX = (float) mapValueFromRangeToRange(value, 0, 1, spotMinX, spotMaxX); spotX = mapToggleX; float mapOffLineWidth = (float) mapValueFromRangeToRange(1 - value, 0, 1, 10, spotSize); offLineWidth = mapOffLineWidth; //开启时候的背景色 final int fr = Color.red(onColor); final int fg = Color.green(onColor); final int fb = Color.blue(onColor); //关闭后的背景色 final int tr = Color.red(offColor); final int tg = Color.green(offColor); final int tb = Color.blue(offColor); //border颜色渐变 int sr = (int) mapValueFromRangeToRange(1 - value, 0, 1, fr, tr); int sg = (int) mapValueFromRangeToRange(1 - value, 0, 1, fg, tg); int sb = (int) mapValueFromRangeToRange(1 - value, 0, 1, fb, tb); sr = clamp(sr, 0, 255); sg = clamp(sg, 0, 255); sb = clamp(sb, 0, 255); borderColor = Color.rgb(sr, sg, sb); //重绘 if (Looper.myLooper() == Looper.getMainLooper()) { invalidate(); } else { postInvalidate(); } } @SuppressLint("NewApi") private void takeToggleAction(boolean isOn){ if(isOn) { animation.setFloatValues(0.f, 1.f); } else { animation.setFloatValues(1.f, 0.f); } animation.start(); } /** * 切换开关 */ public void toggle() { toggleOn = !toggleOn; takeToggleAction(toggleOn); if(listener != null){//触发toggle事件 listener.onToggle(toggleOn); } } /** * 切换为打开状态 */ public void toggleOn() { toggleOn = true; takeToggleAction(toggleOn); if(listener != null){//触发toggle事件 listener.onToggle(toggleOn); } } /** * 切换为关闭状态 */ public void toggleOff() { toggleOn = false; takeToggleAction(toggleOn); if(listener != null){//触发toggle事件 listener.onToggle(toggleOn); } } /** * 设置显示成打开样式,不会触发toggle事件 */ public void setToggleOn(){ toggleOn = true; calculateToggleEffect(1.0f); } /** * 设置显示成关闭样式,不会触发toggle事件 */ public void setToggleOff() { toggleOn = false; calculateToggleEffect(0.0f); } /** * 状态切换监听器 */ public interface OnToggleChanged{ void onToggle(boolean on); } public void setOnToggleChanged(OnToggleChanged onToggleChanged) { listener = onToggleChanged; } }