package com.doyou.cv.widget; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.DashPathEffect; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PointF; import android.graphics.Shader; import android.util.AttributeSet; import com.dongni.tools.DensityUtil; import com.doyou.cv.widget.base.BaseGradientView; import java.util.List; import androidx.annotation.Nullable; /** * 渐变曲线自定义 * * @autor hongbing * @date 2018/11/23 * 参考资料: * 1.https://www.jianshu.com/p/5522470760c1 * <p> * 贝塞尔曲线原理: * 2.https://blog.csdn.net/u013831257/article/details/51281136 * <p> * canvas和paint相关知识点: * 3.https://juejin.im/entry/58c663f944d9040069956731 */ public class GradientLine extends BaseGradientView { private Paint mPaint; // 曲线画笔 private Paint mImaPaint; // 虚线画笔 private Paint mPointPaint; // 圆点 private PointF control1, control2; // 控制点 private float mRadius; // 小圆半径 private float mImaLineL; // 曲线长度 private int mLineW; // 图形长度 public GradientLine(Context context) { this(context,null); } public GradientLine(Context context,@Nullable AttributeSet attrs) { this(context,attrs,0); } public GradientLine(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public interface LineMode { int FLAT = 0; int UP = 1; int DOWN = 2; } @Override protected void init() { super.init(); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStrokeWidth(8f); // mPaint.setStrokeCap(Paint.Cap.ROUND); mPaint.setColor(Color.RED); mPaint.setStyle(Paint.Style.STROKE); mPointPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPointPaint.setStyle(Paint.Style.STROKE); mPointPaint.setColor(Color.GREEN); mPointPaint.setStrokeWidth(5f); mImaPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mImaPaint.setStyle(Paint.Style.STROKE); mImaPaint.setStrokeWidth(5f); mImaPaint.setColor(Color.rgb(200, 200, 200)); mImaPaint.setPathEffect(new DashPathEffect(new float[]{16, 4}, 0)); // 参数:数组(param1:线段长度,线段间的间距) control1 = new PointF(0f, 0f); control2 = new PointF(0f, 0f); mRadius = DensityUtil.dp2px(2.4f); mLineW = DensityUtil.dp2px(80f); mRadian = (1.4f / 3); // 控制点弧度 } public void setMode(int mode) { mMode = Mode.values()[mode]; log("显示模式-> mMode = " + mMode); if(mIsAnim && mIsRunging){ // 如果当前动画还没结束,有开启其他模式的动画,需要将动画开关关闭,避免同个控件同时刷新数据导致的图形问题 mIsAnim = false; } invalidate(); } private float mRadian; // 控制点弧度 public void setRadian(float radian) { mRadian = radian; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); final int width = measureSize(widthMeasureSpec); final int height = measureSize(heightMeasureSpec); log("onMeasure-->width = " + width + "->height = " + height); setMeasuredDimension(width, height); } private void caclControPoint(int w, int h) { if (mMode == Mode.Up) { control1.x = w * mRadian; control1.y = h - mRadius * 2 - mPointPaint.getStrokeWidth(); control2.x = w * (1.f - mRadian); control2.y = mRadius; } else if (mMode == Mode.Down) { control1.x = w * mRadian; control1.y = mRadius + mPointPaint.getStrokeWidth(); control2.x = w * (1.f - mRadian); control2.y = h - mRadius * 2 - mPointPaint.getStrokeWidth(); } } private int measureSize(int measureSpec) { int result; int mode = MeasureSpec.getMode(measureSpec); int size = MeasureSpec.getSize(measureSpec); switch (mode) { case MeasureSpec.EXACTLY: result = size; break; default: result = mLineW; break; } return result; } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { // 控件大小发生改变,invalidate之行不会调用本方法 super.onSizeChanged(w, h, oldw, oldh); log("->>>>>>onSizeChanged...(w * mRadian) = " + w * mRadian + "->(w * 1 / 4) = " + (w * 1 / 4) + "->w = " + w); caclControPoint(w, h); } // @Override // protected void onLayout(boolean changed, int left, int top, int right, int bottom) { // requestLayout会重新调用该方法 // super.onLayout(changed, left, top, right, bottom); // Log.d(TAG,"->>>>>>onLayout->w = " + (right - left)); // } @Override protected void onDraw(Canvas canvas) { log("->>>>>>onDraw...control1.x = " + control1.x + "->control1.y = " + control1.y + "->control2.x = " + control2.x + "->control2.y = " + control2.y); mView_W = getWidth(); mView_H = getHeight(); caclControPoint(mView_W, mView_H); if (mMode == Mode.Flat) { // if (mIsAnim) { // if (mCurrFlatPoint == null) { // mCurrFlatPoint = new GradientPoint(mRadius * 2 + mPointPaint.getStrokeWidth(), mView_H / 2 - mPaint.getStrokeWidth() / 2); // startAnimByFlat(mCurrFlatPoint.getX(), mCurrFlatPoint.getY(), // mView_W - mRadius * 2 - mPointPaint.getStrokeWidth(), mView_H / 2 - mPaint.getStrokeWidth() / 2); // } else { // drawFlat(canvas, mView_W, mView_H); // } // } else { // mCurrFlatPoint = new GradientPoint(mView_W - mRadius * 2 - mPointPaint.getStrokeWidth(), // mView_H / 2 - mPaint.getStrokeWidth() / 2); // drawFlat(canvas, mView_W, mView_H); // } if (mIsAnim && !mIsRunging) { drawFlatByAnim(); } else { drawFlat(canvas, mIsAnim); } } else if (mMode == Mode.Up) { if (mIsAnim && !mIsRunging) { drawUpByAnim(); } else { drawUp(canvas, mIsAnim); } } else if(mMode == Mode.Down) { if (mIsAnim && !mIsRunging) { drawDownByAnim(); } else { drawDown(canvas, mIsAnim); } } } // private GradientPoint mCurrFlatPoint; // // private void startAnimByFlat(float start_x, float start_y, float end_x, float end_y) { // GradientPoint startPoint = new GradientPoint(start_x, start_y); // GradientPoint endPoint = new GradientPoint(end_x, end_y); // ValueAnimator animator = ValueAnimator.ofObject(new GradientEvaluator(), startPoint, endPoint); // animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { // @Override // public void onAnimationUpdate(ValueAnimator animation) { // invalidate(); // mCurrFlatPoint = (GradientPoint) animation.getAnimatedValue(); // } // }); // animator.addListener(new AnimatorListenerAdapter() { // @Override // public void onAnimationEnd(Animator animation) { // super.onAnimationEnd(animation); //// mCurrFlatPoint = null; // } // }); // animator.setDuration(mAnimDuration); // animator.start(); // } @Override protected void drawBezierPoint(List<Point> bezierPoints, Canvas canvas, Mode mode) { log("画曲线了->bazierPoints.size = " + bezierPoints.size() + "->mode = " + mode.name()); if (mode == Mode.None) { return; } if (mode == Mode.Up) { upLgMoreConfig(canvas); } else if (mode == Mode.Down) { downLgMoreCofig(canvas); }else if(mode == Mode.Flat){ flatLgMoreConfig(canvas); } Path path = new Path(); path.moveTo(bezierPoints.get(0).getX(), bezierPoints.get(0).getY()); int index = 1; int var6 = bezierPoints.size() - 1; if (index <= var6) { while (true) { path.lineTo(bezierPoints.get(index).getX(), bezierPoints.get(index).getY()); if (index == var6) { break; } ++index; } } canvas.drawPath(path, mPaint); } private void flatLgMoreConfig(Canvas canvas) { mPaint.setShader(null); mPaint.setColor(Color.rgb(88, 181, 250)); // 画左右圆点 mPointPaint.setShader(null); mPointPaint.setColor(Color.rgb(88, 181, 250)); canvas.drawCircle(mRadius + mPointPaint.getStrokeWidth(), mView_H / 2 - mPaint.getStrokeWidth() / 2, mRadius, mPointPaint); canvas.drawCircle(mView_W - mRadius - mPointPaint.getStrokeWidth(), mView_H / 2 - mPaint.getStrokeWidth() / 2, mRadius, mPointPaint); } @Override protected void drawFlat(Canvas canvas, boolean isAnim) { if (isAnim) { updatePath(canvas, getPoints(), mMode); } else { flatLgMoreConfig(canvas); canvas.drawLine(mRadius * 2 + mPointPaint.getStrokeWidth(), mView_H / 2 - mPaint.getStrokeWidth() / 2, mView_W - mRadius * 2 - mPointPaint.getStrokeWidth(), mView_H / 2 - mPaint.getStrokeWidth() / 2, mPaint); } } @Override protected void drawFlatByAnim() { clearPointsAll(); float y = mView_H / 2 - mPaint.getStrokeWidth() / 2; // 起始点 addPoint(mRadius * 2 + mPointPaint.getStrokeWidth(), y); // 结束点 addPoint(mView_W - mRadius * 2 - mPointPaint.getStrokeWidth(), y); startAnim(); } private void upLgMoreConfig(Canvas canvas){ LinearGradient lg = new LinearGradient(mRadius * 2, control1.y, mView_W / 2, control2.y + DensityUtil.dp2px(2), Color.rgb(255, 196, 0), Color.rgb(255, 105, 83), Shader.TileMode.CLAMP); mPaint.setShader(lg); // 画虚线 canvas.drawLine(mView_W - mRadius - mPointPaint.getStrokeWidth(), mRadius * 2 + mPointPaint.getStrokeWidth(), mView_W - mRadius - mPointPaint.getStrokeWidth(), mView_H - mRadius * 2 - mPointPaint.getStrokeWidth(), mImaPaint); // 画圆点 mPointPaint.setShader(lg); canvas.drawCircle(mRadius + mPointPaint.getStrokeWidth(), mView_H - mRadius - mPointPaint.getStrokeWidth(), mRadius, mPointPaint); canvas.drawCircle(mView_W - mRadius - mPointPaint.getStrokeWidth(), control2.y + DensityUtil.dp2px(2), mRadius, mPointPaint); } @Override protected void drawUp(Canvas canvas, boolean isAnim) { // 设置渐变曲线配置 if (isAnim) { updatePath(canvas, getPoints(),mMode); } else { upLgMoreConfig(canvas); Path path = new Path(); path.moveTo(mRadius * 2 + mPointPaint.getStrokeWidth(), mView_H - mRadius - mPointPaint.getStrokeWidth()); path.cubicTo(control1.x, control1.y, control2.x, control2.y, mView_W - (mRadius * 2) - mPointPaint.getStrokeWidth(), control2.y + DensityUtil.dp2px(2)); canvas.drawPath(path, mPaint); } } @Override protected void drawUpByAnim() { clearPointsAll(); // 起始点 addPoint(mRadius * 2 + mPointPaint.getStrokeWidth(), mView_H - mRadius - mPointPaint.getStrokeWidth()); // 控制点1 addPoint(control1.x, control1.y); // 控制点2 addPoint(control2.x, control2.y); // 结束点 addPoint(mView_W - (mRadius * 2) - mPointPaint.getStrokeWidth(), control2.y + DensityUtil.dp2px(2)); startAnim(); } private void downLgMoreCofig(Canvas canvas){ // 设置渐变曲线配置 float y0 = control1.y; LinearGradient lg = new LinearGradient(mRadius * 2, y0, mView_W / 2, control2.y + DensityUtil.dp2px(2), Color.rgb(88, 181, 250), Color.rgb(101, 226, 175), Shader.TileMode.CLAMP); mPaint.setShader(lg); // 画虚线 canvas.drawLine(mRadius + mPointPaint.getStrokeWidth(), mRadius * 2 + mPointPaint.getStrokeWidth(), mRadius + mPointPaint.getStrokeWidth(), mView_H - mRadius * 2 - mPointPaint.getStrokeWidth(), mImaPaint); // 画圆点 mPointPaint.setShader(lg); canvas.drawCircle(mRadius + mPointPaint.getStrokeWidth(), control1.y, mRadius, mPointPaint); canvas.drawCircle(mView_W - mRadius - mPointPaint.getStrokeWidth(), control2.y, mRadius, mPointPaint); } @Override protected void drawDown(Canvas canvas, boolean isAnim) { if (isAnim) { updatePath(canvas, getPoints(),mMode); } else { downLgMoreCofig(canvas); Path path = new Path(); path.moveTo(mRadius * 2 + mPointPaint.getStrokeWidth(), mRadius + mPointPaint.getStrokeWidth()); path.cubicTo(control1.x, control1.y, control2.x, control2.y, mView_W - (mRadius * 2) - mPointPaint.getStrokeWidth(), control2.y); canvas.drawPath(path, mPaint); } } @Override protected void drawDownByAnim() { clearPointsAll(); // 起始点 addPoint(mRadius * 2 + mPointPaint.getStrokeWidth(), mRadius + mPointPaint.getStrokeWidth()); // 控制点1 addPoint(control1.x, control1.y); // 控制点2 addPoint(control2.x, control2.y); // 结束点 addPoint(mView_W - (mRadius * 2) - mPointPaint.getStrokeWidth(), control2.y); startAnim(); } }