前言
加载动画主要用于网络请求时提示用户等待,用来提升体验,各家 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);
}
深入理解JVM
提取码:bn53
网友评论