• 自定义小太阳控件


    你没看错,右上角的那个大圆就是传说中的太阳,^_^

    这个动画的难点在于这个“食物”的绘制上吧,不用怀疑,你还是没看错,那些小点就是传说中的食物

    首先一步步来,看到这种效果,第一个想到的就是一个普通的小圆,而这个大圆就用贝塞尔绘制,至于为什么用贝塞尔而不是直接绘制一个半圆呢,因为食物是绕着半圆的,紧贴着,你需要拿这个半圆做参照物,不然随便改改布局就乱套了,

    然后是小圆也需要围着半圆绕圈,这两个都需要一个已知的point

    接下来就开始绘制半圆了,小圆就直接被忽略了

    半圆采用 drawPath.cubicTo 来绘制,需要四个点,其中有两个控制点

    具体怎么绘制看后面的代码

    接下来绘制食物,这下可能一部分人会有点困惑,食物怎么绘制,怎么让食物紧贴着这个半圆去绘制,那我是不是可以照着这个半圆把Y轴的坐标改改,这样另外一个半圆就出现了,只是需要改变下样式,因为食物是一个个小点

    这里设置paint的effect就可以改变绘制的样式了

    就这样食物出现了

    然后就是一个难点了,小圆怎么沿着食物一路吃下去呢,很显然不能直接按照食物的绘制方法来绘制了,因为小圆是会动的,需要加入动画,而不是一下子全绘制,这样就需要知道食物的坐标了,这样才能让小圆绕着食物吃下去

    这里需要用到 Evaluator 根据控制点去计算出它本身的一个绘制坐标,贝塞尔是一个公式,可以推算出它的一个坐标点

    package com.fragmentapp.view;
    
    import android.graphics.PointF;
    
    /**
     * Created by liuzhen on 2018/1/24.
     */
    
    public class BezierUtil {
    
        /**
         * B(t) = (1 - t)^2 * P0 + 2t * (1 - t) * P1 + t^2 * P2, t ∈ [0,1]
         *
         * @param t  曲线长度比例
         * @param p0 起始点
         * @param p1 控制点
         * @param p2 终止点
         * @return t对应的点
         */
        public static PointF CalculateBezierPointForQuadratic(float t, PointF p0, PointF p1, PointF p2) {
            PointF point = new PointF();
            float temp = 1 - t;
            point.x = temp * temp * p0.x + 2 * t * temp * p1.x + t * t * p2.x;
            point.y = temp * temp * p0.y + 2 * t * temp * p1.y + t * t * p2.y;
            return point;
        }
    
        /**
         * B(t) = P0 * (1-t)^3 + 3 * P1 * t * (1-t)^2 + 3 * P2 * t^2 * (1-t) + P3 * t^3, t ∈ [0,1]
         *
         * @param t  曲线长度比例
         * @param p0 起始点
         * @param p1 控制点1
         * @param p2 控制点2
         * @param p3 终止点
         * @return t对应的点
         */
        public static PointF CalculateBezierPointForCubic(float t, PointF p0, PointF p1, PointF p2, PointF p3) {
            PointF point = new PointF();
            float temp = 1 - t;
            point.x = p0.x * temp * temp * temp + 3 * p1.x * t * temp * temp + 3 * p2.x * t * t * temp + p3.x * t * t * t;
            point.y = p0.y * temp * temp * temp + 3 * p1.y * t * temp * temp + 3 * p2.y * t * t * temp + p3.y * t * t * t;
            return point;
        }
    
    }
    View Code
    package com.fragmentapp.view;
    
    import android.animation.TypeEvaluator;
    import android.graphics.PointF;
    
    /**
     * Created by liuzhen on 2018/1/24.
     */
    
    public class PathEvaluator implements TypeEvaluator<PointF> {
    
        private PointF mControlPoint1,mControlPoint2;
    
    //    public PathEvaluator(PointF controlPoint) {
    //        this.mControlPoint1 = controlPoint;
    //    }
        public PathEvaluator(PointF controlPoint1,PointF controlPoint2) {
            this.mControlPoint1 = controlPoint1;
            this.mControlPoint2 = controlPoint2;
        }
    
        @Override
        public PointF evaluate(float t, PointF startValue, PointF endValue) {
            return BezierUtil.CalculateBezierPointForCubic(t, startValue, mControlPoint1,mControlPoint2, endValue);
        }
    
    }
    View Code

    有了这些坐标那小圆就可以沿着食物吃下去了,但是随着吃的动作又会发现怎么吃这个问题了,这显然也不是很好操作,本人想了一会,发现这条路行不通,技术还没达标,于是另辟蹊径,用另外的办法去做了,想想是绘制,那就干脆在绘制一条背景色的线

    这样食物就会出现被吃掉的效果了,所以在上面有个clears.add的操作

    在给小圆添加一张嘴吧

    控制angle这个参数,小圆终于可以吃了,然后为了追求更高的一个层次,又添加了一个移动的动画,然后动画开始的时候先让半圆有个上升的过程,这样顿时看起来高大上一些了

    最后stop

    使用就是两个方法,一个start动画,一个开始吃的动画操作,两个方法,在合适的时候调用就行了,直接上代码

    package com.fragmentapp.view.refresh;
    
    import android.animation.ValueAnimator;
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.DashPathEffect;
    import android.graphics.Matrix;
    import android.graphics.Paint;
    import android.graphics.Path;
    import android.graphics.PathDashPathEffect;
    import android.graphics.PathEffect;
    import android.graphics.PointF;
    import android.graphics.RectF;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.View;
    import android.view.animation.LinearInterpolator;
    
    import com.fragmentapp.R;
    import com.fragmentapp.helper.TimeUtil;
    import com.fragmentapp.view.PathEvaluator;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Created by liuzhen on 2018/1/24.
     */
    
    public class SunHeadView extends View implements IHeadView{
    
        private int mWidth;
        private int mHeight;
    
        private Paint effectPaint,facePaint,clearPaint,defPaint;//这里定义多个属性都是为了能够自定义不同的样式
        private RectF rectF = null;
    
        private float angle,loadAngle = 45;
    
        private ValueAnimator faceVa,arcVa;
    
        private int left,top;
    
        private boolean isDraw = false;
    
        private Path path,foodPath;
        private PointF startPoint = null,movePoint1 = null,movePoint2 = null,endPoint = null;
        private List<PointF> clears = null;
    
        private PathEffect effect = null;
    
        private int faceRadius = 30,foodRadius = 3;
    
        public SunHeadView(Context context) {
            this(context, null, 0);
        }
    
        public SunHeadView(Context context, AttributeSet attrs) {
            this(context, attrs,0);
        }
    
        public SunHeadView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init(){
    
            path = new Path();
    
            foodPath = new Path();
            foodPath.addCircle(0, 0, foodRadius, Path.Direction.CCW);
    
            effectPaint = new Paint();
            effectPaint.setAntiAlias(true);
            effectPaint.setStyle(Paint.Style.STROKE);
            effectPaint.setColor(getResources().getColor(R.color.color_a9a05c));
    
            effect = new PathDashPathEffect(foodPath, 12, -1, PathDashPathEffect.Style.ROTATE);
            effectPaint.setPathEffect(effect);
    
            facePaint = new Paint();
            facePaint.setAntiAlias(true);
            facePaint.setStyle(Paint.Style.FILL);
            facePaint.setColor(getResources().getColor(R.color.color_a9a05c));
    
            defPaint = new Paint();
            defPaint.setAntiAlias(true);
            defPaint.setStyle(Paint.Style.FILL);
            defPaint.setColor(getResources().getColor(R.color.color_a9a05c));
    
            rectF = new RectF(0,0,0,0);
            startPoint = new PointF();
            movePoint1 = new PointF();
            movePoint2 = new PointF();
            endPoint = new PointF();
    
            clearPaint = new Paint();
            clearPaint.setAntiAlias(true);
            clearPaint.setStyle(Paint.Style.FILL);
            clearPaint.setColor(getResources().getColor(R.color.white));
    
            clears = new ArrayList<>();
        }
    
        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            super.onLayout(changed, left, top, right, bottom);
            if (changed) {
                mWidth = getWidth();
                mHeight = getHeight();
    
                this.left = mWidth / 2;
                this.top = mHeight / 3;
    
                rectF.set(startPoint.x - faceRadius/2,startPoint.y - faceRadius,startPoint.x + faceRadius/2,startPoint.y);
            }
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            if (!isDraw) return;
            //绘制“食物”
            foodPath.reset();
            foodPath.moveTo(startPoint.x,startPoint.y);
            foodPath.cubicTo(movePoint1.x,movePoint1.y,movePoint2.x,movePoint2.y,endPoint.x,endPoint.y);
            canvas.drawPath(foodPath, effectPaint);
            //绘制大球
            path.reset();
            path.moveTo(startPoint.x + faceRadius/2,startPoint.y);
            path.cubicTo(movePoint1.x,movePoint1.y + faceRadius/2,movePoint2.x,movePoint2.y + faceRadius/2,endPoint.x - faceRadius/2,endPoint.y);
            canvas.drawPath(path, defPaint);
            //吃掉“食物”
            for (PointF f : clears) {
                RectF rectF = new RectF(f.x-foodRadius*2,f.y-foodRadius*2,f.x+foodRadius*2,f.y+foodRadius*2);
                canvas.drawOval(rectF,clearPaint);
            }
    
            //绘制小球,需要在最后面绘制
            canvas.drawArc(rectF, angle, 360 - angle * 2, true, facePaint);
    
        }
    
        @Override
        public View getView() {
            return this;
        }
    
        /**开始动画*/
        public void upAnim(){
            if (faceVa != null)
                faceVa.cancel();
            faceVa = null;
    
            effectPaint.setColor(getResources().getColor(R.color.color_a9a05c));
            facePaint.setColor(getResources().getColor(R.color.color_a9a05c));
            clearPaint.setColor(getResources().getColor(R.color.white));
            defPaint.setColor(getResources().getColor(R.color.color_a9a05c));
    
            startPoint.set(mWidth*1/2 + faceRadius*2,mHeight + faceRadius);
            movePoint1.set(mWidth*2/3, 0);
            movePoint2.set(mWidth*5/6, 0);
            endPoint.set(mWidth - faceRadius*2,mHeight + faceRadius);
    
            clears.clear();
    
            PathEvaluator bezierEvaluator = new PathEvaluator(movePoint1,movePoint2);
            arcVa = ValueAnimator.ofObject(bezierEvaluator, startPoint, endPoint);
            arcVa.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {//饶球移动
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    PointF point = (PointF) valueAnimator.getAnimatedValue();
    //                if (point.x + faceRadius <= endPoint.x)
                    clears.add(new PointF(point.x,point.y));//保存移动的坐标
                    //faceRadius/2是为了让小球的中心点刚好在大球的中间
                    rectF.set(point.x - faceRadius/2,point.y - faceRadius/2,point.x + faceRadius/2,point.y + faceRadius/2);
    
                    postInvalidate();
                }
            });
            arcVa.setInterpolator(new LinearInterpolator());
            arcVa.setDuration(2000);
            arcVa.setRepeatMode(ValueAnimator.RESTART);
            arcVa.start();
    
            rectF.set(startPoint.x - faceRadius/2,startPoint.y - faceRadius,startPoint.x + faceRadius/2,startPoint.y);
            angle = loadAngle;
            faceVa = ValueAnimator.ofFloat(loadAngle , 0);
    
            faceVa.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {//吃食物动作
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    angle = (float)valueAnimator.getAnimatedValue();
                    postInvalidate();
                }
            });
            faceVa.setInterpolator(new LinearInterpolator());
            faceVa.setDuration(500);
            faceVa.setRepeatMode(ValueAnimator.RESTART);
            faceVa.setRepeatCount(-1);
            faceVa.start();
        }
    
        @Override
        public void startAnim() {//前奏
            effectPaint.setColor(getResources().getColor(R.color.transparent));
            facePaint.setColor(getResources().getColor(R.color.transparent));
            clearPaint.setColor(getResources().getColor(R.color.transparent));
    
            isDraw = true;
            faceVa = ValueAnimator.ofFloat(0 , mHeight + faceRadius);//大球落下
    
            faceVa.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {//吃食物动作
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    float val = (float)valueAnimator.getAnimatedValue();
    
                    startPoint.set(mWidth*1/2 + faceRadius*2,val);
                    movePoint1.set(mWidth*2/3, 0);
                    movePoint2.set(mWidth*5/6, 0);
                    endPoint.set(mWidth - faceRadius*2,val);
    
                    postInvalidate();
                }
            });
            faceVa.setInterpolator(new LinearInterpolator());
            faceVa.setDuration(1000);
            faceVa.setRepeatMode(ValueAnimator.RESTART);
            faceVa.start();
        }
    
        @Override
        public void stopAnim() {
            if (arcVa != null)
                arcVa.cancel();
            if (faceVa != null)
                faceVa.cancel();
            arcVa = null;
            faceVa = null;
            isDraw = false;
        }
    }
    View Code

    最后,欢迎收藏

    GitHub:https://github.com/1024477951/FragmentApp

  • 相关阅读:
    Codeforces 220C
    Codeforces 697D
    HDU 4417
    Codeforces 396C
    Codeforces 246C
    HDU 6333
    HDU 3389
    总结:树上启发式合并
    HDU 6319
    Codeforces 1009G
  • 原文地址:https://www.cnblogs.com/LiuZhen/p/8359611.html
Copyright © 2020-2023  润新知