Android 吸入动画效果详解

来源:互联网 发布:b超数据看男孩女孩 编辑:程序博客网 时间:2024/04/20 05:28

转自:http://blog.csdn.net/leehong2005/article/details/9127095

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.     }  

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 小孩不喜欢吃蔬菜怎么办 小孩不愿练钢琴怎么办 孩子不喜欢幼儿园老师怎么办 孩子不主动思考怎么办 我初一不想读书怎么办 初中孩子不爱学习怎么办 初一就不想读书怎么办 孩子懒得写作业怎么办 初中学生讨厌学习怎么办? 小学不写作业怎么办 幼儿园小朋友不愿意上学怎么办 幼儿园孩子不愿意上学怎么办 宝宝不愿意上幼儿园怎么办 孩子不爱上幼儿园怎么办 宝宝不爱上幼儿园怎么办 宝宝不爱去幼儿园怎么办 幼儿园宝宝不爱写字怎么办 小孩贪玩不爱学习怎么办 老师对幼儿不好怎么办 小孩不愿写作业怎么办 小孩怕老师厌学怎么办 幼儿园怕老师厌学怎么办 孩子很怕老师怎么办 孩子怕外教老师怎么办 家长打老师老师怎么办 被学生骂了怎么办 老师经常打孩子怎么办 老师每天打孩子怎么办 小孩不想去幼儿园怎么办 宝宝不想上幼儿园怎么办 小孩不爱学英语怎么办 孩子抵触学英语怎么办 初三了英语不好怎么办 孩子入园焦虑怎么办 家长总是说孩子怎么办 幼师打了小朋友怎么办 幼儿园老师欺负孩子怎么办 被老师冷落怎么办豆瓣 不满老师对孩子怎么办 老师总找茬孩子 怎么办 老师总针对孩子怎么办