Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 吸入動畫效果詳解

Android 吸入動畫效果詳解

編輯:關於Android編程

1,背景

 
上圖演示了動畫的某幾幀,其中從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來構造mesh頂點就可以了。


構建兩條Path的代碼如下:

 

 

[java]
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); 

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時間可能是一個三角形。下圖說明了動畫過程中圖片的變化。

 

 

 

 


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]
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; 
        } 
    } 

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方法。最本質是要計算出一個頂點數組。

 

 

[java]
canvas.drawBitmapMesh(mBitmap, 
        mInhaleMesh.getWidth(), 
        mInhaleMesh.getHeight(), 
        mInhaleMesh.getVertices(), 
        0, null, 0, mPaint); 

canvas.drawBitmapMesh(mBitmap,
        mInhaleMesh.getWidth(),
        mInhaleMesh.getHeight(),
        mInhaleMesh.getVertices(),
        0, null, 0, mPaint);

 

5,如何實現動畫

[java] view plaincopyprint?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); 
            } 
        } 

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

[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); 
    } 

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

[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); 
            } 
        } 
    } 

/*
 * 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
[java]
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; 
                } 
            } 
        } 
    } 

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類的實現


[java]
/*
 * 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; 
            } 
        } 
    } 

/*
 * 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;
            }
        }
    }
}

 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved