参考链接
https://www.jianshu.com/p/e4de28b4d8ac
效果

一.动画分类介绍
帧动画 和 补间动画
帧动画:一张一张的图片不断轮巡播放
补间动画:位移,透明度,像缩放,改变的是View的属性(属性动画属于补间动画)
二.效果分析
2.1 加载显示组合控件(布局) 布局三个部分 = ShapeView + View(阴影背景 shape=“oval”) + TextView
2.2 编写自定view ShapeView
2.3 将布局加载到自定义ViewGroup LoadView中
2.3 分析实现动画
2.3.1第一部分 关于阴影:下落位移的时候配合中间阴影缩小,上抛的时候配合中间阴影放大
2.3.2第二部分 关于形状 1.下落动画+上抛动画 2.差值器,动画速率的问题,下落的时候如果是小球在真实生活中,下落的速度应该是越来越快,上抛的速度应该是越来越慢 3.下落的时候改变形状 4.旋转动画,正方形 180 圆形 三角120
三.性能优化
当后台数据返回的时候我们要把当前页面,设置成 gone (隐藏),只是用代码设置为了隐藏,但是动画View的内存还在跑,怎么办?
释放动画资源
四.部分实现代码
xml部分
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff">
<com.example.custom58loadview.ShapeView
android:id="@+id/shape_view"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginBottom="80dp"
android:layout_above="@+id/image_shadow"
android:layout_centerInParent="true" />
<ImageView
android:id="@+id/image_shadow"
android:layout_width="50dp"
android:layout_height="4dp"
android:layout_centerInParent="true"
android:layout_margin="15dp"
android:src="@drawable/shadow_view" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/image_shadow"
android:layout_centerInParent="true"
android:layout_marginTop="5dp"
android:text="玩命加载中..." />
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.example.custom58loadview.CustomLoadView
android:id="@+id/load_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
</RelativeLayout>
Java部分
public class ShapeView extends View {
@ShapeType
private int mCurrentShape = ShapeType.CIRCLE;
private Paint mCirclePaint, mTrianglePaint, mSquarePaint;
private Path mPath;
public ShapeView(Context context) {
this(context, null);
}
public ShapeView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ShapeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mCirclePaint = initPaintByColor(Color.RED);
mTrianglePaint = initPaintByColor(Color.YELLOW);
mSquarePaint = initPaintByColor(Color.GRAY);
}
private Paint initPaintByColor(int color) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setDither(true);
paint.setColor(color);
return paint;
}
@Retention(RetentionPolicy.SOURCE)
@IntDef({ShapeType.CIRCLE, ShapeType.SQUARE, ShapeType.TRIANGLE})
public @interface ShapeType {
int CIRCLE = 0;
int SQUARE = 1;
int TRIANGLE = 2;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(Math.min(width, height), Math.min(width, height));
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
switch (mCurrentShape) {
case ShapeType.CIRCLE:
// 画圆形
int center = getWidth() / 2;
canvas.drawCircle(center, center, center, mCirclePaint);
break;
case ShapeType.SQUARE:
// 画正方形
canvas.drawRect(0, 0, getWidth(), getHeight(), mSquarePaint);
break;
case ShapeType.TRIANGLE:
// 画三角 Path 画路线
if (mPath == null) {
mPath = new Path();
mPath.moveTo(getWidth() >> 1, 0);
//勾股定理
mPath.lineTo(0, (float) (Math.sqrt(3) * getWidth() / 2));
mPath.lineTo(getWidth(), ((float) Math.sqrt(3) * getWidth() / 2));
mPath.close();//直接连接到起点
}
canvas.drawPath(mPath, mTrianglePaint);
break;
}
}
public void exchange() {
switch (mCurrentShape) {
case ShapeType.CIRCLE:
mCurrentShape = ShapeType.SQUARE;
break;
case ShapeType.SQUARE:
mCurrentShape = ShapeType.TRIANGLE;
break;
case ShapeType.TRIANGLE:
mCurrentShape = ShapeType.CIRCLE;
break;
}
// 不断重新绘制形状
invalidate();
}
public int getCurrentShape() {
return mCurrentShape;
}
}
public class CustomLoadView extends RelativeLayout {
private static final long ANIMATOR_DURATION = 1000;
private ShapeView mShapeView;
private ImageView mShadowView;
private int mShapeTransactionY;//他的值为ShapeView的margin bottom
public CustomLoadView(Context context) {
this(context, null);
}
public CustomLoadView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomLoadView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
inflate(context, R.layout.load_view, this);
//上面的这句等价于下面的两句
// LayoutInflater layoutInflater = LayoutInflater.from(context);
// layoutInflater.inflate(R.layout.load_view,this,true);
//初始化各个部件 用于后期动画
mShapeView = findViewById(R.id.shape_view);
mShadowView = findViewById(R.id.image_shadow);
mShapeTransactionY = ((MarginLayoutParams) mShapeView.getLayoutParams()).bottomMargin;
post(new Runnable() {
@Override
public void run() {
// onResume 之后View绘制流程执行完毕之后(View的绘制流程源码分析那一章)
startFullDownAnimate();
}
});
// onCreate() 方法中执行 ,布局文件解析 反射创建实例
}
//和下落动画相反
private void startJumpUpAnimate() {
//关于形状的动画
// 1.下落动画+上抛动画
// 这里是弹起// Y值越大 越在下方
ObjectAnimator shapeTransactionAnimate = ObjectAnimator.ofFloat(mShapeView, "translationY", mShapeTransactionY, 0);
// 2.差值器,动画速率的问题,下落的时候如果是小球在真实生活中,下落的速度应该是越来越快,上抛的速度应该是越来越慢
// 这里是下落
DecelerateInterpolator decelerateInterpolator = new DecelerateInterpolator();
// 3.下落的时候改变形状
// mShapeView.exchange();
// 4.旋转动画,正方形 180 圆形 三角120
float rotationValue = -180;
switch (mShapeView.getCurrentShape()) {
case ShapeView.ShapeType.SQUARE:
rotationValue = -180;
break;
case ShapeView.ShapeType.TRIANGLE:
rotationValue = -120;
break;
}
ObjectAnimator shapeRotateAnimate = ObjectAnimator.ofFloat(mShapeView, "rotation", rotationValue);
ObjectAnimator shadowScaleAnimate = ObjectAnimator.ofFloat(mShadowView, "scaleX", 0.3f, 1);//从0.3增加到原有宽度
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(ANIMATOR_DURATION);
animatorSet.setInterpolator(decelerateInterpolator);
animatorSet.playTogether(shapeTransactionAnimate, shapeRotateAnimate, shadowScaleAnimate);
animatorSet.start();
//当动画下落完之后就下落了
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// 下落完之后就上抛了
startFullDownAnimate();
}
});
}
private void startFullDownAnimate() {
// 关于阴影的动画 下落位移的时候配合中间阴影缩小,上抛的时候配合中间阴影放大
//mShadowView.setScaleX(); //里面的参数的定义可以拿出来直接作为ObjectAnimator.ofFloat的第二个参数 表示动画的类型
//关于形状的动画
// 1.下落动画+上抛动画
// 这里是从高处落下 // Y值越大 越在下方
ObjectAnimator shapeTransactionAnimate = ObjectAnimator.ofFloat(mShapeView, "translationY", 0, mShapeTransactionY);
// 2.差值器,动画速率的问题,下落的时候如果是小球在真实生活中,下落的速度应该是越来越快,上抛的速度应该是越来越慢
// 这里是下落
AccelerateInterpolator accelerateInterpolator = new AccelerateInterpolator();
// 3.下落的时候改变形状
mShapeView.exchange();
// 4.旋转动画,正方形 180 圆形 三角120
float rotationValue = 0;
switch (mShapeView.getCurrentShape()) {
case ShapeView.ShapeType.SQUARE:
rotationValue = 180;
break;
case ShapeView.ShapeType.TRIANGLE:
rotationValue = 120;
break;
}
ObjectAnimator shapeRotateAnimate = ObjectAnimator.ofFloat(mShapeView, "rotation", rotationValue);
ObjectAnimator shadowScaleAnimate = ObjectAnimator.ofFloat(mShadowView, "scaleX", 1, 0.3f);//从原有宽度缩短到0.3
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(ANIMATOR_DURATION);
animatorSet.setInterpolator(accelerateInterpolator);
animatorSet.playTogether(shapeTransactionAnimate, shapeRotateAnimate, shadowScaleAnimate);
// 先执行 translationAnimator 接着执行 scaleAnimator
// animatorSet.playSequentially(translationAnimator,scaleAnimator);
animatorSet.start();
//当动画下落完之后就上抛了
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// 下落完之后就上抛了
startJumpUpAnimate();
}
});
}
//加载完毕调用 释放动画资源
public void loadComplete() {
mShapeView.clearAnimation();
mShadowView.clearAnimation();
// 把LoadingView从父布局移除
ViewGroup parent = (ViewGroup) getParent();
if (parent != null) {
parent.removeView(this);// 从父布局移除
removeAllViews();// 移除自己所有的View
}
}
}
代码
https://github.com/caihuijian/learn_darren_android.git
952

被折叠的 条评论
为什么被折叠?



