Android:Loading动画——竜人の剣を喰え

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/DrkCore/article/details/52664088

【转载请注明出处】
笔者:DrkCore (http://blog.csdn.net/DrkCore)
原文链接:(http://blog.csdn.net/drkcore/article/details/52664088)



一、 竜人の剣を喰え

守望先锋作为当下最有火爆的射击游戏之一为我们带了一波网络用语的节奏,你要是不会一句 “溜金哇开呀酷裂” 都不好意思说自己玩过屁股。作为Android狗的笔者最近想写个Loading界面掌握一些新姿势,索性实现一下守望先锋游戏界面的加载图吧。

原版效果如下:

治疗颈椎病的加载图

该截图具有治疗颈椎病的效果,所以请不要在意这5毛画质。最后实现的效果则如下图:

效果

主要用到的都是自定义View、ValueAnimator等基本的Android知识,稍微花点时间都能实现出来的。

二、 绘制守望先锋图标

从网络上找一张清晰度高一点的守望先锋ICON配合PS我们能够很容易地得到图标上的点和角度数据,之后只需要用Canvas绘制出来即可:


private void initIcon() {
    //ICON半径
    iconRadius = dpToPx(DEFAULT_ICON_RADIUS_DP);
    iconWidth = iconRadius * (87 / 300F);
    iconGapWidth = iconRadius * (20 / 300F);
    //按比例算出图标内部图案的每个点的坐标
    iconCornerPoints = new float[10];
    iconCornerPoints[0] = iconRadius * ((300 - 71) / 300F);
    iconCornerPoints[1] = iconRadius * (450 / 300F);

    iconCornerPoints[2] = iconRadius * ((300 - 233) / 300F);
    iconCornerPoints[3] = iconRadius * (295 / 300F);

    iconCornerPoints[4] = iconRadius * ((300 - 287) / 300F);
    iconCornerPoints[5] = iconRadius * (165 / 300F);

    iconCornerPoints[6] = iconRadius * ((300 - 287) / 300F);
    iconCornerPoints[7] = iconRadius * (361 / 300F);

    iconCornerPoints[8] = iconRadius * ((300 - 136) / 300F);
    iconCornerPoints[9] = iconRadius * (505 / 300F);
}

private Bitmap iconBmp;

private void drawIcon(Canvas canvas) {
    float left = centerX - iconRadius;
    float top = centerY - iconRadius;
    float right = centerX + iconRadius;
    float bottom = centerY + iconRadius;
    iconRectF.set(left, top, right, bottom);

    //缓存绘制结果
    if (iconBmp == null) {
        iconBmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas bmpCanvas = new Canvas(iconBmp);
        doDrawIcon(bmpCanvas);
    }
    canvas.drawBitmap(iconBmp, 0, 0, null);
}

private void doDrawIcon(Canvas canvas) {
    //标记图层
    int saveCount = canvas.saveLayer(0, 0, width, height, null, Canvas.ALL_SAVE_FLAG);

    //绘制灰圈
    paint.setStyle(Paint.Style.STROKE);
    paint.setStrokeWidth(iconWidth);
    paint.setColor(iconColor);
    canvas.drawArc(iconRectF, 0, 360, false, paint);

    //抹去交界线
    paint.setXfermode(dstOutMode);
    paint.setStyle(Paint.Style.FILL);
    paint.setStrokeWidth(iconGapWidth);
    paint.setColor(Color.RED);
    canvas.rotate(40, centerX, centerY);
    canvas.drawLine(centerX, centerY, centerX, 0, paint);
    canvas.rotate(-80, centerX, centerY);
    canvas.drawLine(centerX, centerY, centerX, 0, paint);
    paint.setXfermode(null);

    //叠加图层
    canvas.restoreToCount(saveCount);

    //绘制中心的两个角
    iconCornerLeftPath.rewind();
    iconCornerRightPath.rewind();
    float tmpX, tmpY;
    for (int i = 0, len = iconCornerPoints.length; i < len; i += 2) {
        tmpX = iconCornerPoints[i];
        tmpY = iconCornerPoints[i + 1];
        tmpY += centerY - iconRadius;
        if (iconCornerLeftPath.isEmpty()) {
            iconCornerLeftPath.moveTo(centerX - tmpX, tmpY);
            iconCornerRightPath.moveTo(centerX + tmpX, tmpY);
        } else {
            iconCornerLeftPath.lineTo(centerX - tmpX, tmpY);
            iconCornerRightPath.lineTo(centerX + tmpX, tmpY);
        }
    }
    iconCornerLeftPath.close();
    iconCornerRightPath.close();
    paint.setColor(iconColor);
    canvas.drawPath(iconCornerLeftPath, paint);
    canvas.drawPath(iconCornerRightPath, paint);
}

因为图标的部分是静态的我们可以将之用Bitmap缓存起来,这样可以避免在多次绘制中造成的内存抖动。

三、 绘制外圈

我们看到的Loading效果都是动态的,这部分主要用成员变量和ValueAnimator来实现。


    private float middleWidth;
    private float middleRadius;
    private RectF middleRectF = new RectF();

    private int middleBgColor = Color.argb(128, 227, 165, 4);

    private int middleColor = Color.rgb(227, 165, 4);

    private float middleAngel_1 = 116;
    private float middleAngel_2 = -74;
    private float middleAngel_3 = 45;

    private float middleStartAngel = 0;
    private float middleSwipeAngel = 300;

    private void initMiddleStroke() {
        middleWidth = iconWidth * 2 / 3;
        middleRadius = iconRadius + iconWidth / 2 + middleWidth;
    }

    private Animator[] prepareMiddleStrokeAnimator() {
        ValueAnimator[] anim = new ValueAnimator[5];
        anim[0] = ValueAnimator.ofFloat(0, 360);
        anim[0].setRepeatCount(ValueAnimator.INFINITE);
        anim[0].setDuration(1500);
        anim[0].setInterpolator(new LinearInterpolator());
        anim[0].addUpdateListener(animation -> {
            middleStartAngel = (float) animation.getAnimatedValue();
            invalidate();
        });

        anim[1] = ValueAnimator.ofFloat(0, 320);
        anim[1].setRepeatMode(ValueAnimator.REVERSE);
        anim[1].setRepeatCount(ValueAnimator.INFINITE);
        anim[1].setDuration(1500);
        anim[1].setInterpolator(new AccelerateDecelerateInterpolator());
        anim[1].addUpdateListener(animation -> {
            middleSwipeAngel = (float) animation.getAnimatedValue();
            invalidate();
        });

        anim[2] = ValueAnimator.ofFloat(middleAngel_1, middleAngel_1 + 360);
        anim[2].setRepeatCount(ValueAnimator.INFINITE);
        anim[2].setDuration(1500);
        anim[2].setInterpolator(new LinearInterpolator());
        anim[2].addUpdateListener(animation -> {
            middleAngel_1 = -(float) animation.getAnimatedValue();
            invalidate();
        });

        anim[3] = ValueAnimator.ofFloat(middleAngel_2, middleAngel_2 + 360);
        anim[3].setRepeatCount(ValueAnimator.INFINITE);
        anim[3].setDuration(1500);
        anim[3].setInterpolator(new LinearInterpolator());
        anim[3].addUpdateListener(animation -> {
            middleAngel_2 = (float) animation.getAnimatedValue();
            invalidate();
        });

        anim[4] = ValueAnimator.ofFloat(middleAngel_3, middleAngel_3 + 360);
        anim[4].setRepeatCount(ValueAnimator.INFINITE);
        anim[4].setDuration(1500);
        anim[4].setInterpolator(new LinearInterpolator());
        anim[4].addUpdateListener(animation -> {
            middleAngel_3 = -(float) animation.getAnimatedValue();
            invalidate();
        });
        return anim;
    }

    private void drawMiddleStroke(Canvas canvas) {
        float left = centerX - middleRadius;
        float top = centerY - middleRadius;
        float right = centerX + middleRadius;
        float bottom = centerY + middleRadius;
        middleRectF.set(left, top, right, bottom);

        paint.setColor(middleBgColor);
        paint.setStrokeWidth(middleWidth);
        paint.setStyle(Paint.Style.STROKE);
        canvas.drawArc(middleRectF, middleStartAngel, middleSwipeAngel, false, paint);

        paint.setColor(middleColor);
        canvas.drawArc(middleRectF, middleAngel_1, 10, false, paint);
        canvas.drawArc(middleRectF, middleAngel_2, 54, false, paint);
        canvas.drawArc(middleRectF, middleAngel_3, 40, false, paint);
    }

ValueAnimator能够动态改变属性值并且我们在之中调用invalidate(),最后就能得到动画的效果了。

四、 源码

源码请参见我的GitHub:

DrkCore的GitHub

猜你喜欢

转载自blog.csdn.net/DrkCore/article/details/52664088
今日推荐