Android 仿 58同城加载动画


/**
 * Created by Administrator on 2018/6/3.
 */

public class BoundLoadingView extends View {

    private static final int DEFAULT_CIRCLE_COLOR = 0xffff0000;

    private static final int DEFAULT_RECT_COLOR = 0xff00ff00;

    private static final int DEFAULT_TRIANGLE_COLOR = 0xff0000ff;

    private static final int DEFAULT_TEXT_COLOR = Color.BLACK;

    private Paint paint;         // shape paint

    private Paint textPaint;     // loading text paint

    private int circleColor;

    private int rectColor;

    private int triangleColor;

    private float centerX;

    private float radius;

    private int mHeight;
    private float fraction;
    private float degree;
    private Path path;

    private RectF shadowRect;

    private int textGap = dp2Px(30);

    private float textSize;   // loading text size

    private int textColor;    // loading text color

    private String text;  // loading text

    private Paint.FontMetrics fontMetrics;

    private float textHeight; // loading text height

    private float textWidth;  // text width

    private float startY;     // rectangle,circle,triangle 上升起点

    private float endY;       // rectangle,circle,triangle 上升终点

    private float currentY;   // rectangle,circle,triangle 动画过程中当前处于的坐标点


    public BoundLoadingView(Context context) {
        this(context, null);
    }

    public BoundLoadingView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        readAttrs(context, attrs);
        init();
    }


    private void readAttrs(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.BoundLoadingView);
        circleColor = typedArray.getColor(R.styleable.BoundLoadingView_circleColor, DEFAULT_CIRCLE_COLOR);
        rectColor = typedArray.getColor(R.styleable.BoundLoadingView_rectColor, DEFAULT_RECT_COLOR);
        triangleColor = typedArray.getColor(R.styleable.BoundLoadingView_triangleColor, DEFAULT_TRIANGLE_COLOR);
        textColor = typedArray.getColor(R.styleable.BoundLoadingView_textColor, DEFAULT_TEXT_COLOR);
        textSize = typedArray.getDimension(R.styleable.BoundLoadingView_textSize, dp2Px(14));
        text = typedArray.getString(R.styleable.BoundLoadingView_loadingText);
        if (TextUtils.isEmpty(text)) {
            text = "loading...";
        }
        typedArray.recycle();
    }


    private void init() {
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStyle(Paint.Style.FILL);

        textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        textPaint.setTextSize(textSize);
        textPaint.setColor(textColor);
        textPaint.setTextAlign(Paint.Align.CENTER);
        textWidth = textPaint.measureText(text);

        Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
        textHeight = fontMetrics.bottom - fontMetrics.top + fontMetrics.leading;
        Log.d("debug", "init: height ====" + textHeight);

        path = new Path();

        shadowRect = new RectF();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = getMeasuredSize(widthMeasureSpec, (int) textWidth);
        int height = getMeasuredSize(heightMeasureSpec, dp2Px(80));
        setMeasuredDimension(width, height);
    }


    private int getMeasuredSize(int measureSpec, int defaultSize) {
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        if (mode == MeasureSpec.EXACTLY) {
            return size;
        } else if (mode == MeasureSpec.AT_MOST) {
            return Math.min(size, defaultSize);
        } else {
            return defaultSize;
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mHeight = h;
        centerX = w / 2f;

        radius = w > dp2Px(20) ? dp2Px(10) : w / 3f;
        // 上升,下降动画起点和终点
        startY = mHeight - textHeight - textGap - dp2Px(20);
        endY = (float) Math.sqrt(2) * radius;

        startAnimator();

    }

    @Override
    protected void onDraw(Canvas canvas) {
        // draw loading text
        canvas.drawText(text, centerX, mHeight - textHeight, textPaint);

        // draw shadow
        shadowRect.set(centerX - radius * fraction, mHeight - textGap - 10 - textHeight, centerX + radius * fraction, mHeight - textHeight - textGap);
        paint.setColor(Color.BLACK);
        // 设置画笔阴影效果,需要关闭硬件加速
        paint.setMaskFilter(new BlurMaskFilter(4, BlurMaskFilter.Blur.NORMAL));
        canvas.drawOval(shadowRect, paint);


        canvas.save();
        // 将画布原点移动到当前小球,方块所处的坐标点,所以画布旋转中心为(0,0)
        canvas.translate(centerX, currentY);
        canvas.rotate(degree, 0, 0);
        paint.setMaskFilter(null);
        // draw rectangle,triangle,circle
        if (number % 3 == 0) {
            paint.setColor(rectColor);
            // 坐标值相对于中心点(0,0)
            canvas.drawRect(-radius, -radius, radius, radius, paint);
        } else if (number % 3 == 1) {
            paint.setColor(circleColor);
            canvas.drawCircle(0, 0, radius, paint);
        } else if (number % 3 == 2) {
            path.reset();
            path.moveTo((float) (-Math.sqrt(3) / 2f * radius), radius / 2f);
            path.lineTo(0, -radius);
            path.lineTo((float) (Math.sqrt(3) / 2f * radius), radius / 2f);
            path.close();
            paint.setColor(triangleColor);
            canvas.drawPath(path, paint);
        }
        canvas.restore();
    }

    private boolean isUp = true;

    private int number = 0;

    private ValueAnimator jumpAnimator;

    private ValueAnimator shadowAnimator;

    private ValueAnimator rotateAnimator;

    private AnimatorSet animatorSet;

    private AnimatorSet initAnimator() {
        jumpAnimator = ValueAnimator.ofFloat(isUp ? startY : endY, isUp ? endY : startY);
        jumpAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        jumpAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                currentY = (float) animation.getAnimatedValue();
                invalidate();
            }
        });

        shadowAnimator = ValueAnimator.ofFloat(isUp ? 0 : 1.0f, isUp ? 1.0f : 0);
        shadowAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        shadowAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                fraction = (float) animation.getAnimatedValue();
            }
        });

        rotateAnimator = ValueAnimator.ofFloat(isUp ? 0 : 210, isUp ? 210 : 270);
        rotateAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        rotateAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                degree = (float) animation.getAnimatedValue();
            }
        });

        animatorSet = new AnimatorSet();
        animatorSet.setDuration(600);
        animatorSet.playTogether(jumpAnimator, rotateAnimator, shadowAnimator);
        animatorSet.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                isUp = !isUp;
                startAnimator();
                if (isUp) {
                    number++;
                }

            }
        });
        return animatorSet;
    }


    public void startAnimator() {
        AnimatorSet animatorSet = initAnimator();
        if (animatorSet != null) {
            animatorSet.cancel();
        }
        animatorSet.start();
    }

    public void stopAnimator() {
        if (shadowAnimator != null) {
            shadowAnimator.cancel();
        }
        if (rotateAnimator != null) {
            rotateAnimator.cancel();
        }
        if (jumpAnimator != null) {
            jumpAnimator.cancel();
        }
        if (animatorSet != null) {
            animatorSet.cancel();
        }
    }


    private int dp2Px(int dpValue) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, getResources().getDisplayMetrics());
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值