自定义控件-58同城加载动画

前言

加载动画主要用于网络请求时提示用户等待,用来提升体验,各家 App 的效果千差万别,大多数应用使用 Progressbar ,也有蛮多设计感十足的加载动画,其中 58同城 的自由落体动画就算一个,先来展示最终效果

文末附上<深入理解Java虚拟机>电子书,包括 Epub,mobi 等格式

1545820706607_video.gif

目录

  • 分析
  • 动画效果
  • 自定义控件:下落物体
  • 自定义控件:阴影
  • 深入理解JVM

分析

该动画可用组合控件 + AnimatorSet 实现,组合控件继承自 LinearLayout,由两个自定义控件加 TextView 实现



    // 下落物体
    

    // 物体下方的阴影
    
 
    




动画效果

包括下列动画

  1. 下落
  2. 上抛
  3. 旋转
  4. 缩放

下落和上抛都属于属性动画中的位移动画,各个动画效果使用 AnimatorListener + AnimatorSet 互相衔接,比如当物体下落到底部时,阴影随物体下落而缩小,当下落到最低点时此刻也意味着物体上抛+物体旋转动画+阴影放大动画可以开始执行了.

  private void freeFallAnimator() {
        // 动画集合
        mFreeFallAnimatorSet = new AnimatorSet();
        // 自由落体动画
        ObjectAnimator freeFallAnimator  = ObjectAnimator.ofFloat(mLoadingShape,"translationY",-mTranslationY,mTranslationY*2);
        // 底部阴影动画
        ObjectAnimator shadowScaleAnimator = ObjectAnimator.ofFloat(mLoadingIndicator,"scaleX",1,0.2f);
        mFreeFallAnimatorSet.setDuration(ANIMATOR_DURATION);
        mFreeFallAnimatorSet.playTogether(freeFallAnimator,shadowScaleAnimator);
        mFreeFallAnimatorSet.start();
        mFreeFallAnimatorSet.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                // 上抛动画
                throwUpAnimator();
                // 切换形状
                mLoadingShape.exchangeShape();
            }
        });
    }
    
    
       /**
     * 向上回弹动画
     */
    private void throwUpAnimator() {
        // 上抛动画集合
        mThrowUpAnimatorSet = new AnimatorSet();
        // 垂直上抛
        ObjectAnimator throwUpAnimator = ObjectAnimator.ofFloat(mLoadingShape,"translationY",mTranslationY*2,-mTranslationY);
        throwUpAnimator.setInterpolator(new DecelerateInterpolator());
        ObjectAnimator rotationAnimator = ObjectAnimator.ofFloat(mLoadingShape,"rotation",0,160);
        ObjectAnimator shadowScaleAnimator = ObjectAnimator.ofFloat(mLoadingIndicator,"scaleX",0.2f,1);
        mThrowUpAnimatorSet.playTogether(throwUpAnimator,shadowScaleAnimator,rotationAnimator);
        mThrowUpAnimatorSet.setDuration(ANIMATOR_DURATION);
        mThrowUpAnimatorSet.start();
        mThrowUpAnimatorSet.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                freeFallAnimator();
            }
        });
    }

自定义控件:下落物体

下落物体由三个几何图形组成:

  1. 圆形
  2. 正方形
  3. 三角形

我们用在 onLayout 方法中获取控件宽高,对比宽高的大小取最小值,以此来获得一个正方形的绘制区域,等会各种图形都将在该区域内被绘制.

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        mWidth = getWidth();
        mHeight = getHeight();

        if (mWidth > mHeight) {
            mWidth = mHeight;
        } else {
            mHeight = mWidth;
        }
        // 绘制区域
        int viewleft = 0;
        int viewTop = 0;
        int viewRight = mWidth;
        int viewBottom = mHeight;
        mRect = new Rect(viewleft, viewTop, viewRight, viewBottom);
        mCenterX = mWidth/2;
        mCenterY = mHeight/2;
        mRadius = mWidth/2;
    }

重写 onDraw 方法,运用 Paint 在 Canvas 上绘制图形,调用 Canvas.drawXXX 方法就可轻松绘制常见的形状

   @Override
    protected void onDraw(Canvas canvas) {

        switch(mCurrentShape){
            case Cirle:
                drawCircle(canvas);
                break;
            case Triangle:
                drawTriangle(canvas);
                break;
            case Rectangle:
                drawRectangle(canvas);
                break;
            default:
                break;
        }
    }

  • 绘制圆形
 private void drawCircle(Canvas canvas) {
        mPaint.setColor(getContext().getResources().getColor(R.color.circleColor));
        // 绘制
        canvas.drawCircle(mCenterX, mCenterY, mRadius,mPaint);
    }

  • 绘制三角形

先使用 Path 画出两条线段,然后调用 path.close 方法将图形自动闭合

    private void drawTriangle(Canvas canvas) {
        mPaint.setColor(getContext().getResources().getColor(R.color.triangleColor));
        if (mPath == null) {
            mPath = new Path();
            mPath.moveTo(mWidth / 2, 0);
            mPath.lineTo(0, mHeight / 2);
            mPath.lineTo(mWidth, mHeight / 2);
            mPath.close();
        }
        canvas.drawPath(mPath,mPaint);
    }
  • 绘制正方形
  private void drawRectangle(Canvas canvas) {
        mPaint.setColor(getContext().getResources().getColor(R.color.rectangleColor));
        canvas.drawRect(mRect,mPaint);
    }
  • 物体变化
   public void exchangeShape(){
        // 设置初始形状
        if (mCurrentShape == null)
            mCurrentShape = Shape.Cirle;

        switch (mCurrentShape){
            case Cirle:
                mCurrentShape = Shape.Rectangle;
                break;
            case Rectangle:
                mCurrentShape = Shape.Triangle;
                break;
            case Triangle:
                mCurrentShape = Shape.Cirle;
                break;
            default:
                break;
        }
        invalidate();
    }

自定义控件:阴影

阴影部分其实就是一个椭圆,通过 Canvas.drawOval 方法很容易就做好了


    // 绘制阴影椭圆
    @Override
    protected void onDraw(Canvas canvas){
        drawOval(canvas);
    }

    private void drawOval(Canvas canvas) {
        mPaint.setColor(getContext().getResources().getColor(R.color.shadowColor));
        canvas.drawOval(mRect,mPaint);
    }

深入理解JVM

电子书网盘链接

提取码:bn53

你可能感兴趣的:(自定义控件-58同城加载动画)