ShapeLoadingView
阴影布局--shadow_view.xml
<?xml version="1.0" encoding="utf-8"?>
<!--底部阴影部分-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#11000000"/>
</shape>
阴影布局--loading_layout.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">
<com.android.myapplication.demo7.MulShapeView
android:id="@+id/shape"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginBottom="50dp"
android:layout_centerInParent="true"
android:layout_above="@+id/image_shadow"
/>
<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="loading..." />
</RelativeLayout>
MulShapeView.java--多形状View
package com.android.myapplication.demo7;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
public class MulShapeView extends View {
private int mCurrentShape = ShapeType.CIRCLE;
private Paint circlePaint, rectPaint, trianglePaint;
private Path mPath;//画三角形所需
public MulShapeView(Context context) {
this(context, null);
}
public MulShapeView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MulShapeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//init
circlePaint = initPaint(Color.RED);
rectPaint = initPaint(Color.MAGENTA);
trianglePaint = initPaint(Color.YELLOW);
}
private Paint initPaint(int color) {
Paint p = new Paint();
p.setAntiAlias(true);
p.setDither(true);
p.setColor(color);
p.setStyle(Paint.Style.FILL);//默认
return p;
}
public interface ShapeType {
int CIRCLE = 0;
int RECT = 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, circlePaint);
break;
case ShapeType.RECT:
int centerX = getWidth();
int centerY = getHeight();
canvas.drawRect(0, 0, centerX, centerY, rectPaint);
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, trianglePaint);
break;
}
}
public void exchange(){
switch (mCurrentShape){
case ShapeType.CIRCLE:
mCurrentShape = ShapeType.RECT;
break;
case ShapeType.RECT:
mCurrentShape = ShapeType.TRIANGLE;
break;
case ShapeType.TRIANGLE:
mCurrentShape = ShapeType.CIRCLE ;
break;
}
//重新绘制形状
invalidate();
}
public int getCurrentShape(){
return mCurrentShape;
}
}
ShapeLoadingView.java--loading
package com.android.myapplication.demo7;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import com.android.myapplication.R;
import androidx.annotation.Nullable;
/**
* @author
* https://blog.csdn.net/u011109881/article/details/112167257
* https://github.com/caihuijian/learn_darren_android
*/
public class ShapeLoadingView extends RelativeLayout {
private static final int ANIMATOR_TIME = 1000;
private MulShapeView mMulShapeView;//变形imageView
private ImageView mShadowView;// 阴影View
private int mShapeTransactionY;//MulShapeView的margin_bottom (底边距)
public ShapeLoadingView(Context context) {
this(context, null);
}
public ShapeLoadingView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ShapeLoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//inflate(context, R.layout.loading_layout, this);
//添加布局
LayoutInflater layoutInflater = LayoutInflater.from(context);
layoutInflater.inflate(R.layout.loading_layout, this);
//init
mMulShapeView = findViewById(R.id.shape);
mShadowView = findViewById(R.id.image_shadow);
mShapeTransactionY = ((MarginLayoutParams) mMulShapeView.getLayoutParams()).bottomMargin;
//post
post(new Runnable() {
@Override
public void run() {
// onResume 之后View绘制流程执行完毕之后
fullDownAnimate();
}
});
// onCreate() 方法中执行 ,布局文件解析 反射创建实例
}
private void jumpUPAnimate(){
//垂直上抛
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mMulShapeView, "translationY", mShapeTransactionY ,0);
//插值器 减速
DecelerateInterpolator interpolator = new DecelerateInterpolator();
//细节
float rotateAngle = -180;
switch (mMulShapeView.getCurrentShape()){
case MulShapeView.ShapeType.RECT:
rotateAngle = -180;
break;
case MulShapeView.ShapeType.TRIANGLE:
rotateAngle = -270;
break;
}
ObjectAnimator shapeRotateAnimate = ObjectAnimator.ofFloat(mMulShapeView, "rotation", rotateAngle);
//阴影 从原有宽度从0.3还原
ObjectAnimator shadowScaleAnimate = ObjectAnimator.ofFloat(mShadowView, "scaleX", 0.3f,1 );
// 将单个动画做成组合
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(ANIMATOR_TIME);
animatorSet.setInterpolator(interpolator);
animatorSet.playTogether(objectAnimator,shapeRotateAnimate,shadowScaleAnimate);//组合动画
animatorSet.start();
//监听器
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
//上抛到最顶点下落
fullDownAnimate();
}
});
}
private void fullDownAnimate(){
//下落
//mShapeTransactionY 原始位置
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mMulShapeView, "translationY", 0, mShapeTransactionY);
//插值器 加速下落
AccelerateInterpolator interpolator = new AccelerateInterpolator();
//细节
float rotateAngle = 0;
switch (mMulShapeView.getCurrentShape()){
case MulShapeView.ShapeType.RECT:
rotateAngle = 180;
break;
case MulShapeView.ShapeType.TRIANGLE:
rotateAngle = 270;
break;
}
ObjectAnimator shapeRotateAnimate = ObjectAnimator.ofFloat(mMulShapeView, "rotation", rotateAngle);
//阴影 从原有宽度缩短到0.3
ObjectAnimator shadowScaleAnimate = ObjectAnimator.ofFloat(mShadowView, "scaleX", 1, 0.3f);
// 将单个动画做成组合
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(ANIMATOR_TIME);
animatorSet.setInterpolator(interpolator);
animatorSet.playTogether(objectAnimator,shapeRotateAnimate,shadowScaleAnimate);//组合动画
animatorSet.start();
//监听器
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
//下落中改变图形
mMulShapeView.exchange();
//下落完成上抛
jumpUPAnimate();
}
});
}
//加载完毕调用 释放动画资源
public void loadComplete() {
mMulShapeView.clearAnimation();
mShadowView.clearAnimation();
// 把LoadingView从父布局移除
ViewGroup parent = (ViewGroup) getParent();
if (parent != null) {
parent.removeView(this);// 从父布局移除
removeAllViews();// 移除自己所有的View
}
}
/**
* 采用代码的方式添加
*
* @param parent
* @return
*/
public static ShapeLoadingView attach(ViewGroup parent) {
ShapeLoadingView loadingView = new ShapeLoadingView(parent.getContext());
loadingView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
parent.addView(loadingView);
return loadingView;
}
/**
* 优化性能
* @param visibility 隐藏
*/
@Override
public void setVisibility(int visibility) {
super.setVisibility(View.INVISIBLE);
ViewGroup parent = (ViewGroup) this.getParent();
if(parent != null){
parent.removeView(this);
mMulShapeView.clearAnimation();
mShadowView.clearAnimation();
this.removeAllViews();
}
}
//dip to px
private int dip2px(int dip) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics());
}
}
//使用
mShapeLoadingView = findViewById(R.id.loading);
mShapeLoadingView.postDelayed(new Runnable() {
@Override
public void run() {
mShapeLoadingView.loadComplete();
}
}, 3000);