Android 吸入动画效果详解

来源:互联网 发布:抢火车票软件下载 编辑:程序博客网 时间:2024/04/27 13:40

1,背景


吸入(Inhale)效果,最初我是在iOS上面看到的,它是在Note程序中,用户可能添加了一页记录,在做删除时,它的删除效果是:这一页内容吸入到一个垃圾框的图标里面。请看下图所示:



===============================================================================

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




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

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


2,Mesh的概念


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



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

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

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




3,如何构建Mesh


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


3.1,创建两条路径(Path)

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



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

构建两条Path的代码如下: 


[java] view plain copy
  1. mFirstPathMeasure.setPath(mFirstPath, false);  
  2. mSecondPathMeasure.setPath(mSecondPath, false);  
  3.   
  4. float w = mBmpWidth;  
  5. float h = mBmpHeight;  
  6.   
  7. mFirstPath.reset();  
  8. mSecondPath.reset();  
  9. mFirstPath.moveTo(00);  
  10. mSecondPath.moveTo(w, 0);  
  11.   
  12. mFirstPath.lineTo(0, h);  
  13. mSecondPath.lineTo(w, h);  
  14.   
  15. mFirstPath.quadTo(0, (endY + h) / 2, endX, endY);  
  16. mSecondPath.quadTo(w, (endY + h) / 2, endX, endY);  


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

算法:

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

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




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

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




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

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


[java] view plain copy
  1. private void buildMeshByPathOnVertical(int timeIndex)  
  2. {  
  3.     mFirstPathMeasure.setPath(mFirstPath, false);  
  4.     mSecondPathMeasure.setPath(mSecondPath, false);  
  5.   
  6.     int index = 0;  
  7.     float[] pos1 = {0.0f, 0.0f};  
  8.     float[] pos2 = {0.0f, 0.0f};  
  9.     float firstLen  = mFirstPathMeasure.getLength();  
  10.     float secondLen = mSecondPathMeasure.getLength();  
  11.   
  12.     float len1 = firstLen / HEIGHT;  
  13.     float len2 = secondLen / HEIGHT;  
  14.   
  15.     float firstPointDist  = timeIndex * len1;  
  16.     float secondPointDist = timeIndex * len2;  
  17.     float height = mBmpHeight;  
  18.   
  19.     mFirstPathMeasure.getPosTan(firstPointDist, pos1, null);  
  20.     mFirstPathMeasure.getPosTan(firstPointDist + height, pos2, null);  
  21.     float x1 = pos1[0];  
  22.     float x2 = pos2[0];  
  23.     float y1 = pos1[1];  
  24.     float y2 = pos2[1];  
  25.     float FIRST_DIST  = (float)Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );  
  26.     float FIRST_H = FIRST_DIST / HEIGHT;  
  27.   
  28.     mSecondPathMeasure.getPosTan(secondPointDist, pos1, null);  
  29.     mSecondPathMeasure.getPosTan(secondPointDist + height, pos2, null);  
  30.     x1 = pos1[0];  
  31.     x2 = pos2[0];  
  32.     y1 = pos1[1];  
  33.     y2 = pos2[1];  
  34.   
  35.     float SECOND_DIST = (float)Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );  
  36.     float SECOND_H = SECOND_DIST / HEIGHT;  
  37.   
  38.     for (int y = 0; y <= HEIGHT; ++y)  
  39.     {  
  40.         mFirstPathMeasure.getPosTan(y * FIRST_H + firstPointDist, pos1, null);  
  41.         mSecondPathMeasure.getPosTan(y * SECOND_H + secondPointDist, pos2, null);  
  42.   
  43.         float w = pos2[0] - pos1[0];  
  44.         float fx1 = pos1[0];  
  45.         float fx2 = pos2[0];  
  46.         float fy1 = pos1[1];  
  47.         float fy2 = pos2[1];  
  48.         float dy = fy2 - fy1;  
  49.         float dx = fx2 - fx1;  
  50.   
  51.         for (int x = 0; x <= WIDTH; ++x)  
  52.         {  
  53.             // y = x * dy / dx  
  54.             float fx = x * w / WIDTH;  
  55.             float fy = fx * dy / dx;  
  56.   
  57.             mVerts[index * 2 + 0] = fx + fx1;  
  58.             mVerts[index * 2 + 1] = fy + fy1;  
  59.   
  60.             index += 1;  
  61.         }  
  62.     }  
  63. }  


4,如何绘制


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


[java] view plain copy
  1. canvas.drawBitmapMesh(mBitmap,  
  2.         mInhaleMesh.getWidth(),  
  3.         mInhaleMesh.getHeight(),  
  4.         mInhaleMesh.getVertices(),  
  5.         0null0, mPaint);  


5,如何实现动画

[java] view plain copy
  1. protected void applyTransformation(float interpolatedTime, Transformation t)  
  2.         {  
  3.             int curIndex = 0;  
  4.             Interpolator interpolator = this.getInterpolator();  
  5.             if (null != interpolator)  
  6.             {  
  7.                 float value = interpolator.getInterpolation(interpolatedTime);  
  8.                 interpolatedTime = value;  
  9.             }  
  10.   
  11.             if (mReverse)  
  12.             {  
  13.                 interpolatedTime = 1.0f - interpolatedTime;  
  14.             }  
  15.   
  16.             curIndex = (int)(mFromIndex + (mEndIndex - mFromIndex) * interpolatedTime);  
  17.   
  18.             if (null != mListener)  
  19.             {  
  20.                 mListener.onAnimUpdate(curIndex);  
  21.             }  
  22.         }  

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

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


6,总结


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

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


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

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


7,实现代码


InhaleAnimationActivity.java

[java] view plain copy
  1. package com.nj1s.lib.test.anim;  
  2.   
  3. import android.os.Bundle;  
  4. import android.view.Gravity;  
  5. import android.view.Menu;  
  6. import android.view.MenuItem;  
  7. import android.view.View;  
  8. import android.widget.Button;  
  9. import android.widget.LinearLayout;  
  10.   
  11. import com.nj1s.lib.mesh.InhaleMesh.InhaleDir;  
  12. import com.nj1s.lib.test.GABaseActivity;  
  13. import com.nj1s.lib.test.R;  
  14. import com.nj1s.lib.test.effect.BitmapMesh;  
  15.   
  16. public class InhaleAnimationActivity extends GABaseActivity  
  17. {  
  18.     private static final boolean DEBUG_MODE = false;  
  19.     private BitmapMesh.SampleView mSampleView = null;  
  20.       
  21.     @Override  
  22.     protected void onCreate(Bundle savedInstanceState)  
  23.     {  
  24.         super.onCreate(savedInstanceState);  
  25.           
  26.         LinearLayout linearLayout = new LinearLayout(this);  
  27.           
  28.         mSampleView = new BitmapMesh.SampleView(this);  
  29.         mSampleView.setIsDebug(DEBUG_MODE);  
  30.         mSampleView.setLayoutParams(new LinearLayout.LayoutParams(-1, -1));  
  31.         Button btn = new Button(this);  
  32.         btn.setText("Run");  
  33.         btn.setTextSize(20.0f);  
  34.         btn.setLayoutParams(new LinearLayout.LayoutParams(150, -2));  
  35.         btn.setOnClickListener(new View.OnClickListener()  
  36.         {  
  37.             boolean mReverse = false;  
  38.               
  39.             @Override  
  40.             public void onClick(View v)  
  41.             {  
  42.                 if (mSampleView.startAnimation(mReverse))  
  43.                 {  
  44.                     mReverse = !mReverse;  
  45.                 }  
  46.             }  
  47.         });  
  48.           
  49.         linearLayout.setOrientation(LinearLayout.VERTICAL);  
  50.         linearLayout.setGravity(Gravity.CENTER_VERTICAL);  
  51.         linearLayout.addView(btn);  
  52.         linearLayout.addView(mSampleView);  
  53.           
  54.         setContentView(linearLayout);  
  55.     }  
  56.   
  57.     @Override  
  58.     public boolean onCreateOptionsMenu(Menu menu)   
  59.     {  
  60.         getMenuInflater().inflate(R.menu.inhale_anim_menu, menu);  
  61.         return true;  
  62.     }  
  63.   
  64.     @Override  
  65.     public boolean onOptionsItemSelected(MenuItem item)  
  66.     {  
  67.         switch(item.getItemId())  
  68.         {  
  69.         case R.id.menu_inhale_down:  
  70.             mSampleView.setInhaleDir(InhaleDir.DOWN);  
  71.             break;  
  72.               
  73.         case R.id.menu_inhale_up:  
  74.             mSampleView.setInhaleDir(InhaleDir.UP);  
  75.             break;  
  76.               
  77.         case R.id.menu_inhale_left:  
  78.             mSampleView.setInhaleDir(InhaleDir.LEFT);  
  79.             break;  
  80.               
  81.         case R.id.menu_inhale_right:  
  82.             mSampleView.setInhaleDir(InhaleDir.RIGHT);  
  83.             break;  
  84.         }  
  85.           
  86.         return super.onOptionsItemSelected(item);  
  87.     }  
  88. }  


BitmapMesh.java

[java] view plain copy
  1. /* 
  2.  * Copyright (C) 2008 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package com.nj1s.lib.test.effect;  
  18.   
  19. import android.content.Context;  
  20. import android.graphics.Bitmap;  
  21. import android.graphics.BitmapFactory;  
  22. import android.graphics.Canvas;  
  23. import android.graphics.Color;  
  24. import android.graphics.Matrix;  
  25. import android.graphics.Paint;  
  26. import android.graphics.Paint.Style;  
  27. import android.graphics.Path;  
  28. import android.util.Log;  
  29. import android.view.MotionEvent;  
  30. import android.view.View;  
  31. import android.view.animation.Animation;  
  32. import android.view.animation.Interpolator;  
  33. import android.view.animation.Transformation;  
  34.   
  35. import com.nj1s.lib.mesh.InhaleMesh;  
  36. import com.nj1s.lib.mesh.InhaleMesh.InhaleDir;  
  37. import com.nj1s.lib.test.R;  
  38.   
  39. public class BitmapMesh {  
  40.       
  41.     public static class SampleView extends View {  
  42.           
  43.         private static final int WIDTH = 40;  
  44.         private static final int HEIGHT = 40;  
  45.   
  46.         private final Bitmap mBitmap;  
  47.         private final Matrix mMatrix = new Matrix();  
  48.         private final Matrix mInverse = new Matrix();  
  49.           
  50.         private boolean mIsDebug = false;  
  51.         private Paint mPaint = new Paint();  
  52.         private float[] mInhalePt = new float[] {00};  
  53.         private InhaleMesh mInhaleMesh = null;  
  54.   
  55.         public SampleView(Context context) {  
  56.             super(context);  
  57.             setFocusable(true);  
  58.   
  59.             mBitmap = BitmapFactory.decodeResource(getResources(),  
  60.                                                      R.drawable.beach);  
  61.               
  62.             mInhaleMesh = new InhaleMesh(WIDTH, HEIGHT);  
  63.             mInhaleMesh.setBitmapSize(mBitmap.getWidth(), mBitmap.getHeight());  
  64.             mInhaleMesh.setInhaleDir(InhaleDir.DOWN);  
  65.         }  
  66.           
  67.         public void setIsDebug(boolean isDebug)  
  68.         {  
  69.             mIsDebug = isDebug;  
  70.         }  
  71.           
  72.         public void setInhaleDir(InhaleMesh.InhaleDir dir)  
  73.         {  
  74.             mInhaleMesh.setInhaleDir(dir);  
  75.               
  76.             float w = mBitmap.getWidth();  
  77.             float h = mBitmap.getHeight();  
  78.             float endX = 0;  
  79.             float endY = 0;  
  80.             float dx = 10;  
  81.             float dy = 10;  
  82.             mMatrix.reset();  
  83.               
  84.             switch (dir)  
  85.             {  
  86.             case DOWN:  
  87.                 endX = w / 2;  
  88.                 endY = getHeight() - 20;  
  89.                 break;  
  90.                   
  91.             case UP:  
  92.                 dy = getHeight() - h - 20;  
  93.                 endX = w / 2;  
  94.                 endY = -dy + 10;  
  95.                 break;  
  96.                   
  97.             case LEFT:  
  98.                 dx = getWidth() - w - 20;  
  99.                 endX = -dx + 10;  
  100.                 endY = h / 2;  
  101.                 break;  
  102.                   
  103.             case RIGHT:  
  104.                 endX = getWidth() - 20;  
  105.                 endY = h / 2;  
  106.                 break;  
  107.             }  
  108.               
  109.             mMatrix.setTranslate(dx, dy);  
  110.             mMatrix.invert(mInverse);  
  111.             buildPaths(endX, endY);  
  112.             buildMesh(w, h);  
  113.             invalidate();  
  114.         }  
  115.           
  116.         @Override  
  117.         protected void onSizeChanged(int w, int h, int oldw, int oldh)  
  118.         {  
  119.             super.onSizeChanged(w, h, oldw, oldh);  
  120.               
  121.             float bmpW = mBitmap.getWidth();  
  122.             float bmpH = mBitmap.getHeight();  
  123.               
  124.             mMatrix.setTranslate(1010);  
  125.             //mMatrix.setTranslate(10, 10);  
  126.             mMatrix.invert(mInverse);  
  127.               
  128.             mPaint.setColor(Color.RED);  
  129.             mPaint.setStrokeWidth(2);  
  130.             mPaint.setAntiAlias(true);  
  131.               
  132.             buildPaths(bmpW / 2, h - 20);  
  133.             buildMesh(bmpW, bmpH);  
  134.         }  
  135.   
  136.         public boolean startAnimation(boolean reverse)  
  137.         {  
  138.             Animation anim = this.getAnimation();  
  139.             if (null != anim && !anim.hasEnded())  
  140.             {  
  141.                 return false;  
  142.             }  
  143.               
  144.             PathAnimation animation = new PathAnimation(0, HEIGHT + 1, reverse,   
  145.                     new PathAnimation.IAnimationUpdateListener()  
  146.             {  
  147.                 @Override  
  148.                 public void onAnimUpdate(int index)  
  149.                 {  
  150.                     mInhaleMesh.buildMeshes(index);  
  151.                     invalidate();  
  152.                 }  
  153.             });  
  154.               
  155.             if (null != animation)  
  156.             {  
  157.                 animation.setDuration(1000);  
  158.                 this.startAnimation(animation);  
  159.             }  
  160.               
  161.             return true;  
  162.         }  
  163.           
  164.         @Override   
  165.         protected void onDraw(Canvas canvas)  
  166.         {  
  167.             Log.i("leehong2""onDraw  =========== ");  
  168.             canvas.drawColor(0xFFCCCCCC);  
  169.   
  170.             canvas.concat(mMatrix);  
  171.               
  172.             canvas.drawBitmapMesh(mBitmap,  
  173.                     mInhaleMesh.getWidth(),   
  174.                     mInhaleMesh.getHeight(),   
  175.                     mInhaleMesh.getVertices(),  
  176.                     0null0, mPaint);  
  177.               
  178.             // ===========================================  
  179.             // Draw the target point.  
  180.             mPaint.setColor(Color.RED);  
  181.             mPaint.setStyle(Style.FILL);  
  182.             canvas.drawCircle(mInhalePt[0], mInhalePt[1], 5, mPaint);  
  183.               
  184.             if (mIsDebug)  
  185.             {  
  186.                 // ===========================================  
  187.                 // Draw the mesh vertices.  
  188.                 canvas.drawPoints(mInhaleMesh.getVertices(), mPaint);  
  189.                   
  190.                 // ===========================================  
  191.                 // Draw the paths  
  192.                 mPaint.setColor(Color.BLUE);  
  193.                 mPaint.setStyle(Style.STROKE);  
  194.                 Path[] paths = mInhaleMesh.getPaths();  
  195.                 for (Path path : paths)  
  196.                 {  
  197.                     canvas.drawPath(path, mPaint);  
  198.                 }  
  199.             }  
  200.         }  
  201.           
  202.         private void buildMesh(float w, float h)  
  203.         {  
  204.             mInhaleMesh.buildMeshes(w, h);  
  205.         }  
  206.           
  207.         private void buildPaths(float endX, float endY)  
  208.         {  
  209.             mInhalePt[0] = endX;  
  210.             mInhalePt[1] = endY;  
  211.             mInhaleMesh.buildPaths(endX, endY);  
  212.         }  
  213.           
  214.         int mLastWarpX = 0;  
  215.         int mLastWarpY = 0;  
  216.         @Override   
  217.         public boolean onTouchEvent(MotionEvent event)  
  218.         {  
  219.             float[] pt = { event.getX(), event.getY() };  
  220.             mInverse.mapPoints(pt);  
  221.   
  222.             if (event.getAction() == MotionEvent.ACTION_UP)  
  223.             {  
  224.                 int x = (int)pt[0];  
  225.                 int y = (int)pt[1];  
  226.                 if (mLastWarpX != x || mLastWarpY != y) {  
  227.                     mLastWarpX = x;  
  228.                     mLastWarpY = y;  
  229.                     buildPaths(pt[0], pt[1]);  
  230.                     invalidate();  
  231.                 }  
  232.             }  
  233.             return true;  
  234.         }  
  235.     }  
  236.       
  237.     private static class PathAnimation extends Animation  
  238.     {  
  239.         public interface IAnimationUpdateListener  
  240.         {  
  241.             public void onAnimUpdate(int index);  
  242.         }  
  243.           
  244.         private int mFromIndex = 0;  
  245.         private int mEndIndex = 0;  
  246.         private boolean mReverse = false;  
  247.         private IAnimationUpdateListener mListener = null;  
  248.           
  249.         public PathAnimation(int fromIndex, int endIndex, boolean reverse, IAnimationUpdateListener listener)  
  250.         {  
  251.             mFromIndex = fromIndex;  
  252.             mEndIndex = endIndex;  
  253.             mReverse = reverse;  
  254.             mListener = listener;  
  255.         }  
  256.           
  257.         public boolean getTransformation(long currentTime, Transformation outTransformation) {  
  258.               
  259.             boolean more = super.getTransformation(currentTime, outTransformation);  
  260.             Log.d("leehong2""getTransformation    more = " + more);  
  261.             return more;  
  262.         }  
  263.           
  264.         @Override  
  265.         protected void applyTransformation(float interpolatedTime, Transformation t)   
  266.         {  
  267.             int curIndex = 0;  
  268.             Interpolator interpolator = this.getInterpolator();  
  269.             if (null != interpolator)  
  270.             {  
  271.                 float value = interpolator.getInterpolation(interpolatedTime);  
  272.                 interpolatedTime = value;  
  273.             }  
  274.               
  275.             if (mReverse)  
  276.             {  
  277.                 interpolatedTime = 1.0f - interpolatedTime;  
  278.             }  
  279.               
  280.             curIndex = (int)(mFromIndex + (mEndIndex - mFromIndex) * interpolatedTime);  
  281.               
  282.             if (null != mListener)  
  283.             {  
  284.                 Log.i("leehong2""onAnimUpdate  =========== curIndex = " + curIndex);  
  285.                 mListener.onAnimUpdate(curIndex);  
  286.             }  
  287.         }  
  288.     }  
  289. }  


最核心的类

InhaleMesh

[java] view plain copy
  1. package com.nj1s.lib.mesh;  
  2.   
  3. import android.graphics.Path;  
  4. import android.graphics.PathMeasure;  
  5.   
  6. public class InhaleMesh extends Mesh  
  7. {  
  8.     public enum InhaleDir  
  9.     {  
  10.         UP,  
  11.         DOWN,  
  12.         LEFT,  
  13.         RIGHT,  
  14.     }  
  15.       
  16.     private Path mFirstPath  = new Path();  
  17.     private Path mSecondPath = new Path();  
  18.     private PathMeasure mFirstPathMeasure  = new PathMeasure();  
  19.     private PathMeasure mSecondPathMeasure = new PathMeasure();  
  20.     private InhaleDir mInhaleDir = InhaleDir.DOWN;  
  21.       
  22.     public InhaleMesh(int width, int height)  
  23.     {  
  24.         super(width, height);  
  25.     }  
  26.       
  27.     public void setInhaleDir(InhaleDir inhaleDir)  
  28.     {  
  29.         mInhaleDir = inhaleDir;  
  30.     }  
  31.       
  32.     public InhaleDir getInhaleDir()  
  33.     {  
  34.         return mInhaleDir;  
  35.     }  
  36.   
  37.     @Override  
  38.     public void buildPaths(float endX, float endY)  
  39.     {  
  40.         if (mBmpWidth <= 0 || mBmpHeight <= 0)  
  41.         {  
  42.             throw new IllegalArgumentException(  
  43.                     "Bitmap size must be > 0, do you call setBitmapSize(int, int) method?");  
  44.         }  
  45.           
  46.         switch (mInhaleDir)  
  47.         {  
  48.         case UP:  
  49.             buildPathsUp(endX, endY);  
  50.             break;  
  51.               
  52.         case DOWN:  
  53.             buildPathsDown(endX, endY);  
  54.             break;  
  55.               
  56.         case RIGHT:  
  57.             buildPathsRight(endX, endY);  
  58.             break;  
  59.               
  60.         case LEFT:  
  61.             buildPathsLeft(endX, endY);  
  62.             break;  
  63.         }  
  64.     }  
  65.   
  66.     @Override  
  67.     public void buildMeshes(int index)  
  68.     {  
  69.         if (mBmpWidth <= 0 || mBmpHeight <= 0)  
  70.         {  
  71.             throw new IllegalArgumentException(  
  72.                     "Bitmap size must be > 0, do you call setBitmapSize(int, int) method?");  
  73.         }  
  74.           
  75.         switch (mInhaleDir)  
  76.         {  
  77.         case UP:  
  78.         case DOWN:  
  79.             buildMeshByPathOnVertical(index);  
  80.             break;  
  81.               
  82.         case RIGHT:  
  83.         case LEFT:  
  84.             buildMeshByPathOnHorizontal(index);  
  85.             break;  
  86.         }  
  87.     }  
  88.       
  89.     public Path[] getPaths()  
  90.     {  
  91.         return new Path[] { mFirstPath, mSecondPath };  
  92.     }  
  93.       
  94.     private void buildPathsDown(float endX, float endY)  
  95.     {  
  96.         mFirstPathMeasure.setPath(mFirstPath, false);  
  97.         mSecondPathMeasure.setPath(mSecondPath, false);  
  98.           
  99.         float w = mBmpWidth;  
  100.         float h = mBmpHeight;  
  101.           
  102.         mFirstPath.reset();  
  103.         mSecondPath.reset();  
  104.         mFirstPath.moveTo(00);  
  105.         mSecondPath.moveTo(w, 0);  
  106.           
  107.         mFirstPath.lineTo(0, h);  
  108.         mSecondPath.lineTo(w, h);  
  109.           
  110.         mFirstPath.quadTo(0, (endY + h) / 2, endX, endY);  
  111.         mSecondPath.quadTo(w, (endY + h) / 2, endX, endY);  
  112.     }  
  113.       
  114.     private void buildPathsUp(float endX, float endY)  
  115.     {  
  116.         mFirstPathMeasure.setPath(mFirstPath, false);  
  117.         mSecondPathMeasure.setPath(mSecondPath, false);  
  118.           
  119.         float w = mBmpWidth;  
  120.         float h = mBmpHeight;  
  121.           
  122.         mFirstPath.reset();  
  123.         mSecondPath.reset();  
  124.         mFirstPath.moveTo(0, h);  
  125.         mSecondPath.moveTo(w, h);  
  126.           
  127.         mFirstPath.lineTo(00);  
  128.         mSecondPath.lineTo(w, 0);  
  129.           
  130.         mFirstPath.quadTo(0, (endY - h) / 2, endX, endY);  
  131.         mSecondPath.quadTo(w, (endY - h) / 2, endX, endY);  
  132.     }  
  133.       
  134.     private void buildPathsRight(float endX, float endY)  
  135.     {  
  136.         mFirstPathMeasure.setPath(mFirstPath, false);  
  137.         mSecondPathMeasure.setPath(mSecondPath, false);  
  138.           
  139.         float w = mBmpWidth;  
  140.         float h = mBmpHeight;  
  141.           
  142.         mFirstPath.reset();  
  143.         mSecondPath.reset();  
  144.           
  145.         mFirstPath.moveTo(00);  
  146.         mSecondPath.moveTo(0, h);  
  147.           
  148.         mFirstPath.lineTo(w, 0);  
  149.         mSecondPath.lineTo(w, h);  
  150.           
  151.         mFirstPath.quadTo((endX + w) / 20, endX, endY);  
  152.         mSecondPath.quadTo((endX + w) / 2, h, endX, endY);  
  153.     }  
  154.       
  155.     private void buildPathsLeft(float endX, float endY)  
  156.     {  
  157.         mFirstPathMeasure.setPath(mFirstPath, false);  
  158.         mSecondPathMeasure.setPath(mSecondPath, false);  
  159.           
  160.         float w = mBmpWidth;  
  161.         float h = mBmpHeight;  
  162.           
  163.         mFirstPath.reset();  
  164.         mSecondPath.reset();  
  165.           
  166.         mFirstPath.moveTo(w, 0);  
  167.         mSecondPath.moveTo(w, h);  
  168.           
  169.         mFirstPath.lineTo(00);  
  170.         mSecondPath.lineTo(0, h);  
  171.           
  172.         mFirstPath.quadTo((endX - w) / 20, endX, endY);  
  173.         mSecondPath.quadTo((endX - w) / 2, h, endX, endY);  
  174.     }  
  175.       
  176.     private void buildMeshByPathOnVertical(int timeIndex)  
  177.     {  
  178.         mFirstPathMeasure.setPath(mFirstPath, false);  
  179.         mSecondPathMeasure.setPath(mSecondPath, false);  
  180.           
  181.         int index = 0;  
  182.         float[] pos1 = {0.0f, 0.0f};  
  183.         float[] pos2 = {0.0f, 0.0f};  
  184.         float firstLen  = mFirstPathMeasure.getLength();  
  185.         float secondLen = mSecondPathMeasure.getLength();  
  186.           
  187.         float len1 = firstLen / HEIGHT;  
  188.         float len2 = secondLen / HEIGHT;  
  189.           
  190.         float firstPointDist  = timeIndex * len1;  
  191.         float secondPointDist = timeIndex * len2;  
  192.         float height = mBmpHeight;  
  193.           
  194.         mFirstPathMeasure.getPosTan(firstPointDist, pos1, null);  
  195.         mFirstPathMeasure.getPosTan(firstPointDist + height, pos2, null);  
  196.         float x1 = pos1[0];  
  197.         float x2 = pos2[0];  
  198.         float y1 = pos1[1];  
  199.         float y2 = pos2[1];  
  200.         float FIRST_DIST  = (float)Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );  
  201.         float FIRST_H = FIRST_DIST / HEIGHT;  
  202.           
  203.         mSecondPathMeasure.getPosTan(secondPointDist, pos1, null);  
  204.         mSecondPathMeasure.getPosTan(secondPointDist + height, pos2, null);  
  205.         x1 = pos1[0];  
  206.         x2 = pos2[0];  
  207.         y1 = pos1[1];  
  208.         y2 = pos2[1];  
  209.           
  210.         float SECOND_DIST = (float)Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );  
  211.         float SECOND_H = SECOND_DIST / HEIGHT;  
  212.           
  213.         if (mInhaleDir == InhaleDir.DOWN)  
  214.         {  
  215.             for (int y = 0; y <= HEIGHT; ++y)  
  216.             {  
  217.                 mFirstPathMeasure.getPosTan(y * FIRST_H + firstPointDist, pos1, null);  
  218.                 mSecondPathMeasure.getPosTan(y * SECOND_H + secondPointDist, pos2, null);  
  219.                   
  220.                 float w = pos2[0] - pos1[0];  
  221.                 float fx1 = pos1[0];  
  222.                 float fx2 = pos2[0];  
  223.                 float fy1 = pos1[1];  
  224.                 float fy2 = pos2[1];  
  225.                 float dy = fy2 - fy1;  
  226.                 float dx = fx2 - fx1;  
  227.                   
  228.                 for (int x = 0; x <= WIDTH; ++x)  
  229.                 {  
  230.                     // y = x * dy / dx  
  231.                     float fx = x * w / WIDTH;  
  232.                     float fy = fx * dy / dx;  
  233.                       
  234.                     mVerts[index * 2 + 0] = fx + fx1;  
  235.                     mVerts[index * 2 + 1] = fy + fy1;  
  236.                       
  237.                     index += 1;  
  238.                 }  
  239.             }  
  240.         }  
  241.         else if (mInhaleDir == InhaleDir.UP)  
  242.         {  
  243.             for (int y = HEIGHT; y >= 0; --y)  
  244.             {  
  245.                 mFirstPathMeasure.getPosTan(y * FIRST_H + firstPointDist, pos1, null);  
  246.                 mSecondPathMeasure.getPosTan(y * SECOND_H + secondPointDist, pos2, null);  
  247.                   
  248.                 float w = pos2[0] - pos1[0];  
  249.                 float fx1 = pos1[0];  
  250.                 float fx2 = pos2[0];  
  251.                 float fy1 = pos1[1];  
  252.                 float fy2 = pos2[1];  
  253.                 float dy = fy2 - fy1;  
  254.                 float dx = fx2 - fx1;  
  255.                   
  256.                 for (int x = 0; x <= WIDTH; ++x)  
  257.                 {  
  258.                     // y = x * dy / dx  
  259.                     float fx = x * w / WIDTH;  
  260.                     float fy = fx * dy / dx;  
  261.                       
  262.                     mVerts[index * 2 + 0] = fx + fx1;  
  263.                     mVerts[index * 2 + 1] = fy + fy1;  
  264.                       
  265.                     index += 1;  
  266.                 }  
  267.             }  
  268.         }  
  269.     }  
  270.       
  271.     private void buildMeshByPathOnHorizontal(int timeIndex)  
  272.     {  
  273.         mFirstPathMeasure.setPath(mFirstPath, false);  
  274.         mSecondPathMeasure.setPath(mSecondPath, false);  
  275.           
  276.         int index = 0;  
  277.         float[] pos1 = {0.0f, 0.0f};  
  278.         float[] pos2 = {0.0f, 0.0f};  
  279.         float firstLen  = mFirstPathMeasure.getLength();  
  280.         float secondLen = mSecondPathMeasure.getLength();  
  281.           
  282.         float len1 = firstLen / WIDTH;  
  283.         float len2 = secondLen / WIDTH;  
  284.           
  285.         float firstPointDist  = timeIndex * len1;  
  286.         float secondPointDist = timeIndex * len2;  
  287.         float width = mBmpWidth;  
  288.           
  289.         mFirstPathMeasure.getPosTan(firstPointDist, pos1, null);  
  290.         mFirstPathMeasure.getPosTan(firstPointDist + width, pos2, null);  
  291.         float x1 = pos1[0];  
  292.         float x2 = pos2[0];  
  293.         float y1 = pos1[1];  
  294.         float y2 = pos2[1];  
  295.         float FIRST_DIST  = (float)Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );  
  296.         float FIRST_X = FIRST_DIST / WIDTH;  
  297.           
  298.         mSecondPathMeasure.getPosTan(secondPointDist, pos1, null);  
  299.         mSecondPathMeasure.getPosTan(secondPointDist + width, pos2, null);  
  300.         x1 = pos1[0];  
  301.         x2 = pos2[0];  
  302.         y1 = pos1[1];  
  303.         y2 = pos2[1];  
  304.           
  305.         float SECOND_DIST = (float)Math.sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) );  
  306.         float SECOND_X = SECOND_DIST / WIDTH;  
  307.           
  308.         if (mInhaleDir == InhaleDir.RIGHT)  
  309.         {  
  310.             for (int x = 0; x <= WIDTH; ++x)  
  311.             {  
  312.                 mFirstPathMeasure.getPosTan(x * FIRST_X + firstPointDist, pos1, null);  
  313.                 mSecondPathMeasure.getPosTan(x * SECOND_X + secondPointDist, pos2, null);  
  314.                   
  315.                 float h = pos2[1] - pos1[1];  
  316.                 float fx1 = pos1[0];  
  317.                 float fx2 = pos2[0];  
  318.                 float fy1 = pos1[1];  
  319.                 float fy2 = pos2[1];  
  320.                 float dy = fy2 - fy1;  
  321.                 float dx = fx2 - fx1;  
  322.                   
  323.                 for (int y = 0; y <= HEIGHT; ++y)  
  324.                 {  
  325.                     // x = y * dx / dy  
  326.                     float fy = y * h / HEIGHT;  
  327.                     float fx = fy * dx / dy;  
  328.                       
  329.                     index = y * (WIDTH + 1) + x;  
  330.                       
  331.                     mVerts[index * 2 + 0] = fx + fx1;  
  332.                     mVerts[index * 2 + 1] = fy + fy1;  
  333.                 }  
  334.             }  
  335.         }  
  336.         else if (mInhaleDir == InhaleDir.LEFT)  
  337.         {  
  338.             for (int x = WIDTH; x >= 0; --x)  
  339.             //for (int x = 0; x <= WIDTH; ++x)  
  340.             {  
  341.                 mFirstPathMeasure.getPosTan(x * FIRST_X + firstPointDist, pos1, null);  
  342.                 mSecondPathMeasure.getPosTan(x * SECOND_X + secondPointDist, pos2, null);  
  343.                   
  344.                 float h = pos2[1] - pos1[1];  
  345.                 float fx1 = pos1[0];  
  346.                 float fx2 = pos2[0];  
  347.                 float fy1 = pos1[1];  
  348.                 float fy2 = pos2[1];  
  349.                 float dy = fy2 - fy1;  
  350.                 float dx = fx2 - fx1;  
  351.                   
  352.                 for (int y = 0; y <= HEIGHT; ++y)  
  353.                 {  
  354.                     // x = y * dx / dy  
  355.                     float fy = y * h / HEIGHT;  
  356.                     float fx = fy * dx / dy;  
  357.                       
  358.                     index = y * (WIDTH + 1) + WIDTH - x;  
  359.                       
  360.                     mVerts[index * 2 + 0] = fx + fx1;  
  361.                     mVerts[index * 2 + 1] = fy + fy1;  
  362.                 }  
  363.             }  
  364.         }  
  365.     }  
  366. }  


Mesh类的实现


[java] view plain copy
  1. /* 
  2.  * System: CoreLib 
  3.  * @version     1.00 
  4.  *  
  5.  * Copyright (C) 2010, LZT Corporation. 
  6.  *  
  7.  */  
  8.   
  9. package com.nj1s.lib.mesh;  
  10.   
  11. public abstract class Mesh  
  12. {  
  13.     protected int WIDTH      = 40;  
  14.     protected int HEIGHT     = 40;  
  15.     protected int mBmpWidth   = -1;  
  16.     protected int mBmpHeight  = -1;  
  17.     protected final float[] mVerts;  
  18.       
  19.     public Mesh(int width, int height)  
  20.     {  
  21.         WIDTH  = width;  
  22.         HEIGHT = height;  
  23.         mVerts  = new float[(WIDTH + 1) * (HEIGHT + 1) * 2];  
  24.     }  
  25.       
  26.     public float[] getVertices()  
  27.     {  
  28.         return mVerts;  
  29.     }  
  30.       
  31.     public int getWidth()  
  32.     {  
  33.         return WIDTH;  
  34.     }  
  35.       
  36.     public int getHeight()  
  37.     {  
  38.         return HEIGHT;  
  39.     }  
  40.       
  41.     public static void setXY(float[] array, int index, float x, float y)  
  42.     {  
  43.         array[index*2 + 0] = x;  
  44.         array[index*2 + 1] = y;  
  45.     }  
  46.       
  47.     public void setBitmapSize(int w, int h)  
  48.     {  
  49.         mBmpWidth  = w;  
  50.         mBmpHeight = h;  
  51.     }  
  52.       
  53.     public abstract void buildPaths(float endX, float endY);  
  54.       
  55.     public abstract void buildMeshes(int index);  
  56.   
  57.     public void buildMeshes(float w, float h)  
  58.     {  
  59.         int index = 0;  
  60.           
  61.         for (int y = 0; y <= HEIGHT; ++y)  
  62.         {  
  63.             float fy = y * h / HEIGHT;  
  64.             for (int x = 0; x <= WIDTH; ++x)  
  65.             {  
  66.                 float fx = x * w / WIDTH;  
  67.                   
  68.                 setXY(mVerts, index, fx, fy);  
  69.                   
  70.                 index += 1;  
  71.             }  
  72.         }  
  73.     }  
  74. }  


转自  http://blog.csdn.net/leehong2005/article/details/9127095
0 0
原创粉丝点击