为了账号安全,请及时绑定邮箱和手机立即绑定

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

标签:
Android

前言

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

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

https://img1.sycdn.imooc.com//5d2c93af0001a31704200605.jpg

1545820706607_video.gif

目录

  • 分析

  • 动画效果

  • 自定义控件:下落物体

  • 自定义控件:阴影

  • 深入理解JVM

分析

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

<com.loadingview.LoadingView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center">

    // 下落物体    <com.loadingview.LoadingShapeView
        android:id="@+id/loading_shape"
        android:layout_width="28dp"
        android:layout_height="28dp"
        />

    // 物体下方的阴影    <com.loadingview.LoadingShadowView
        android:id="@+id/loading_indicator"
        android:layout_width="28dp"
        android:layout_height="4dp"
        android:layout_marginTop="80dp" />
 
    <TextView
        android:id="@+id/loading_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="正在加载"
        android:layout_marginTop="12dp"
        android:textSize="12dp" /></com.loadingview.LoadingView>

动画效果

包括下列动画

  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);
    }



作者:毛先森
链接:https://www.jianshu.com/p/3c4dfc4541be


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消