一枝缔、前言
在學(xué)習(xí)自定義View中布疙,不可避免的遇到貝賽爾曲線,在一頓學(xué)習(xí)操作之后愿卸,成功的實(shí)現(xiàn)了一個(gè)水波加載拐辽,效果圖如下:(本文適用于有貝賽爾曲線基礎(chǔ)的人學(xué)習(xí))
二、正文
開始擼代碼
1)首先是實(shí)現(xiàn)思路
1擦酌、水波的實(shí)現(xiàn)俱诸,在接觸貝賽爾曲線之后,用了二階貝賽爾曲線實(shí)現(xiàn)水波效果
2赊舶、加載過程睁搭,通過屬性動(dòng)畫來實(shí)現(xiàn)這個(gè)過程
3赶诊、實(shí)現(xiàn)順序,先實(shí)現(xiàn)水波后實(shí)現(xiàn)加載過程
2)實(shí)現(xiàn)水波
int two = width / 2;
//繪制起點(diǎn)
path.moveTo(-(x - radius) + dx, (y + radius) - dy);
for (int i = width; i < getWidth() + width; i += width) {
//在上一個(gè)點(diǎn)的相對位置上繪制二階貝賽爾曲線园骆,參數(shù)分別為舔痪,控制點(diǎn)與終點(diǎn)
path.rQuadTo(two / 2, -height, two, 0);
//在上一個(gè)點(diǎn)的相對位置上繪制二階貝賽爾曲線,參數(shù)分別為锌唾,控制點(diǎn)與終點(diǎn)
path.rQuadTo(two / 2, height, two, 0);
}
path.lineTo(getWidth(), getHeight());
path.lineTo(0, getHeight());
path.close();
3)水波的動(dòng)畫
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, width);
valueAnimator.setDuration(2000);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
dx = (int) valueAnimator.getAnimatedValue();
refirsh();
}
});
valueAnimator.start();
3)在實(shí)現(xiàn)水波之后锄码,再實(shí)現(xiàn)加載過程的動(dòng)畫
final int totalCount = (int) ((rectF.bottom - rectF.top));
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 1000);
valueAnimator.setDuration(3000);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int progress = (int) valueAnimator.getAnimatedValue();
//計(jì)算每次的占比情況
dy = (float) (progress/maxNumber) * totalCount;
Log.e("TAG", String.valueOf(dy)+"\t"+maxNumber+"\t"+progress);
//完成之后調(diào)用接口
if(dy == totalCount){
loading.over();
}
refirsh();
}
});
valueAnimator.start();
完整代碼
package com.example.myapplication.Widget;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;
import androidx.annotation.Nullable;
public class BesselViewWidget extends View {
private Paint paint;
private Path path;
public BesselViewWidget(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
start();
}
private void init() {
paint = new Paint();
paint.setDither(true);
paint.setDither(true);
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setColor(Color.BLUE);
path = new Path();
paintRect = new Paint();
paintRect.setDither(true);
paintRect.setDither(true);
paintRect.setStrokeWidth(5);
paintRect.setStyle(Paint.Style.STROKE);
paintRect.setColor(Color.BLACK);
}
/**
* 水波的寬
*/
private int width = 700;
/**
* 水波的高
*/
private int height = 20;
/**
* 水波預(yù)留的寬
*/
private int dx = 0;
/**
* 水波預(yù)留的高
*/
private float dy = 0;
/**
* 中心點(diǎn)
*/
private int x;
private int y;
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
x = (right - left) / 2;
y = (bottom - top) / 2;
super.onLayout(changed, left, top, right, bottom);
}
private Paint paintRect;
private RectF rectF;
private int radius = 150;
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
rectF = new RectF(x - radius, y - radius, x + radius, y + radius);
//創(chuàng)建裁剪區(qū)域
canvas.clipRect(rectF);
path.reset();
int two = width / 2;
//繪制起點(diǎn)
path.moveTo(-(x - radius) + dx, (y + radius) - dy);
for (int i = width; i < getWidth() + width; i += width) {
//在上一個(gè)點(diǎn)的相對位置上繪制二階貝賽爾曲線,參數(shù)分別為晌涕,控制點(diǎn)與終點(diǎn)
path.rQuadTo(two / 2, -height, two, 0);
//在上一個(gè)點(diǎn)的相對位置上繪制二階貝賽爾曲線滋捶,參數(shù)分別為,控制點(diǎn)與終點(diǎn)
path.rQuadTo(two / 2, height, two, 0);
}
path.lineTo(getWidth(), getHeight());
path.lineTo(0, getHeight());
path.close();
//繪制線
canvas.drawPath(path, paint);
//繪制區(qū)域
canvas.drawRect(rectF, paintRect);
}
/**
* 最大的值
*/
private float maxNumber = 1000;
/**
* 設(shè)置進(jìn)度(加載的動(dòng)畫)
* @param loading
*/
public void setProgress(final Loading loading){
final int totalCount = (int) ((rectF.bottom - rectF.top));
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 1000);
valueAnimator.setDuration(3000);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
int progress = (int) valueAnimator.getAnimatedValue();
//計(jì)算每次的占比情況
dy = (float) (progress/maxNumber) * totalCount;
Log.e("TAG", String.valueOf(dy)+"\t"+maxNumber+"\t"+progress);
//完成之后調(diào)用接口
if(dy == totalCount){
loading.over();
}
refirsh();
}
});
valueAnimator.start();
}
private void refirsh() {
postInvalidate();
}
/**
* 水波的動(dòng)畫
*/
private void start() {
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, width);
valueAnimator.setDuration(2000);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
dx = (int) valueAnimator.getAnimatedValue();
refirsh();
}
});
valueAnimator.start();
}
public interface Loading{
void over();
}
}
CSDN地址:Android-自定義View-水波加載