打造一个有意思的Wifi加载效果

456 阅读4分钟
原文链接: www.jianshu.com

前言

最近在学习自定义自定义View相关知识,所以除了学习相关知识外,还要上手练习一些实例。 废话不多说: 先上原设计图

图片来自互联网
图片来自互联网

再来一张实现效果图

wifiloading.gif
wifiloading.gif

emmmm,好像不是完全一样啊。大哥,我是模仿,细节问题就不要考虑了吧。

分析阶段

在此之前先复习两个PathMeasure的方法:

  1. getPosTan(float distance, float pos[],float tan[]) - path 为 null ,返回 false

  2. getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo) 截取path片段,传入起点和终点

首先拿到设计图,分析一波:

5个小球,bulingbuling的发光。然后摆动起来,边摆边画线。

  • 5个小球----canvas.drawCircle方法即可实现,没难度。

  • bulingbuling的发光---使用画笔的setShadowLayer属性可以让画出的线变的有发亮的效果。 具体使用如下:

public void setShadowLayer (float radius, float dx, float dy, int color)

radius:阴影半径

dx:X轴方向的偏移量

dy:Y轴方向的偏移量

color:阴影颜色

要想bulingbuling,就要使用动画让阴影半径变化起来。 所以可以设置一个动画来绘制这个效果。

  • 5个小球的摆动并绘线。
    使用Path绘制5个弧线,同时使用pathMeasure测量path的位置。然后绘制圆球中心为path的位置。这样就能摆动起来。 同时为了绘线,pathMeasure得到某一长度的path绘制出来。

代码实现

创建枚举,控制动画状态:

public enum State {
        START, STATE1, STATE2, STATE3, STATE4, STATE5, END
    }

相关初始化:

private void init() {
        paint1 = new Paint();
        paint1.setAntiAlias(true);
        paint1.setColor(0xff1aadc0);

        paint2 = new Paint();
        paint2.setColor(0xff1aadc0);
        paint2.setStrokeWidth(40);
        paint2.setAntiAlias(true);
        paint2.setStyle(Paint.Style.STROKE);
        paint2.setStrokeCap(Paint.Cap.ROUND);//笔尖
        pathMeasure = new PathMeasure();

        initAnimator();
        initHandler();
    }

Handler和Animator的初始化都很简单,这里不再描述,详情可以看代码。

核心部分

接下来就是核心绘图部分,具体看注释:

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(getWidth() / 2, getHeight() / 2); //坐标原点到View中心
        int spaceCircle = 30; //设置球之间的间隔
        canvas.drawColor(0xff000000); //View背景绘制 可以去掉
        paint1.setShadowLayer(30 * mBulingValue, 0, 0, colors[0]); //动画设置path发光半径,bulingbuling
        paint2.setShadowLayer(30 * mBulingValue, 0, 0, colors[0]);

        switch (mCurrentState) {
            case START:

                for (int i = 0; i < 5; i++) {

                    paint1.setStyle(Paint.Style.FILL); //画笔样式
                    canvas.drawCircle(0, -(spaceCircle + 40) * i, 20, paint1);
                }

                break;
            case STATE1:
                paths.clear();
                // 5条path计算出来
                for (int i = 0; i < 5; i++) {
                    Path path = new Path();
                    RectF oval = new RectF(-(40 + spaceCircle) * i, -(40 + spaceCircle) * i, (40 + spaceCircle) * i, (40 + spaceCircle) * i);
                    path.addArc(oval, -90, 60);
                    paths.add(path);
                }

                // 5个小球绘制,并且通过pathMeasure.getPosTan 获得path位置 摆动起来
                for (int i = 0; i < 5; i++) {
                    pathMeasure.setPath(paths.get(i), false);
                    paint1.setStyle(Paint.Style.FILL);
                    float[] pos = new float[2];
                    pathMeasure.getPosTan(pathMeasure.getLength() * mAnimatorValue, pos, null);//获得当前path位置
                    canvas.drawCircle(pos[0], pos[1], 20, paint1);
                }

                break;

            case STATE2:
                paths.clear();
                // 这里 同样计算5条path,但是要绘制出第一条,因为第一条已经画好了 下面类似
                for (int i = 0; i < 5; i++) {
                    Path path = new Path();
                    RectF oval = new RectF(-(40 + spaceCircle) * i, -(40 + spaceCircle) * i, (40 + spaceCircle) * i, (40 + spaceCircle) * i);
                    path.addArc(oval, -30, -120);
                    paths.add(path);
                    if (i < 1) {
                        canvas.drawPath(path, paint2);
                    }
                }


                //去掉一个点不画,已经用path代替了
                for (int i = 0; i < 5; i++) {
                    if (i > 0 || i == 0) {
                        pathMeasure.setPath(paths.get(i), false);
                        paint1.setStyle(Paint.Style.FILL);
                        float[] pos = new float[2];
                        pathMeasure.getPosTan(pathMeasure.getLength() * mAnimatorValue, pos, null);
                        canvas.drawCircle(pos[0], pos[1], 20, paint1);
                    }
                }
                pathMeasure.setPath(paths.get(1), false);
                Path pathDraw2 = new Path();
                pathMeasure.getSegment(0, pathMeasure.getLength() * mAnimatorValue, pathDraw2, true);
                canvas.drawPath(pathDraw2, paint2);
                break;

            case STATE3:
                paths.clear();
                for (int i = 0; i < 5; i++) {
                    Path path = new Path();
                    RectF oval = new RectF(-(40 + spaceCircle) * i, -(40 + spaceCircle) * i, (40 + spaceCircle) * i, (40 + spaceCircle) * i);          // 外圈
                    path.addArc(oval, -150, 120);
                    paths.add(path);
                    if (i < 2) {
                        canvas.drawPath(path, paint2);
                    }
                }

                for (int i = 0; i < 5; i++) {
                    if (i > 1 || i == 0) {
                        pathMeasure.setPath(paths.get(i), false);
                        paint1.setStyle(Paint.Style.FILL);
                        float[] pos = new float[2];
                        pathMeasure.getPosTan(pathMeasure.getLength() * mAnimatorValue, pos, null);
                        canvas.drawCircle(pos[0], pos[1], 20, paint1);
                    }
                }
                pathMeasure.setPath(paths.get(2), false);
                Path pathDraw3 = new Path();
                pathMeasure.getSegment(0, pathMeasure.getLength() * mAnimatorValue, pathDraw3, true);
                canvas.drawPath(pathDraw3, paint2);
                break;
            case STATE4:
                paths.clear();
                for (int i = 0; i < 5; i++) {
                    Path path = new Path();
                    RectF oval = new RectF(-(40 + spaceCircle) * i, -(40 + spaceCircle) * i, (40 + spaceCircle) * i, (40 + spaceCircle) * i);          // 外圈
                    path.addArc(oval, -30, -120);
                    paths.add(path);
                    if (i < 3) {
                        canvas.drawPath(path, paint2);
                    }
                }

                for (int i = 0; i < 5; i++) {
                    if (i > 2 || i == 0) {
                        pathMeasure.setPath(paths.get(i), false);
                        paint1.setStyle(Paint.Style.FILL);
                        float[] pos = new float[2];
                        pathMeasure.getPosTan(pathMeasure.getLength() * mAnimatorValue, pos, null);
                        canvas.drawCircle(pos[0], pos[1], 20, paint1);
                    }

                }
                pathMeasure.setPath(paths.get(3), false);
                Path pathDraw4 = new Path();
                pathMeasure.getSegment(0, pathMeasure.getLength() * mAnimatorValue, pathDraw4, true);
                canvas.drawPath(pathDraw4, paint2);
                break;
            case STATE5:
                paths.clear();
                for (int i = 0; i < 5; i++) {
                    Path path = new Path();
                    RectF oval = new RectF(-(40 + spaceCircle) * i, -(40 + spaceCircle) * i, (40 + spaceCircle) * i, (40 + spaceCircle) * i);          // 外圈
                    path.addArc(oval, -150, 120);
                    paths.add(path);
                    if (i < 4) {
                        canvas.drawPath(path, paint2);
                    }
                }

                for (int i = 0; i < 5; i++) {
                    if (i > 3 || i == 0) {
                        pathMeasure.setPath(paths.get(i), false);
                        paint1.setStyle(Paint.Style.FILL);
                        float[] pos = new float[2];
                        pathMeasure.getPosTan(pathMeasure.getLength() * mAnimatorValue, pos, null);
                        canvas.drawCircle(pos[0], pos[1], 20, paint1);
                    }
                }

                pathMeasure.setPath(paths.get(4), false);
                Path pathDraw5 = new Path();
                pathMeasure.getSegment(0, pathMeasure.getLength() * mAnimatorValue, pathDraw5, true);
                canvas.drawPath(pathDraw5, paint2);
                break;

            case END:
                paths.clear();
                for (int i = 0; i < 5; i++) {
                    Path path = new Path();
                    RectF oval = new RectF(-(40 + spaceCircle) * i, -(40 + spaceCircle) * i, (40 + spaceCircle) * i, (40 + spaceCircle) * i);          // 外圈
                    path.addArc(oval, -150, 120);
                    paths.add(path);
                    if (i < 5) {
                        canvas.drawPath(path, paint2);
                    }
                }


                pathMeasure.setPath(paths.get(0), false);
                paint1.setStyle(Paint.Style.FILL);
                float[] pos = new float[2];
                pathMeasure.getPosTan(pathMeasure.getLength() * mAnimatorValue, pos, null);
                canvas.drawCircle(pos[0], pos[1], 20, paint1);


                break;
        }


    }

值得注意的是:除了第一个点(因为第一个点就是path,没有运动),其他的4个每绘制出一条path后,都要减少一个球的绘制。因为path代替了球。 所以需要判断控制球和path的数量。

项目地址

最后附上github----项目地址,如果喜欢。欢迎fork和start。