http://www.jianshu.com/p/a4dabb3554c1 不过由于该文章中对于动画的具体实现没有提及，所以才有这篇文章的存在了=.=

https://github.com/danielzeller/Depth-LIB-Android-

## 二.分析

1.水的背景波浪是浪起来的？ 2.水面上的波纹是如何看起来随机产生并且粗细不同的？

Android 吸入动画效果实现分解

## 三.实现

3.1先来介绍Renderable

3.2 Water类

3.2.1 实现背景大波浪的效果

[code lang=”java”]
public class Water extends Renderable {

private float mWidth;
private float mHeight;
private PathBitmapMesh mWaterMesh;
private float mWaveHeight;
private Path mWaterPath = new Path();
private int mNumWaves;
/*private Foam[] foams = new Foam[1];
long lastEmit;
private int emitInterWall = 1000;*/

/**
*
* @param water water图像
* @param foam 海浪图像
* @param y 海浪起始左上角坐标的y值
* @param width 海浪显示的宽度
* @param height 海浪显示的高度
* @param numWaves 海浪整个宽度被分成多少份
*/
public Water(Bitmap water, Bitmap foam, float y, float width, float height, int numWaves) {
super(water, 0, y);
mWidth = width;
mHeight = height;
mWaterMesh = new PathBitmapMesh(water, 1500);
mWaveHeight = height / 20;
mNumWaves = numWaves;
/*foams[0] = new Foam(PathBitmapMesh.HORIZONTAL_COUNT, foam, 0, height / 12, 1500);
foams[1] = new Foam(PathBitmapMesh.HORIZONTAL_COUNT, foam, -height / 5, height / 5, 1500);
foams[1].setAlpha(100);
foams[2] = new Foam(PathBitmapMesh.HORIZONTAL_COUNT, foam, -height / 12, height / 12, 1450);
foams[2].setVerticalOffset(height / 7);
foams[3] = new Foam(PathBitmapMesh.HORIZONTAL_COUNT, foam, -height / 12, height / 12, 1400);
foams[3].setVerticalOffset(height / 4);
lastEmit = System.currentTimeMillis();*/
createPath();
}

private void createPath() {
mWaterPath.reset();
mWaterPath.moveTo(0, y);
int step = (int) (mWidth / mNumWaves);
boolean changeDirection = true;
for (int i = 0; i < mNumWaves; i++) {
if (changeDirection) {
mWaterPath.cubicTo(x + step * i, y, x + step * i + step / 2f, y + mWaveHeight, x + step * i + step, y);
} else {
mWaterPath.cubicTo(x + step * i, y, x + step * i + step / 2f, y – mWaveHeight, x + step * i + step, y);
}
changeDirection = !changeDirection;
}
}

@Override
public void draw(Canvas canvas) {
mWaterMesh.draw(canvas);
/*for (Foam foam : foams) {
foam.draw(canvas);
}*/
}

@Override
public void update(float deltaTime) {
mWaterMesh.matchVertsToPath(mWaterPath, mHeight, ((bitmap.getWidth() / mNumWaves) * 4f));
/*for (Foam foam : foams) {
foam.update(deltaTime);
}
for (Foam foam : foams) {
foam.matchVertsToPath(mWaterPath, ((foam.getBitmap().getWidth() / mNumWaves) * 4f));
}
if (lastEmit + emitInterWall < System.currentTimeMillis()) {
for (Foam foam : foams) {
foam.calcWave();
}
lastEmit = System.currentTimeMillis();
}*/
}
}
[/code]

createPath这个方法中有个有趣的参数mNumWaves，这个用来表示海浪整个宽度被分成多少份（这个例子中我们把海浪分成了6份）。

[code lang=”java”]
public class PathBitmapMesh {

protected static int HORIZONTAL_COUNT = 6;//水平方向分片数
protected static int VERTICAL_COUNT = 1;//垂直方向分片
private int mTotalCount;//总共需要计算的网格顶点个数
protected Bitmap bitmap;
protected float[] drawingVerts;//需要绘制的Verts网格坐标
protected float[] staticVerts;//最初始的Verts网格坐标
private Paint mPaint = new Paint();
//private ValueAnimator mValueAnimator;
//protected float pathOffsetPercent;
protected float[] coordsX = new float[2];
protected float[] coordsY = new float[2];

public PathBitmapMesh(Bitmap bitmap, long duration) {
mTotalCount = (HORIZONTAL_COUNT + 1) * (VERTICAL_COUNT + 1);
drawingVerts = new float[mTotalCount * 2];
staticVerts = new float[mTotalCount * 2];
this.bitmap = bitmap;
initVert();
//startValuesAnim(duration);
}

/*private void startValuesAnim(long duration) {
mValueAnimator = ValueAnimator.ofFloat(0, 0.3334f);
mValueAnimator.setDuration(duration);
mValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
mValueAnimator.setRepeatMode(ValueAnimator.RESTART);
mValueAnimator.setInterpolator(new LinearInterpolator());
@Override
public void onAnimationUpdate(ValueAnimator animation) {
pathOffsetPercent = (float) animation.getAnimatedValue();
}
});
mValueAnimator.start();
}*/

private void initVert() {
float bitmapWidth = (float) bitmap.getWidth();
float bitmapHeight = (float) bitmap.getHeight();
int index = 0;
for (int y = 0; y <= VERTICAL_COUNT; y++) {
float fy = bitmapHeight / VERTICAL_COUNT * y;
for (int x = 0; x <= HORIZONTAL_COUNT; x++) {
float fx = bitmapWidth / HORIZONTAL_COUNT * x;
setXY(drawingVerts, index, fx, fy);
setXY(staticVerts, index, fx, fy);
index++;
}
}
}

protected void setXY(float[] arrys, int index, float x, float y) {
arrys[2 * index] = x;
arrys[2 * index + 1] = y;
}

public void matchVertsToPath(Path path, float bottomY, float extraOffset) {
PathMeasure pm = new PathMeasure(path, false);
for (int i = 0; i < staticVerts.length / 2; i++) {
float orignX = staticVerts[2 * i];
float orignY = staticVerts[2 * i + 1];
float percentOffsetX = orignX / bitmap.getWidth();
float percentOffsetY = orignX / (bitmap.getWidth() + extraOffset);
//percentOffsetY += pathOffsetPercent;
pm.getPosTan(pm.getLength() * (percentOffsetX), coordsX, null);
pm.getPosTan(pm.getLength() * (percentOffsetY), coordsY, null);
if (orignY == 0) {
setXY(drawingVerts, i, coordsX[0], coordsY[1]);
} else {
setXY(drawingVerts, i, coordsX[0], bottomY);
}
}
}

public void draw(Canvas canvas) {
canvas.drawBitmapMesh(bitmap, HORIZONTAL_COUNT, VERTICAL_COUNT, drawingVerts, 0, null, 0, mPaint);
}

public Bitmap getBitmap() {
return bitmap;
}

public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
}
[/code]

pm.getLength()得到整条曲线长度，percentOffsetX得到曲线上比例值，最后把得到的坐标返回给coordsX数组。

3.2.1 实现水纹效果

[code lang=”java”]
public class Foam extends PathBitmapMesh {

private float[] foamCoords;
private float[] easedFoamCoords;
private int mHorizontalSlices;//水纹水平方向分片
private float minHeight;//水纹最小高度
private float maxHeight;//水纹最大高度
private float verticalOffset;

public Foam(int horizontalSlices, Bitmap bitmap, float minHeight, float maxHeight, long duration) {
super(bitmap, duration);
mHorizontalSlices = horizontalSlices;
this.minHeight = minHeight;
this.maxHeight = maxHeight;
init();
}

private void init() {
foamCoords = new float[mHorizontalSlices];
easedFoamCoords = new float[mHorizontalSlices];
for (int i = 0; i < mHorizontalSlices; i++) {
foamCoords[i] = 0;
easedFoamCoords[i] = 0;
}
}

/**
* 随着时间的流逝不断更改
* @param deltaTime
*/
public void update(float deltaTime) {
for (int i = 0; i < foamCoords.length; i++) {
easedFoamCoords[i] += ((foamCoords[i] – easedFoamCoords[i])) * deltaTime;
}
}

/**
* 根据传入的最低，最高高度得到一个适合的高度
*/
public void calcWave() {
for (int i = 0; i < foamCoords.length; i++) {
foamCoords[i] = MathHelper.randomRange(minHeight, maxHeight);
}
}

/**
* 计算水纹的各个顶点坐标
* @param path
* @param extraOffset
*/
public void matchVertsToPath(Path path, float extraOffset) {
PathMeasure pm = new PathMeasure(path, false);
int index = 0;
for (int i = 0; i < staticVerts.length / 2; i++) {
float orignX = staticVerts[2 * i];
float orignY = staticVerts[2 * i + 1];
float percentOffsetX = orignX / bitmap.getWidth();
float percentOffsetY = orignX / (bitmap.getWidth() + extraOffset);
percentOffsetY += pathOffsetPercent;
pm.getPosTan(pm.getLength() * percentOffsetX, coordsX, null);
pm.getPosTan(pm.getLength() * percentOffsetY, coordsY, null);
if (orignY == 0) {
setXY(drawingVerts, i, coordsX[0], coordsY[1]+verticalOffset);
} else {
float desiredYCoord = Math.max(coordsY[1], coordsY[1] + easedFoamCoords[Math.min(easedFoamCoords.length – 1, index)]);
setXY(drawingVerts, i, coordsX[0], desiredYCoord+verticalOffset);
index += 1;
}
}
}

public void setVerticalOffset(float verticalOffset) {
this.verticalOffset = verticalOffset;
}
}
[/code]

## 四.合成

[code lang=”java”]
public class WaterScreenView extends View {

private Water mWater;
private Renderable[] mRenderables;

public WaterScreenView(Context context) {
super(context);
}

public WaterScreenView(Context context, AttributeSet attrs) {
super(context, attrs);
}

public WaterScreenView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (mRenderables == null && getWidth() != 0) {
init();
}
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (mRenderables == null) {
init();
}
}

private void init() {
mRenderables = new Renderable[1];
Bitmap waterBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.water);
Bitmap foam = BitmapFactory.decodeResource(getResources(), R.drawable.foam);
setLayerType(View.LAYER_TYPE_HARDWARE, null);
mWater = new Water(waterBitmap, foam, getHeight() * 0.65f, getWidth(), getHeight(), 6);
mRenderables[0] = mWater;
Bitmap aura = BitmapFactory.decodeResource(getResources(), R.drawable.sun_aura);
mRenderables[1] = new Renderable(aura, getWidth() * 0.5f, getHeight() * 0.35f);
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float deltaTime = FrameRateCounter.timeStep();

for (Renderable renderable : mRenderables) {
renderable.draw(canvas);
renderable.update(deltaTime);
}
if (!isPause) {
invalidate();
}
}

private boolean isPause = false;
}
[/code]

## 六.下载

Share:

This site uses Akismet to reduce spam. Learn how your comment data is processed.