手机
当前位置:查字典教程网 >编程开发 >安卓软件开发 >Android 吸入动画效果实现分解
Android 吸入动画效果实现分解
摘要:Android吸入动画效果详解.这里,我要介绍的是如何在Android上面实现一个类似的效果。先看看我实现的效果图。上图演示了动画的某几帧,...

Android 吸入动画效果详解 .

1

这里,我要介绍的是如何在Android上面实现一个类似的效果。先看看我实现的效果图。

2

上图演示了动画的某几帧,其中从1 - 4,演示了图片从原始图形吸入到一个点(红色标识)。

实现这样的效果,我们利用了Canvas.drawBitmapMesh()方法,这里涉及到了一个Mesh的概念。

2,Mesh的概念

Mesh表示网格,说得通俗一点,可以将画板想像成一张格子布,在这个张布上绘制图片。对于一个网格端点均匀分布的网格来说,横向有meshWidth + 1个顶点,纵向有meshHeight + 1个端点。顶点数据verts是以行优先的数组(二维数组以一维数组表示,先行后列)。网格可以不均匀分布。请看下图所示:

3

上图中显示了把图片分成很多格子,上图中的每个格子是均匀的,它的顶点数是:(meshWidth + 1) * (meshHeight + 1)个,那么放这些顶点的一维数据的大小应该是:(meshWidth + 1) * (meshHeight + 1) * 2 (一个点包含x, y坐标)

复制代码 代码如下:

float[] vertices = new float[:(meshWidth + 1) * (meshHeight + 1) * 2];

试想,我们让这个格子(mesh)不均匀分布,那么绘制出来的图片就会变形,请看下图所示:

4

3,如何构建Mesh

吸入动画的核心是吸入到一个点,那么我们就是要在不同的时刻构造出不同的mesh的顶点坐标,我们是怎么做的呢?

3.1,创建两条路径(Path)

假如我们的吸入效果是从上到下吸入,我们构造的Path是如下图所示:

5

上图中蓝色的线表示我们构造的Path,其实只要我们沿着这两条Path来构造mesh顶点就可以了。

构建两条Path的代码如下:

复制代码 代码如下:

mFirstPathMeasure.setPath(mFirstPath, false);

mSecondPathMeasure.setPath(mSecondPath, false);

float w = mBmpWidth;

float h = mBmpHeight;

mFirstPath.reset();

mSecondPath.reset();

mFirstPath.moveTo(0, 0);

mSecondPath.moveTo(w, 0);

mFirstPath.lineTo(0, h);

mSecondPath.lineTo(w, h);

mFirstPath.quadTo(0, (endY + h) / 2, endX, endY);

mSecondPath.quadTo(w, (endY + h) / 2, endX, endY);

3.2,根据Path来计算顶点坐标

算法:

1,假如我们把格子分为WIDTH, HEIGHT份,把Path的长度分的20份,[0, length],表示20个时刻。

2,第0时间,我们要的形状是一个矩形,第1时刻可能是梯形,第n时间可能是一个三角形。下图说明了动画过程中图片的变化。

6

3,第一条(左)Path的长度为len1,第二条(右)Path的长度为len2,对于任意时刻 t [0 - 20],我们可以知道梯形的四个顶点距Path最顶端的length。

左上角:t * (len1 / 20)

左下角:t * (len1 / 20) + bitmapHeight

右上角:t * (len2 / 20)

右下角:t * (len2 / 20) + bitmapHeight

7

我们可以通过PathMeasure类根据length算出在Path上面点的坐标,也就是说,根据两条Path,我们可以分别算了四个顶点的坐标,我这里分别叫做A, B, C, D(顺时针方向),有了点的坐标,我们可以算出AD,BC的长度,并且将基进行HEIGHT等分(因为我们把mesh分成宽WIDTH,高HEIGHT等分),将AD,BC上面每等分的点连接起来形成一条直接,将再这条直接水平WIDTH等分,根据直线方程,依据x算出y,从而算出每一个顶点的坐标。(请参考上图)

下面是计算顶点坐标的详细代码:

复制代码 代码如下:

private void buildMeshByPathOnVertical(int timeIndex)

{

mFirstPathMeasure.setPath(mFirstPath, false);

mSecondPathMeasure.setPath(mSecondPath, false);

int index = 0;

float[] pos1 = {0.0f, 0.0f};

float[] pos2 = {0.0f, 0.0f};

float firstLen = mFirstPathMeasure.getLength();

float secondLen = mSecondPathMeasure.getLength();

float len1 = firstLen / HEIGHT;

float len2 = secondLen / HEIGHT;

float firstPointDist = timeIndex * len1;

float secondPointDist = timeIndex * len2;

float height = mBmpHeight;

mFirstPathMeasure.getPosTan(firstPointDist, pos1, null);

mFirstPathMeasure.getPosTan(firstPointDist + height, pos2, null);

float x1 = pos1[0];

float x2 = pos2[0];

float y1 = pos1[1];

float y2 = pos2[1];

float FIRST_DIST = (float)Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );

float FIRST_H = FIRST_DIST / HEIGHT;

mSecondPathMeasure.getPosTan(secondPointDist, pos1, null);

mSecondPathMeasure.getPosTan(secondPointDist + height, pos2, null);

x1 = pos1[0];

x2 = pos2[0];

y1 = pos1[1];

y2 = pos2[1];

float SECOND_DIST = (float)Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );

float SECOND_H = SECOND_DIST / HEIGHT;

for (int y = 0; y <= HEIGHT; ++y)

{

mFirstPathMeasure.getPosTan(y * FIRST_H + firstPointDist, pos1, null);

mSecondPathMeasure.getPosTan(y * SECOND_H + secondPointDist, pos2, null);

float w = pos2[0] - pos1[0];

float fx1 = pos1[0];

float fx2 = pos2[0];

float fy1 = pos1[1];

float fy2 = pos2[1];

float dy = fy2 - fy1;

float dx = fx2 - fx1;

for (int x = 0; x <= WIDTH; ++x)

{

// y = x * dy / dx

float fx = x * w / WIDTH;

float fy = fx * dy / dx;

mVerts[index * 2 + 0] = fx + fx1;

mVerts[index * 2 + 1] = fy + fy1;

index += 1;

}

}

}

4,如何绘制

绘制代码很简单,调用Canvas.drawBitmapMesh方法。最本质是要计算出一个顶点数组。

复制代码 代码如下:

canvas.drawBitmapMesh(mBitmap,

mInhaleMesh.getWidth(),

mInhaleMesh.getHeight(),

mInhaleMesh.getVertices(),

0, null, 0, mPaint);

5,如何实现动画

复制代码 代码如下:

protected void applyTransformation(float interpolatedTime, Transformation t)

{

int curIndex = 0;

Interpolator interpolator = this.getInterpolator();

if (null != interpolator)

{

float value = interpolator.getInterpolation(interpolatedTime);

interpolatedTime = value;

}

if (mReverse)

{

interpolatedTime = 1.0f - interpolatedTime;

}

curIndex = (int)(mFromIndex + (mEndIndex - mFromIndex) * interpolatedTime);

if (null != mListener)

{

mListener.onAnimUpdate(curIndex);

}

}

在动画里面,我们计算出要做动画的帧的index,假设我们把吸入动画分为20帧,在动画里面,计算出每一帧,最后通过onAnimUpdate(int index)方法回调,在这个方法实现里面,我们根据帧的index来重新计算一个新的mesh顶点数组,再用这个数组来绘制bitmap。这样,我们就可以看来一组连续变化的mesh,也就能看到吸扩效果的动画。

动画类里面,最核心就是扩展Animation类,重写applyTransformation方法。

6,总结

本文简单介绍了吸放效果的实现,根据这个原理,我们可以构造更加复杂的Path来做更多的效果。同时,也能实现向上,向左,向右的吸入效果。

最本质是我们要理解Mesh的概念,最核心的工作就是构造出Mesh的顶点坐标。

计算Mesh通常是一个很复杂的工作,作一些简单的变形还可以,对于太复杂的变形,可能还是不太方便。另外,像书籍翻页的效果,用mesh其实也是可以做到的。只是算法复杂一点。

这里不能给出完整的代码,原理可能不是说得太清楚,但愿给想实现的人一个思路指引吧。

7,实现代码

InhaleAnimationActivity.java

复制代码 代码如下:

package com.nj1s.lib.test.anim;

import android.os.Bundle;

import android.view.Gravity;

import android.view.Menu;

import android.view.MenuItem;

import android.view.View;

import android.widget.Button;

import android.widget.LinearLayout;

import com.nj1s.lib.mesh.InhaleMesh.InhaleDir;

import com.nj1s.lib.test.GABaseActivity;

import com.nj1s.lib.test.R;

import com.nj1s.lib.test.effect.BitmapMesh;

public class InhaleAnimationActivity extends GABaseActivity

{

private static final boolean DEBUG_MODE = false;

private BitmapMesh.SampleView mSampleView = null;

@Override

protected void onCreate(Bundle savedInstanceState)

{

super.onCreate(savedInstanceState);

LinearLayout linearLayout = new LinearLayout(this);

mSampleView = new BitmapMesh.SampleView(this);

mSampleView.setIsDebug(DEBUG_MODE);

mSampleView.setLayoutParams(new LinearLayout.LayoutParams(-1, -1));

Button btn = new Button(this);

btn.setText("Run");

btn.setTextSize(20.0f);

btn.setLayoutParams(new LinearLayout.LayoutParams(150, -2));

btn.setOnClickListener(new View.OnClickListener()

{

boolean mReverse = false;

@Override

public void onClick(View v)

{

if (mSampleView.startAnimation(mReverse))

{

mReverse = !mReverse;

}

}

});

linearLayout.setOrientation(LinearLayout.VERTICAL);

linearLayout.setGravity(Gravity.CENTER_VERTICAL);

linearLayout.addView(btn);

linearLayout.addView(mSampleView);

setContentView(linearLayout);

}

@Override

public boolean onCreateOptionsMenu(Menu menu)

{

getMenuInflater().inflate(R.menu.inhale_anim_menu, menu);

return true;

}

@Override

public boolean onOptionsItemSelected(MenuItem item)

{

switch(item.getItemId())

{

case R.id.menu_inhale_down:

mSampleView.setInhaleDir(InhaleDir.DOWN);

break;

case R.id.menu_inhale_up:

mSampleView.setInhaleDir(InhaleDir.UP);

break;

case R.id.menu_inhale_left:

mSampleView.setInhaleDir(InhaleDir.LEFT);

break;

case R.id.menu_inhale_right:

mSampleView.setInhaleDir(InhaleDir.RIGHT);

break;

}

return super.onOptionsItemSelected(item);

}

}

BitmapMesh.java

复制代码 代码如下:

/*

* Copyright (C) 2008 The Android Open Source Project

*

* Licensed under the Apache License, Version 2.0 (the "License");

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at

*

* http://www.apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/

package com.nj1s.lib.test.effect;

import android.content.Context;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Matrix;

import android.graphics.Paint;

import android.graphics.Paint.Style;

import android.graphics.Path;

import android.util.Log;

import android.view.MotionEvent;

import android.view.View;

import android.view.animation.Animation;

import android.view.animation.Interpolator;

import android.view.animation.Transformation;

import com.nj1s.lib.mesh.InhaleMesh;

import com.nj1s.lib.mesh.InhaleMesh.InhaleDir;

import com.nj1s.lib.test.R;

public class BitmapMesh {

public static class SampleView extends View {

private static final int WIDTH = 40;

private static final int HEIGHT = 40;

private final Bitmap mBitmap;

private final Matrix mMatrix = new Matrix();

private final Matrix mInverse = new Matrix();

private boolean mIsDebug = false;

private Paint mPaint = new Paint();

private float[] mInhalePt = new float[] {0, 0};

private InhaleMesh mInhaleMesh = null;

public SampleView(Context context) {

super(context);

setFocusable(true);

mBitmap = BitmapFactory.decodeResource(getResources(),

R.drawable.beach);

mInhaleMesh = new InhaleMesh(WIDTH, HEIGHT);

mInhaleMesh.setBitmapSize(mBitmap.getWidth(), mBitmap.getHeight());

mInhaleMesh.setInhaleDir(InhaleDir.DOWN);

}

public void setIsDebug(boolean isDebug)

{

mIsDebug = isDebug;

}

public void setInhaleDir(InhaleMesh.InhaleDir dir)

{

mInhaleMesh.setInhaleDir(dir);

float w = mBitmap.getWidth();

float h = mBitmap.getHeight();

float endX = 0;

float endY = 0;

float dx = 10;

float dy = 10;

mMatrix.reset();

switch (dir)

{

case DOWN:

endX = w / 2;

endY = getHeight() - 20;

break;

case UP:

dy = getHeight() - h - 20;

endX = w / 2;

endY = -dy + 10;

break;

case LEFT:

dx = getWidth() - w - 20;

endX = -dx + 10;

endY = h / 2;

break;

case RIGHT:

endX = getWidth() - 20;

endY = h / 2;

break;

}

mMatrix.setTranslate(dx, dy);

mMatrix.invert(mInverse);

buildPaths(endX, endY);

buildMesh(w, h);

invalidate();

}

@Override

protected void onSizeChanged(int w, int h, int oldw, int oldh)

{

super.onSizeChanged(w, h, oldw, oldh);

float bmpW = mBitmap.getWidth();

float bmpH = mBitmap.getHeight();

mMatrix.setTranslate(10, 10);

//mMatrix.setTranslate(10, 10);

mMatrix.invert(mInverse);

mPaint.setColor(Color.RED);

mPaint.setStrokeWidth(2);

mPaint.setAntiAlias(true);

buildPaths(bmpW / 2, h - 20);

buildMesh(bmpW, bmpH);

}

public boolean startAnimation(boolean reverse)

{

Animation anim = this.getAnimation();

if (null != anim && !anim.hasEnded())

{

return false;

}

PathAnimation animation = new PathAnimation(0, HEIGHT + 1, reverse,

new PathAnimation.IAnimationUpdateListener()

{

@Override

public void onAnimUpdate(int index)

{

mInhaleMesh.buildMeshes(index);

invalidate();

}

});

if (null != animation)

{

animation.setDuration(1000);

this.startAnimation(animation);

}

return true;

}

@Override

protected void onDraw(Canvas canvas)

{

Log.i("leehong2", "onDraw =========== ");

canvas.drawColor(0xFFCCCCCC);

canvas.concat(mMatrix);

canvas.drawBitmapMesh(mBitmap,

mInhaleMesh.getWidth(),

mInhaleMesh.getHeight(),

mInhaleMesh.getVertices(),

0, null, 0, mPaint);

// ===========================================

// Draw the target point.

mPaint.setColor(Color.RED);

mPaint.setStyle(Style.FILL);

canvas.drawCircle(mInhalePt[0], mInhalePt[1], 5, mPaint);

if (mIsDebug)

{

// ===========================================

// Draw the mesh vertices.

canvas.drawPoints(mInhaleMesh.getVertices(), mPaint);

// ===========================================

// Draw the paths

mPaint.setColor(Color.BLUE);

mPaint.setStyle(Style.STROKE);

Path[] paths = mInhaleMesh.getPaths();

for (Path path : paths)

{

canvas.drawPath(path, mPaint);

}

}

}

private void buildMesh(float w, float h)

{

mInhaleMesh.buildMeshes(w, h);

}

private void buildPaths(float endX, float endY)

{

mInhalePt[0] = endX;

mInhalePt[1] = endY;

mInhaleMesh.buildPaths(endX, endY);

}

int mLastWarpX = 0;

int mLastWarpY = 0;

@Override

public boolean onTouchEvent(MotionEvent event)

{

float[] pt = { event.getX(), event.getY() };

mInverse.mapPoints(pt);

if (event.getAction() == MotionEvent.ACTION_UP)

{

int x = (int)pt[0];

int y = (int)pt[1];

if (mLastWarpX != x || mLastWarpY != y) {

mLastWarpX = x;

mLastWarpY = y;

buildPaths(pt[0], pt[1]);

invalidate();

}

}

return true;

}

}

private static class PathAnimation extends Animation

{

public interface IAnimationUpdateListener

{

public void onAnimUpdate(int index);

}

private int mFromIndex = 0;

private int mEndIndex = 0;

private boolean mReverse = false;

private IAnimationUpdateListener mListener = null;

public PathAnimation(int fromIndex, int endIndex, boolean reverse, IAnimationUpdateListener listener)

{

mFromIndex = fromIndex;

mEndIndex = endIndex;

mReverse = reverse;

mListener = listener;

}

public boolean getTransformation(long currentTime, Transformation outTransformation) {

boolean more = super.getTransformation(currentTime, outTransformation);

Log.d("leehong2", "getTransformation more = " + more);

return more;

}

@Override

protected void applyTransformation(float interpolatedTime, Transformation t)

{

int curIndex = 0;

Interpolator interpolator = this.getInterpolator();

if (null != interpolator)

{

float value = interpolator.getInterpolation(interpolatedTime);

interpolatedTime = value;

}

if (mReverse)

{

interpolatedTime = 1.0f - interpolatedTime;

}

curIndex = (int)(mFromIndex + (mEndIndex - mFromIndex) * interpolatedTime);

if (null != mListener)

{

Log.i("leehong2", "onAnimUpdate =========== curIndex = " + curIndex);

mListener.onAnimUpdate(curIndex);

}

}

}

}

最核心的类

InhaleMesh

复制代码 代码如下:

package com.nj1s.lib.mesh;

import android.graphics.Path;

import android.graphics.PathMeasure;

public class InhaleMesh extends Mesh

{

public enum InhaleDir

{

UP,

DOWN,

LEFT,

RIGHT,

}

private Path mFirstPath = new Path();

private Path mSecondPath = new Path();

private PathMeasure mFirstPathMeasure = new PathMeasure();

private PathMeasure mSecondPathMeasure = new PathMeasure();

private InhaleDir mInhaleDir = InhaleDir.DOWN;

public InhaleMesh(int width, int height)

{

super(width, height);

}

public void setInhaleDir(InhaleDir inhaleDir)

{

mInhaleDir = inhaleDir;

}

public InhaleDir getInhaleDir()

{

return mInhaleDir;

}

@Override

public void buildPaths(float endX, float endY)

{

if (mBmpWidth <= 0 || mBmpHeight <= 0)

{

throw new IllegalArgumentException(

"Bitmap size must be > 0, do you call setBitmapSize(int, int) method?");

}

switch (mInhaleDir)

{

case UP:

buildPathsUp(endX, endY);

break;

case DOWN:

buildPathsDown(endX, endY);

break;

case RIGHT:

buildPathsRight(endX, endY);

break;

case LEFT:

buildPathsLeft(endX, endY);

break;

}

}

@Override

public void buildMeshes(int index)

{

if (mBmpWidth <= 0 || mBmpHeight <= 0)

{

throw new IllegalArgumentException(

"Bitmap size must be > 0, do you call setBitmapSize(int, int) method?");

}

switch (mInhaleDir)

{

case UP:

case DOWN:

buildMeshByPathOnVertical(index);

break;

case RIGHT:

case LEFT:

buildMeshByPathOnHorizontal(index);

break;

}

}

public Path[] getPaths()

{

return new Path[] { mFirstPath, mSecondPath };

}

private void buildPathsDown(float endX, float endY)

{

mFirstPathMeasure.setPath(mFirstPath, false);

mSecondPathMeasure.setPath(mSecondPath, false);

float w = mBmpWidth;

float h = mBmpHeight;

mFirstPath.reset();

mSecondPath.reset();

mFirstPath.moveTo(0, 0);

mSecondPath.moveTo(w, 0);

mFirstPath.lineTo(0, h);

mSecondPath.lineTo(w, h);

mFirstPath.quadTo(0, (endY + h) / 2, endX, endY);

mSecondPath.quadTo(w, (endY + h) / 2, endX, endY);

}

private void buildPathsUp(float endX, float endY)

{

mFirstPathMeasure.setPath(mFirstPath, false);

mSecondPathMeasure.setPath(mSecondPath, false);

float w = mBmpWidth;

float h = mBmpHeight;

mFirstPath.reset();

mSecondPath.reset();

mFirstPath.moveTo(0, h);

mSecondPath.moveTo(w, h);

mFirstPath.lineTo(0, 0);

mSecondPath.lineTo(w, 0);

mFirstPath.quadTo(0, (endY - h) / 2, endX, endY);

mSecondPath.quadTo(w, (endY - h) / 2, endX, endY);

}

private void buildPathsRight(float endX, float endY)

{

mFirstPathMeasure.setPath(mFirstPath, false);

mSecondPathMeasure.setPath(mSecondPath, false);

float w = mBmpWidth;

float h = mBmpHeight;

mFirstPath.reset();

mSecondPath.reset();

mFirstPath.moveTo(0, 0);

mSecondPath.moveTo(0, h);

mFirstPath.lineTo(w, 0);

mSecondPath.lineTo(w, h);

mFirstPath.quadTo((endX + w) / 2, 0, endX, endY);

mSecondPath.quadTo((endX + w) / 2, h, endX, endY);

}

private void buildPathsLeft(float endX, float endY)

{

mFirstPathMeasure.setPath(mFirstPath, false);

mSecondPathMeasure.setPath(mSecondPath, false);

float w = mBmpWidth;

float h = mBmpHeight;

mFirstPath.reset();

mSecondPath.reset();

mFirstPath.moveTo(w, 0);

mSecondPath.moveTo(w, h);

mFirstPath.lineTo(0, 0);

mSecondPath.lineTo(0, h);

mFirstPath.quadTo((endX - w) / 2, 0, endX, endY);

mSecondPath.quadTo((endX - w) / 2, h, endX, endY);

}

private void buildMeshByPathOnVertical(int timeIndex)

{

mFirstPathMeasure.setPath(mFirstPath, false);

mSecondPathMeasure.setPath(mSecondPath, false);

int index = 0;

float[] pos1 = {0.0f, 0.0f};

float[] pos2 = {0.0f, 0.0f};

float firstLen = mFirstPathMeasure.getLength();

float secondLen = mSecondPathMeasure.getLength();

float len1 = firstLen / HEIGHT;

float len2 = secondLen / HEIGHT;

float firstPointDist = timeIndex * len1;

float secondPointDist = timeIndex * len2;

float height = mBmpHeight;

mFirstPathMeasure.getPosTan(firstPointDist, pos1, null);

mFirstPathMeasure.getPosTan(firstPointDist + height, pos2, null);

float x1 = pos1[0];

float x2 = pos2[0];

float y1 = pos1[1];

float y2 = pos2[1];

float FIRST_DIST = (float)Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );

float FIRST_H = FIRST_DIST / HEIGHT;

mSecondPathMeasure.getPosTan(secondPointDist, pos1, null);

mSecondPathMeasure.getPosTan(secondPointDist + height, pos2, null);

x1 = pos1[0];

x2 = pos2[0];

y1 = pos1[1];

y2 = pos2[1];

float SECOND_DIST = (float)Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );

float SECOND_H = SECOND_DIST / HEIGHT;

if (mInhaleDir == InhaleDir.DOWN)

{

for (int y = 0; y <= HEIGHT; ++y)

{

mFirstPathMeasure.getPosTan(y * FIRST_H + firstPointDist, pos1, null);

mSecondPathMeasure.getPosTan(y * SECOND_H + secondPointDist, pos2, null);

float w = pos2[0] - pos1[0];

float fx1 = pos1[0];

float fx2 = pos2[0];

float fy1 = pos1[1];

float fy2 = pos2[1];

float dy = fy2 - fy1;

float dx = fx2 - fx1;

for (int x = 0; x <= WIDTH; ++x)

{

// y = x * dy / dx

float fx = x * w / WIDTH;

float fy = fx * dy / dx;

mVerts[index * 2 + 0] = fx + fx1;

mVerts[index * 2 + 1] = fy + fy1;

index += 1;

}

}

}

else if (mInhaleDir == InhaleDir.UP)

{

for (int y = HEIGHT; y >= 0; --y)

{

mFirstPathMeasure.getPosTan(y * FIRST_H + firstPointDist, pos1, null);

mSecondPathMeasure.getPosTan(y * SECOND_H + secondPointDist, pos2, null);

float w = pos2[0] - pos1[0];

float fx1 = pos1[0];

float fx2 = pos2[0];

float fy1 = pos1[1];

float fy2 = pos2[1];

float dy = fy2 - fy1;

float dx = fx2 - fx1;

for (int x = 0; x <= WIDTH; ++x)

{

// y = x * dy / dx

float fx = x * w / WIDTH;

float fy = fx * dy / dx;

mVerts[index * 2 + 0] = fx + fx1;

mVerts[index * 2 + 1] = fy + fy1;

index += 1;

}

}

}

}

private void buildMeshByPathOnHorizontal(int timeIndex)

{

mFirstPathMeasure.setPath(mFirstPath, false);

mSecondPathMeasure.setPath(mSecondPath, false);

int index = 0;

float[] pos1 = {0.0f, 0.0f};

float[] pos2 = {0.0f, 0.0f};

float firstLen = mFirstPathMeasure.getLength();

float secondLen = mSecondPathMeasure.getLength();

float len1 = firstLen / WIDTH;

float len2 = secondLen / WIDTH;

float firstPointDist = timeIndex * len1;

float secondPointDist = timeIndex * len2;

float width = mBmpWidth;

mFirstPathMeasure.getPosTan(firstPointDist, pos1, null);

mFirstPathMeasure.getPosTan(firstPointDist + width, pos2, null);

float x1 = pos1[0];

float x2 = pos2[0];

float y1 = pos1[1];

float y2 = pos2[1];

float FIRST_DIST = (float)Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );

float FIRST_X = FIRST_DIST / WIDTH;

mSecondPathMeasure.getPosTan(secondPointDist, pos1, null);

mSecondPathMeasure.getPosTan(secondPointDist + width, pos2, null);

x1 = pos1[0];

x2 = pos2[0];

y1 = pos1[1];

y2 = pos2[1];

float SECOND_DIST = (float)Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );

float SECOND_X = SECOND_DIST / WIDTH;

if (mInhaleDir == InhaleDir.RIGHT)

{

for (int x = 0; x <= WIDTH; ++x)

{

mFirstPathMeasure.getPosTan(x * FIRST_X + firstPointDist, pos1, null);

mSecondPathMeasure.getPosTan(x * SECOND_X + secondPointDist, pos2, null);

float h = pos2[1] - pos1[1];

float fx1 = pos1[0];

float fx2 = pos2[0];

float fy1 = pos1[1];

float fy2 = pos2[1];

float dy = fy2 - fy1;

float dx = fx2 - fx1;

for (int y = 0; y <= HEIGHT; ++y)

{

// x = y * dx / dy

float fy = y * h / HEIGHT;

float fx = fy * dx / dy;

index = y * (WIDTH + 1) + x;

mVerts[index * 2 + 0] = fx + fx1;

mVerts[index * 2 + 1] = fy + fy1;

}

}

}

else if (mInhaleDir == InhaleDir.LEFT)

{

for (int x = WIDTH; x >= 0; --x)

//for (int x = 0; x <= WIDTH; ++x)

{

mFirstPathMeasure.getPosTan(x * FIRST_X + firstPointDist, pos1, null);

mSecondPathMeasure.getPosTan(x * SECOND_X + secondPointDist, pos2, null);

float h = pos2[1] - pos1[1];

float fx1 = pos1[0];

float fx2 = pos2[0];

float fy1 = pos1[1];

float fy2 = pos2[1];

float dy = fy2 - fy1;

float dx = fx2 - fx1;

for (int y = 0; y <= HEIGHT; ++y)

{

// x = y * dx / dy

float fy = y * h / HEIGHT;

float fx = fy * dx / dy;

index = y * (WIDTH + 1) + WIDTH - x;

mVerts[index * 2 + 0] = fx + fx1;

mVerts[index * 2 + 1] = fy + fy1;

}

}

}

}

}

Mesh类的实现

复制代码 代码如下:

/*

* System: CoreLib

* @version 1.00

*

* Copyright (C) 2010, LZT Corporation.

*

*/

package com.nj1s.lib.mesh;

public abstract class Mesh

{

protected int WIDTH = 40;

protected int HEIGHT = 40;

protected int mBmpWidth = -1;

protected int mBmpHeight = -1;

protected final float[] mVerts;

public Mesh(int width, int height)

{

WIDTH = width;

HEIGHT = height;

mVerts = new float[(WIDTH + 1) * (HEIGHT + 1) * 2];

}

public float[] getVertices()

{

return mVerts;

}

public int getWidth()

{

return WIDTH;

}

public int getHeight()

{

return HEIGHT;

}

public static void setXY(float[] array, int index, float x, float y)

{

array[index*2 + 0] = x;

array[index*2 + 1] = y;

}

public void setBitmapSize(int w, int h)

{

mBmpWidth = w;

mBmpHeight = h;

}

public abstract void buildPaths(float endX, float endY);

public abstract void buildMeshes(int index);

public void buildMeshes(float w, float h)

{

int index = 0;

for (int y = 0; y <= HEIGHT; ++y)

{

float fy = y * h / HEIGHT;

for (int x = 0; x <= WIDTH; ++x)

{

float fx = x * w / WIDTH;

setXY(mVerts, index, fx, fy);

index += 1;

}

}

}

}

【Android 吸入动画效果实现分解】相关文章:

基于Android本地代码生成器详解

Android 按后退键退出Android程序的实现方法

Android开发实现HttpClient工具类

Android adb的常用命令

android 左右滑动+索引图标实现方法与代码

Android中的Button自定义点击效果实例代码

Android 显示和隐藏输入法实现代码

Android 监听apk安装替换卸载广播的实现代码

android平台的左右上下都能滚动的效果

android底部菜单栏实现原理与代码

精品推荐
分类导航