前言
加载动画主要用于网络请求时提示用户等待,用来提升体验,各家 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