我们先看一下效果
首先说明这里实现的是刚好一个周期的正弦函数,
我们来说明一下原理,我们是由左向右移动,由于刚好一个周期,所以我们将右边消失的y值正是左边新出现的y值。所以我们可以将函数的y值存储在一个数组里面,然后不断地在数组里面循环地改变不同位置的y值,那么就形成了水波纹效果。
- 首先我们要两组组数组来存储通过正弦函数的得到的y值,和用来交换时存储的函数。
private float[] mContentOneYs = null;//这两个代表两条线的y值得数据组
private float[] mContentTwoys = null;
private float[] mRestoreOnes = null;//这两个分笔试两个数组的备用数组在数组的交换的时候使用
private float[] mRestoreTwos = null;
然后我们通过x值求出Y的数组
for (int i = 0; i < mWidth; i++) {
mContentOneYs[i] = getYPosition(i, SWINGONE, OFFSETONE, INIT_BASE_HEIGHT1);
mContentTwoys[i] = getYPosition(i, SWINGTWO, OFFSETTWO, INIT_BASE_HEIGHT2);
}
private float getYPosition(int x, int swing, int offset, int baseHeight) {
float cycle = (float) (2 * Math.PI) / mWidth;
return (float) Math.sin(cycle * x + offset) * swing + baseHeight;
}
说明一下这里面的几个值得意思
x代表x值,swing代表这幅高度,offset代表一开始的sin'的角度,baseHeight代表基础高度大小,mWidth代表控件的宽度。这个在获取控件宽度的时候实现就是Onsizechang的时候。加下来就是我们移动y值了
private void changeRestorePosition() {
if (mWidth != 0) {
mPosition1 = (mPosition1 + STEP1) % mWidth;//循环显示
System.arraycopy(mContentOneYs, mPosition1, mRestoreOnes, 0, mWidth - mPosition1);
System.arraycopy(mContentOneYs, 0, mRestoreOnes, mWidth - mPosition1, mPosition1);
mPosition2 = (mPosition2 + STEP2) % mWidth;//循环显示
System.arraycopy(mContentTwoys, mPosition2, mRestoreTwos, 0, mWidth - mPosition2);
System.arraycopy(mContentTwoys, 0, mRestoreTwos, mWidth - mPosition2, mPosition2);
}
}
STEP1 和 STEP2 分别代表不同的移动速度
而要绘制出图形就必须把y值和底部连成线然后由mWidht多太哦直线形成一个函数
for (int i = 0; i < mWidth; i++) {
final int x = i;
final float y1 = mRestoreOnes[i];
final float y2 = mRestoreTwos[i];
canvas.drawLine(x, y2, x, mHeight, mPaint2);
canvas.drawLine(x, y1, x, mHeight, mPaint1);
}
- 最后是不断刷新 由于人眼一般能分辨的是30fps 所以 我这里设置是睡眠0.05秒当然你不怕性能影响的话就直接在OnDraw的时候invalidate();
先面试完整代码
public class WaveView extends View {
private volatile boolean RUNING=true;
private final int INIT_BASE_HEIGHT1 = 300;//基础高度
private final int INIT_BASE_HEIGHT2 = 300;
private int mHeight;
private int mWidth;
private float[] mContentOneYs = null;//这两个代表两条线的y值得数据组
private float[] mContentTwoys = null;
private float[] mRestoreOnes = null;//这两个分笔试两个数组的备用数组在数组的交换的时候使用
private float[] mRestoreTwos = null;
private static final int SWINGONE = 40;//分别代表两个的正弦函数的上下幅度
private static final int SWINGTWO = 80;
private static final int OFFSETONE = 0;//代表一开始的sin 的角度 从 0到360
private static final int OFFSETTWO = 40;
private int mPosition1 = 0;//代表起始高度
private int mPosition2 = 0;
private static final int STEP1 = 5;//这两个分别代表两条线的两个的速率
private static final int STEP2 = 8;
private Paint mPaint1;
private Paint mPaint2;
public WaveView(Context context) {
this(context, null);
}
public WaveView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WaveView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint1.setColor(Color.parseColor("#AB9DCF"));
mPaint1.setStrokeWidth(4);
mPaint1.setAlpha(125);
mPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint2.setColor(Color.parseColor("#A2D1F3"));
mPaint2.setStrokeWidth(4);
mPaint2.setAlpha(125);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (w != 0 || h != 0 || w != oldw || h != oldh) {
mWidth = w;
mHeight = h;
calculatePoints();
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
changeRestorePosition();
for (int i = 0; i < mWidth; i++) {
final int x = i;
final float y1 = mRestoreOnes[i];
final float y2 = mRestoreTwos[i];
canvas.drawLine(x, y2, x, mHeight, mPaint2);
canvas.drawLine(x, y1, x, mHeight, mPaint1);
}
// invalidate();//通过这个来形成循环
}
private void calculatePoints() {
mContentOneYs = new float[mWidth];
mContentTwoys = new float[mWidth];
mRestoreOnes = new float[mWidth];
mRestoreTwos = new float[mWidth];
for (int i = 0; i < mWidth; i++) {
mContentOneYs[i] = getYPosition(i, SWINGONE, OFFSETONE, INIT_BASE_HEIGHT1);
mContentTwoys[i] = getYPosition(i, SWINGTWO, OFFSETTWO, INIT_BASE_HEIGHT2);
}
}
private void changeRestorePosition() {
if (mWidth != 0) {
mPosition1 = (mPosition1 + STEP1) % mWidth;//循环显示
System.arraycopy(mContentOneYs, mPosition1, mRestoreOnes, 0, mWidth - mPosition1);
System.arraycopy(mContentOneYs, 0, mRestoreOnes, mWidth - mPosition1, mPosition1);
mPosition2 = (mPosition2 + STEP2) % mWidth;//循环显示
System.arraycopy(mContentTwoys, mPosition2, mRestoreTwos, 0, mWidth - mPosition2);
System.arraycopy(mContentTwoys, 0, mRestoreTwos, mWidth - mPosition2, mPosition2);
}
}
private float getYPosition(int x, int swing, int offset, int baseHeight) {
float cycle = (float) (2 * Math.PI) / mWidth;
return (float) Math.sin(cycle * x + offset) * swing + baseHeight;
}
public void startAnimotion()
{
RUNING=true;
final Handler handler=new Handler();
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
while (RUNING)
{
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.post(new Runnable() {
@Override
public void run() {
invalidate();
}
});
}
}
});
thread.start();
}
@Override
protected void onDetachedFromWindow() {
RUNING=false;
super.onDetachedFromWindow();
}
}
接下来我们要实现小球
我们只需要设置Paint的模式为DST_OUT,关于Paint的模式请参考
吴小龙
代码如下
mPaint2.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
mCanvas.drawCircle(mWidth/2,mHeight/2,Math.min(mWidth,mHeight)/2,mPaint2);
canvas.drawBitmap(bitmap,0,0,null);
然后是居中显示 文字
Paint textPaint = new Paint(); // 创建画笔
textPaint.setColor(0xffffffff); // 设置颜色
textPaint.setStyle(Paint.Style.FILL); // 设置样式
textPaint.setTextSize(120);
Rect targetRect = new Rect((mWidth-mHeight)/2, 0,mHeight+(mWidth-mHeight)/2, mHeight);
// textPaint.measureText("",0,1);
Paint.FontMetricsInt fontMetrics = mPaint2.getFontMetricsInt();
int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
textPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(value+"分", targetRect.centerX(), baseline+50, textPaint);
同时我们在xml里给他设置一个background
最后得到的效果如下
最后代码如下
public class WaveView extends View {
private int value=80;
private boolean drawCircle=true;
private volatile boolean RUNING=true;
private final int INIT_BASE_HEIGHT1 = 300;//基础高度
private final int INIT_BASE_HEIGHT2 = 300;
private int mHeight;
private int mWidth;
private float[] mContentOneYs = null;//这两个代表两条线的y值得数据组
private float[] mContentTwoys = null;
private float[] mRestoreOnes = null;//这两个分笔试两个数组的备用数组在数组的交换的时候使用
private float[] mRestoreTwos = null;
private static final int SWINGONE = 40;//分别代表两个的正弦函数的上下幅度
private static final int SWINGTWO = 80;
private static final int OFFSETONE = 0;//代表一开始的sin 的角度 从 0到360
private static final int OFFSETTWO = 40;
private int mPosition1 = 0;//代表起始高度
private int mPosition2 = 0;
private static final int STEP1 = 5;//这两个分别代表两条线的两个的速率
private static final int STEP2 = 8;
private Paint mPaint1;
private Paint mPaint2;
private Paint circlePaint;
Bitmap bitmap;
Canvas mCanvas;
public WaveView(Context context) {
this(context, null);
}
public WaveView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WaveView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint1.setColor(Color.parseColor("#AB9DCF"));
mPaint1.setStrokeWidth(4);
mPaint1.setAlpha(125);
mPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint2.setColor(Color.parseColor("#A2D1F3"));
mPaint2.setStrokeWidth(4);
mPaint2.setAlpha(125);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
bitmap = Bitmap.createBitmap(getMeasuredWidth(),getMeasuredHeight(), Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(bitmap);
mCanvas.drawColor(Color.WHITE);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (w != 0 || h != 0 || w != oldw || h != oldh) {
mWidth = w;
mHeight = h;
calculatePoints();
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
init();
changeRestorePosition();
for (int i = 0; i < mWidth; i++) {
final int x = i;
final float y1 = mRestoreOnes[i];
final float y2 = mRestoreTwos[i];
canvas.drawLine(x, y2, x, mHeight, mPaint2);
canvas.drawLine(x, y1, x, mHeight, mPaint1);
}
// invalidate();//通过这个来形成循环
if(drawCircle)
{
Paint textPaint = new Paint(); // 创建画笔
textPaint.setColor(0xffffffff); // 设置颜色
textPaint.setStyle(Paint.Style.FILL); // 设置样式
textPaint.setTextSize(120);
Rect targetRect = new Rect((mWidth-mHeight)/2, 0,mHeight+(mWidth-mHeight)/2, mHeight);
// textPaint.measureText("",0,1);
Paint.FontMetricsInt fontMetrics = mPaint2.getFontMetricsInt();
int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
textPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(value+"分", targetRect.centerX(), baseline+50, textPaint);
}
if (drawCircle ) {
mPaint2.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
mCanvas.drawCircle(mWidth/2,mHeight/2,Math.min(mWidth,mHeight)/2,mPaint2);
canvas.drawBitmap(bitmap,0,0,null);
}
}
private void calculatePoints() {
mContentOneYs = new float[mWidth];
mContentTwoys = new float[mWidth];
mRestoreOnes = new float[mWidth];
mRestoreTwos = new float[mWidth];
for (int i = 0; i < mWidth; i++) {
mContentOneYs[i] = getYPosition(i, SWINGONE, OFFSETONE, INIT_BASE_HEIGHT1);
mContentTwoys[i] = getYPosition(i, SWINGTWO, OFFSETTWO, INIT_BASE_HEIGHT2);
}
}
private void changeRestorePosition() {
if (mWidth != 0) {
mPosition1 = (mPosition1 + STEP1) % mWidth;//循环显示
System.arraycopy(mContentOneYs, mPosition1, mRestoreOnes, 0, mWidth - mPosition1);
System.arraycopy(mContentOneYs, 0, mRestoreOnes, mWidth - mPosition1, mPosition1);
mPosition2 = (mPosition2 + STEP2) % mWidth;//循环显示
System.arraycopy(mContentTwoys, mPosition2, mRestoreTwos, 0, mWidth - mPosition2);
System.arraycopy(mContentTwoys, 0, mRestoreTwos, mWidth - mPosition2, mPosition2);
}
}
private float getYPosition(int x, int swing, int offset, int baseHeight) {
float cycle = (float) (2 * Math.PI) / mWidth;
return (float) Math.sin(cycle * x + offset) * swing + baseHeight;
}
public void startAnimotion()
{
RUNING=true;
final Handler handler=new Handler();
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
while (RUNING)
{
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.post(new Runnable() {
@Override
public void run() {
invalidate();
}
});
}
}
});
thread.start();
}
@Override
protected void onDetachedFromWindow() {
RUNING=false;
super.onDetachedFromWindow();
}
}