版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xingxtao/article/details/80616238
/**
* Created by Administrator on 2018/6/3.
*/
public class BoundLoadingView extends View {
private static final int DEFAULT_CIRCLE_COLOR = 0xffff0000;
private static final int DEFAULT_RECT_COLOR = 0xff00ff00;
private static final int DEFAULT_TRIANGLE_COLOR = 0xff0000ff;
private static final int DEFAULT_TEXT_COLOR = Color.BLACK;
private Paint paint; // shape paint
private Paint textPaint; // loading text paint
private int circleColor;
private int rectColor;
private int triangleColor;
private float centerX;
private float radius;
private int mHeight;
private float fraction;
private float degree;
private Path path;
private RectF shadowRect;
private int textGap = dp2Px(30);
private float textSize; // loading text size
private int textColor; // loading text color
private String text; // loading text
private Paint.FontMetrics fontMetrics;
private float textHeight; // loading text height
private float textWidth; // text width
private float startY; // rectangle,circle,triangle 上升起点
private float endY; // rectangle,circle,triangle 上升终点
private float currentY; // rectangle,circle,triangle 动画过程中当前处于的坐标点
public BoundLoadingView(Context context) {
this(context, null);
}
public BoundLoadingView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
readAttrs(context, attrs);
init();
}
private void readAttrs(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.BoundLoadingView);
circleColor = typedArray.getColor(R.styleable.BoundLoadingView_circleColor, DEFAULT_CIRCLE_COLOR);
rectColor = typedArray.getColor(R.styleable.BoundLoadingView_rectColor, DEFAULT_RECT_COLOR);
triangleColor = typedArray.getColor(R.styleable.BoundLoadingView_triangleColor, DEFAULT_TRIANGLE_COLOR);
textColor = typedArray.getColor(R.styleable.BoundLoadingView_textColor, DEFAULT_TEXT_COLOR);
textSize = typedArray.getDimension(R.styleable.BoundLoadingView_textSize, dp2Px(14));
text = typedArray.getString(R.styleable.BoundLoadingView_loadingText);
if (TextUtils.isEmpty(text)) {
text = "loading...";
}
typedArray.recycle();
}
private void init() {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL);
textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setTextSize(textSize);
textPaint.setColor(textColor);
textPaint.setTextAlign(Paint.Align.CENTER);
textWidth = textPaint.measureText(text);
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
textHeight = fontMetrics.bottom - fontMetrics.top + fontMetrics.leading;
Log.d("debug", "init: height ====" + textHeight);
path = new Path();
shadowRect = new RectF();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredSize(widthMeasureSpec, (int) textWidth);
int height = getMeasuredSize(heightMeasureSpec, dp2Px(80));
setMeasuredDimension(width, height);
}
private int getMeasuredSize(int measureSpec, int defaultSize) {
int mode = MeasureSpec.getMode(measureSpec);
int size = MeasureSpec.getSize(measureSpec);
if (mode == MeasureSpec.EXACTLY) {
return size;
} else if (mode == MeasureSpec.AT_MOST) {
return Math.min(size, defaultSize);
} else {
return defaultSize;
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mHeight = h;
centerX = w / 2f;
radius = w > dp2Px(20) ? dp2Px(10) : w / 3f;
// 上升,下降动画起点和终点
startY = mHeight - textHeight - textGap - dp2Px(20);
endY = (float) Math.sqrt(2) * radius;
startAnimator();
}
@Override
protected void onDraw(Canvas canvas) {
// draw loading text
canvas.drawText(text, centerX, mHeight - textHeight, textPaint);
// draw shadow
shadowRect.set(centerX - radius * fraction, mHeight - textGap - 10 - textHeight, centerX + radius * fraction, mHeight - textHeight - textGap);
paint.setColor(Color.BLACK);
// 设置画笔阴影效果,需要关闭硬件加速
paint.setMaskFilter(new BlurMaskFilter(4, BlurMaskFilter.Blur.NORMAL));
canvas.drawOval(shadowRect, paint);
canvas.save();
// 将画布原点移动到当前小球,方块所处的坐标点,所以画布旋转中心为(0,0)
canvas.translate(centerX, currentY);
canvas.rotate(degree, 0, 0);
paint.setMaskFilter(null);
// draw rectangle,triangle,circle
if (number % 3 == 0) {
paint.setColor(rectColor);
// 坐标值相对于中心点(0,0)
canvas.drawRect(-radius, -radius, radius, radius, paint);
} else if (number % 3 == 1) {
paint.setColor(circleColor);
canvas.drawCircle(0, 0, radius, paint);
} else if (number % 3 == 2) {
path.reset();
path.moveTo((float) (-Math.sqrt(3) / 2f * radius), radius / 2f);
path.lineTo(0, -radius);
path.lineTo((float) (Math.sqrt(3) / 2f * radius), radius / 2f);
path.close();
paint.setColor(triangleColor);
canvas.drawPath(path, paint);
}
canvas.restore();
}
private boolean isUp = true;
private int number = 0;
private ValueAnimator jumpAnimator;
private ValueAnimator shadowAnimator;
private ValueAnimator rotateAnimator;
private AnimatorSet animatorSet;
private AnimatorSet initAnimator() {
jumpAnimator = ValueAnimator.ofFloat(isUp ? startY : endY, isUp ? endY : startY);
jumpAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
jumpAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentY = (float) animation.getAnimatedValue();
invalidate();
}
});
shadowAnimator = ValueAnimator.ofFloat(isUp ? 0 : 1.0f, isUp ? 1.0f : 0);
shadowAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
shadowAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
fraction = (float) animation.getAnimatedValue();
}
});
rotateAnimator = ValueAnimator.ofFloat(isUp ? 0 : 210, isUp ? 210 : 270);
rotateAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
rotateAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
degree = (float) animation.getAnimatedValue();
}
});
animatorSet = new AnimatorSet();
animatorSet.setDuration(600);
animatorSet.playTogether(jumpAnimator, rotateAnimator, shadowAnimator);
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
isUp = !isUp;
startAnimator();
if (isUp) {
number++;
}
}
});
return animatorSet;
}
public void startAnimator() {
AnimatorSet animatorSet = initAnimator();
if (animatorSet != null) {
animatorSet.cancel();
}
animatorSet.start();
}
public void stopAnimator() {
if (shadowAnimator != null) {
shadowAnimator.cancel();
}
if (rotateAnimator != null) {
rotateAnimator.cancel();
}
if (jumpAnimator != null) {
jumpAnimator.cancel();
}
if (animatorSet != null) {
animatorSet.cancel();
}
}
private int dp2Px(int dpValue) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, getResources().getDisplayMetrics());
}
}