1、波浪效果以下canvas
貝塞爾曲線自定義波浪效果的案例不少,一樣方法也很簡單,大多數和本案例同樣使用二次貝塞爾曲線實現,一樣還有一種是PathMeasure的方式,這裏咱們後續補充,先來看貝塞爾曲線的實現方式。app
2、代碼實現ide
本案例難點:post
①波形圖的運動,咱們須要在 座標點x負方向繪製一個完整的波形,當波形運動到0點以後自動從恢復原始位置。ui
②漸變實現,這裏使用LinearGradient,主要是漸變方向,當起始橫軸爲0座標,縱軸不爲0,漸變方向才爲縱向。可參考《Android中的LinearGradient》中的講解。this
代碼以下:rest
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(); } }