效果图:
这里的水波纹效果实际上是用得到了数学里的正弦函数,关于正弦函数:
正弦型函数解析式:y=Asin(ωx+φ)+h
各常数值对函数图像的影响:
φ(初相位):决定波形与X轴位置关系或横向移动距离(左加右减)
ω:决定周期(最小正周期T=2π/|ω|)
A:决定峰值(即纵向拉伸压缩的倍数)
h:表示波形在Y轴的位置关系或纵向移动距离(上加下减)
整个view的绘制,可以分解成3步:
- 绘制出一个圆形的路径,并使用clipPath方法裁剪出来,并设置接下来绘制的两部分的显示方式;
- 在裁剪下来的canvas上在绘制一个背景圆;
- 在背景圆上绘制水波纹。
clipPath:
public boolean clipPath(Path path, Region.Op op) {
}
关于Region.Op参数:把第一次clipRect的绘制范围设为A,第二次clipRect设定的范围设为B
Op.DIFFERENCE,实际上就是求得的A和B的差集范围,即A-B,只有在此范围内的绘制内容才会被显示;
Op.REVERSE_DIFFERENCE,实际上就是求得的B和A的差集范围,即B-A,只有在此范围内的绘制内容才会被显示;;
Op.INTERSECT,即A和B的交集范围,只有在此范围内的绘制内容才会被显示;
Op.REPLACE,不论A和B的集合状况,B的范围将全部进行显示,如果和A有交集,则将覆盖A的交集范围;
Op.UNION,即A和B的并集范围,即两者所包括的范围的绘制内容都会被显示;
Op.XOR,A和B的补集范围,此例中即A除去B以外的范围,只有在此范围内的绘制内容才会被显示;
关于clipPath更多
下面是自定义水球的代码:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.graphics.Rect;
import android.graphics.Region;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;/*** 水球*/
public class WaveBallView extends SurfaceView implements SurfaceHolder.Callback{private SurfaceHolder surfaceHolder;private boolean isDraw;private Paint wavepaint,circlepaint;private Path path;/** 水波纹颜色*/private int wavecolor=Color.GREEN;/** 圆球颜色*/private int circlecolor=Color.RED;private Canvas canvas;private MyThread myThread;private MyThread2 myThread2;private int centerX,centerY,radius;/** 水面目标高度*/private float targetValue=0.8f;/** 实际进度*/private float target;private Rect rect;private String text;private int textheight;private int x = 0,y = 0;/** 初相位(横向移动距离)*/private int φ;/** 周期*/private float w=0.5f;/** 峰值*/private int A=20;/** 纵向移动距离*/private int h;/*** 设置背景圆颜色* @param circlecolor*/public void SetCircleBackgroundColor(int circlecolor){this.circlecolor=circlecolor;}/*** 设置水球颜色* @param wavecolor*/public void SetWaveColor(int wavecolor){this.wavecolor=wavecolor;}/*** 设置目标值* @param targetValue*/public void SetTarget(float targetValue){if(targetValue!=0)this.targetValue=targetValue;}public WaveBallView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init();}public WaveBallView(Context context, AttributeSet attrs) {super(context, attrs);init();}public WaveBallView(Context context) {super(context);init();}private void init() {wavepaint=new Paint();circlepaint=new Paint();wavepaint.setStyle(Style.FILL);circlepaint.setStyle(Style.FILL);circlepaint.setTextAlign(Align.CENTER);circlepaint.setTextSize(80);path=new Path();myThread=new MyThread();myThread2=new MyThread2();surfaceHolder=getHolder();surfaceHolder.addCallback(this);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);centerX=w/2;centerY=h/2;radius=Math.min(centerX,centerY);}@Overridepublic void surfaceCreated(SurfaceHolder holder) {isDraw=true;wavepaint.setColor(wavecolor);circlepaint.setColor(circlecolor);myThread.start();myThread2.start();}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {isDraw=false;myThread=null;myThread2=null;}/* 正弦型函数解析式:y=Asin(ωx+φ)+h各常数值对函数图像的影响:φ(初相位):决定波形与X轴位置关系或横向移动距离(左加右减)ω:决定周期(最小正周期T=2π/|ω|)A:决定峰值(即纵向拉伸压缩的倍数)h:表示波形在Y轴的位置关系或纵向移动距离(上加下减)*/class MyThread extends Thread{@Overridepublic void run() {if(isDraw){draw();}}private void draw() {while(true){if(target<targetValue){target+=0.1f;h=(int) (centerY+radius-target*radius*2);}canvas=surfaceHolder.lockCanvas();if(canvas==null || !isDraw)break;canvas.drawColor(Color.WHITE);path.reset();//重置之前所有设置path.addCircle(centerX,centerY, radius,Direction.CCW);canvas.drawCircle(centerX,centerY, radius, circlepaint);canvas.clipPath(path,Region.Op.INTERSECT);//裁剪//最后一个参数有多个选择分别是://DIFFERENCE是第一次不同于第二次的部分显示出来//REPLACE是显示第二次的//REVERSE_DIFFERENCE 是第二次不同于第一次的部分显示//INTERSECT交集显示//UNION全部显示//XOR补集 就是全集的减去交集剩余部分显示path.reset();//重置之前所有设置for (int i = 0; i < centerX*2; i++) {x=i;y=(int) (A*Math.sin((w*i+φ)*Math.PI/180)+h);if(x==0)path.moveTo(x,y);path.quadTo(x, y, x + 1, y);}path.lineTo(centerX*2,centerY+radius);path.lineTo(0,centerY+radius);path.close();//回到初始点 形成封闭路径canvas.drawPath(path,wavepaint);drawText(canvas);surfaceHolder.unlockCanvasAndPost(canvas);try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}}}}/*** 绘制文字* @param canvas*/public void drawText(Canvas canvas) {rect=new Rect();text=String.format("%.1f",target*100)+"%";circlepaint.getTextBounds(text, 0, text.length()-1, rect);textheight=rect.bottom-rect.top;canvas.drawText(text, centerX, centerY+textheight/2, circlepaint);}class MyThread2 extends Thread{@Overridepublic void run() {if(isDraw){addφ();}}}public void addφ() {while(true){φ+=1;if(φ==360)φ=0;if(!isDraw)break;try {Thread.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}}}}