前言
加载动画主要用于网络请求时提示用户等待,用来提升体验,各家 App 的效果千差万别,大多数应用使用 Progressbar ,也有蛮多设计感十足的加载动画,其中 58同城 的自由落体动画就算一个,先来展示最终效果
文末附上<深入理解Java虚拟机>电子书,包括 Epub,mobi 等格式
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>
动画效果
包括下列动画
下落
上抛
旋转
缩放
下落和上抛都属于属性动画中的位移动画,各个动画效果使用 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(); } }); }
自定义控件:下落物体
下落物体由三个几何图形组成:
圆形
正方形
三角形
我们用在 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
共同学习,写下你的评论
评论加载中...
作者其他优质文章