美文网首页UI效果仿写自定义Android 自定义view
高仿小米"安全中心"中垃圾清理成功后的显示界

高仿小米"安全中心"中垃圾清理成功后的显示界

作者: 美猴王丶 | 来源:发表于2016-11-16 22:48 被阅读515次

    前言#

    工作多年,有史以来,第一次尝试在简书和github上开始分享之旅,惭愧之际。为什么选择简书,简洁,大方,得体。为什么首先选择自定义控件这个知识点,因为是我薄弱地方,所以我要重新认识,再加深理解。本人用的小米手机,所以就直接在小米手机上面寻找模仿的UI界面咯。

    效果预览#

    为了做这个gif动画,还是花了点心思,第一次弄。
    第一步:先用手机录屏软件或者其他方式(自行搜索)先录制好mp4文件。
    第二步:我用的是mac,推荐在AppStore下载VideoGIF Free这款软件制作,将mp4转成gif。

    运行效果动态图

    实现思路#

    1、自定义view,首先利用canvas.drawArc不断的画圆弧,通过改变角度值达到从0到有的圆圈。
    2、等角度达到一定值的时候,开始显示绘制正确完成的钩子。
    3、开始绘制圆弧的时候,一并绘制周围的三角形,这个是随机生成,然后慢慢想最新靠拢。
    4、整体绘制完成,显示提示文字。

    1、画圆弧,并且不断加角度,让其最后绘制成一个圆。通过postInvalidateDelayed方法让圆弧不停的自我绘制,直到角度的值等于360°算绘制完毕。绘制完毕后,回调调用完成的接口。

        /**
         * 画圆弧
         */
        private void drawArc(Canvas canvas) {
            RectF rectF = new RectF();
            rectF.left = (getWidth() - arcWidth) / 2;
            rectF.top = (getHeight() - arcWidth) / 2;
            rectF.right = (getWidth() - arcWidth) / 2 + arcWidth;
            rectF.bottom = (getHeight() - arcWidth) / 2 + arcWidth;
            if (isDraw) {
                //未运行的的时候可以执行一次
                if (!isDrawing) {
                    startBezierAnimotion();
                }
    
                isDrawing = true;
                canvas.drawArc(rectF, -90, arcAngle, false, mArcPaint);
                arcAngle += ARC_CREATE_ANGLE;
    
                if (arcAngle > 330) {
                    drawRight(canvas);
                }
    
                postInvalidateDelayed(ARC_CREATE_TIME); //每隔一段时间开始绘制
    
                if (arcAngle >= 360) {
                    isDraw = false;
                }
    
                drawTriAngle(canvas);
            } else {
                canvas.drawArc(rectF, -90, 360, false, mArcPaint);
                drawRight(canvas);
                isDrawing = false;
                if(listener != null) {
                    listener.onAnimotionFinished();
                }
            }
        }
    

    2、绘制钩子。当角度大于330°的时候,绘制钩子,钩子是根据圆弧的相对角度绘制而成的,利用path就可以做到。

        /**
         * 画正确的钩子
         *
         * @param canvas\
         */
        private void drawRight(Canvas canvas) {
            Path path = new Path();
            path.moveTo((getWidth() - arcWidth) / 2 + arcWidth / 4 + arcWidth / 16, (getHeight() - arcWidth) / 2 + arcWidth / 2);
            path.lineTo((getWidth() - arcWidth) / 2 + arcWidth / 4 + arcWidth / 8 + arcWidth / 16, (getHeight() - arcWidth) / 2 + (arcWidth / 4) * 3 - arcWidth / 8);
            path.lineTo((getWidth() - arcWidth) / 2 + arcWidth / 2 + arcWidth / 4 - arcWidth / 8 + arcWidth / 16, (getHeight() - arcWidth) / 2 + arcWidth / 4 + arcWidth / 8);
            canvas.drawPath(path, mRightPaint);
        }
    

    3、圆弧转动的动的时候,周围生成若干个随机的三角形,这里我生成的是十个。三角形的所处的位置以及运动的轨迹由ValueAnimator动画生成。

        /**
         * 坐标点的位置,用户传递到属性动画当中
         */
        public class Point implements Serializable {
            public int x;
            public int y;
    
            public Point(int x, int y) {
                this.x = x;
                this.y = y;
            }
    
            public Point(Point src) {
                this.x = src.x;
                this.y = src.y;
            }
    
            public void set(int x, int y) {
                this.x = x;
                this.y = y;
            }
        }
    
        /**
         * 用于属性动画的回掉,根据返回的参数t来计算相关数据,t是从0~1变化。
         */
        public class BezierEvaluators implements TypeEvaluator<List<Point>> {
    
            private Point controllPoint;
    
            public BezierEvaluators(Point controllPoint) {
                this.controllPoint = controllPoint;
            }
    
            @Override
            public List<Point> evaluate(float t, List<Point> startValue, List<Point> endValue) {
                List<Point> list = new ArrayList<>();
                if (startValue.size() > 0 && endValue.size() > 0 && startValue.size() == endValue.size()) {
                    for (int i = 0; i < startValue.size(); i++) {
                        //增加角度,让每个碎片三角形所处位置不一样
                        double sum = t + ((double) i / (double) 10);
                        if (sum >= 1) {
                            sum = sum - 1;
                        }
                        // 200 - (80 * t) 旋转越来越靠近圆
                        double x = controllPoint.x + (arcWidth / 2 + 200 - (80 * t)) * Math.cos(360 * sum * Math.PI / 180);
                        double y = controllPoint.y + (arcWidth / 2 + 200 - (80 * t)) * Math.sin(360 * sum * Math.PI / 180);
                        list.add(new Point((int) x, (int) y));
                    }
                }
                return list;
            }
        }
    
        /**
         * 开始运动三角形
         */
        private void startBezierAnimotion() {
            if(isShowTriAngle == 1) {
                float circleX = (getWidth() - arcWidth) / 2 + arcWidth / 2;
                float circleY = (getHeight() - arcWidth) / 2 + arcWidth / 2;
                float x = ((getWidth() - arcWidth) / 2) / 2;
                Point controllPoint = new Point((int) circleX, (int) circleY);
                BezierEvaluators bezierEvaluator = new BezierEvaluators(controllPoint);
                //初始化开始坐标和结束坐标,在这里这个坐标并使用,只是为了能够得属性动画产生的变化值
                List<Point> startPoint = new ArrayList<>();
                List<Point> endPoint = new ArrayList<>();
                triAngle.clear();
                for (int i = 0; i < 10; i++) {
                    startPoint.add(new Point(i, i));    //随意生成
                    endPoint.add(new Point(i, i));      //随意生成
    
                    //半径之外的范围 150 - 200之间
                    double r = Math.random() * 30 + 20;
                    triAngle.add((int) r);
                }
                ValueAnimator anim = ValueAnimator.ofObject(bezierEvaluator, startPoint, endPoint);
                anim.addUpdateListener(this);
                anim.setDuration(1500);
                anim.setInterpolator(new AccelerateDecelerateInterpolator());   //动画运动的速率,开始和结束慢,中间快,还有其他方式可设置。
                anim.start();
            }
        }
    
        /**
         * 回调过来的数据,开始更新,画三角形,这样子就形成了三角形围着圆弧转,并且越靠越近,最后消失。
         */
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            List<Point> valuePoints = (List<Point>) animation.getAnimatedValue();
            this.valuePoints = valuePoints;
            invalidate();
        }
    

    4、动画执行完毕之后,通知接口已完成。

                canvas.drawArc(rectF, -90, 360, false, mArcPaint);
                drawRight(canvas);
                isDrawing = false;
                if(listener != null) {
                    listener.onAnimotionFinished();
                }
    

    结语#

    到这里,大致流程就介绍完了。有什么不足的,欢迎指出。
    欢迎在下方评论和喜欢,谢谢,转载请说明出处。

    欢迎在Github上Star,详细的DEMO地址在https://github.com/yangxixi88/ProcessSuccessView

    相关文章

      网友评论

      • 7e043151b847:3、evaluate里面不用每次都new一个List和一堆Point,可以复用的
        4、(getHeight() - arcWidth) / 2 + arcWidth / 2; :relieved:
        其它的就不说了,建议以后提代码前先看看。
        美猴王丶: @polyfuss 第三条list可以不用每次new,但是point为了计算随机位置,所以得每次new。第四条是微调调成这样子的,哈哈
      • 7e043151b847:1、drawArc里面的那个Rect可以提出来,用不着次次都new一下吧
        2、系统自带Point类
        美猴王丶: @polyfuss 这两条我非常同意,谢谢😀

      本文标题:高仿小米"安全中心"中垃圾清理成功后的显示界

      本文链接:https://www.haomeiwen.com/subject/ecirpttx.html