一、波浪效果如下
貝塞爾曲線自定義波浪效果的案例很多,同樣方法也很簡單,大多數和本案例一樣使用二次貝塞爾曲線實作,同樣還有一種是PathMeasure的方式,這裡我們後續補充,先來看貝塞爾曲線的實作方式。
二、代碼實作
本案例難點:
①波形圖的運動,我們需要在 坐标點x負方向繪制一個完整的波形,當波形運動到0點之後自動從恢複原始位置。
②漸變實作,這裡使用LinearGradient,主要是漸變方向,當起始橫軸為0坐标,縱軸不為0,漸變方向才為縱向。可參考《Android中的LinearGradient》中的講解。
代碼如下:
public class WaveView extends View {
private TextPaint mPaint;
private float dx = 0;
private float mfactory = 5f/4; //波浪因數,用于決定波長
public WaveView(Context context) {
this(context,null);
}
public WaveView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public WaveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPaint();
}
private void initPaint() {
// 執行個體化畫筆并打開抗鋸齒
mPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG );
mPaint.setAntiAlias(true);
mPaint.setPathEffect(new CornerPathEffect(10)); //設定線條類型
mPaint.setStrokeWidth(dip2px(1));
mPaint.setTextSize(dip2px((12)));
mPaint.setStyle(Paint.Style.STROKE);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if(widthMode!=MeasureSpec.EXACTLY){
width = (int) dip2px(300);
}
if(heightMode!=MeasureSpec.EXACTLY){
height = (int) dip2px(100);
}
setMeasuredDimension(width,height);
}
public float dip2px(int dp){
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dp,getResources().getDisplayMetrics());
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(getWidth()<1 || getHeight()<1) return;
int saveCount = canvas.save();
canvas.translate(0,getHeight()/2);
drawWave(canvas);
canvas.restoreToCount(saveCount);
}
private void drawWave(Canvas canvas){
float waveLength = getWidth()*mfactory; //波長
float top = -getHeight()/2f;
float bottom = getHeight()/2f;
int N = (int) Math.ceil(getWidth()/waveLength);
//最大整數,标示view寬度中能容納的波的數量
if(N==0) return;
Path path = new Path();
for (int i=-1;i<N;i++) {
buildWavePath(path,i, waveLength, top, bottom);
}
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.CYAN);
canvas.drawPath(path,mPaint);
float startX = waveLength * (-1) + dx;
float endX = waveLength * N + dx;
path.lineTo(endX,bottom);
path.lineTo(startX,bottom);
path.close();
mPaint.setStyle(Paint.Style.FILL);
LinearGradient linearGradient = new LinearGradient(0,top,0,bottom,new int[]{Color.BLUE,Color.CYAN},new float[]{0,0.9f}, Shader.TileMode.CLAMP);
Shader shader = mPaint.getShader();
mPaint.setShader(linearGradient);
canvas.drawPath(path,mPaint);
mPaint.setShader(shader);
}
private void buildWavePath(Path path,int ith,float waveLength,float top,float bottom) {
float offsetLeft = waveLength * ith + dx;
if(ith==-1) {
path.moveTo(offsetLeft, 0);
}
path.quadTo(offsetLeft+waveLength/4f,top,offsetLeft+waveLength/2f,0);
path.quadTo(offsetLeft+waveLength*3f/4f,bottom,offsetLeft+waveLength,0);
}
public void startAnim(){
ValueAnimator animator = ValueAnimator.ofFloat(0,getWidth()*mfactory);
animator.setDuration(2000);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
dx = (float) animation.getAnimatedValue();
postInvalidate();
}
});
animator.start();
}
}
版權聲明:本文為CSDN部落客「weixin_34392843」的原創文章,遵循CC 4.0 BY-SA版權協定,轉載請附上原文出處連結及本聲明。
原文連結:https://blog.csdn.net/weixin_34392843/article/details/92066197