Android WaveView實現水流波動效果_Android

來源:互聯網
上載者:User

   水流波動的波形都是三角波,曲線是正餘弦曲線,但是Android中沒有提供繪製正餘弦曲線的API,好在Path類有個繪製貝茲路徑的方法quadTo,繪製出來的是2階的貝茲路徑,要想實現波動效果,只能用它來繪製Path曲線。待會兒再講解2階的貝茲路徑是怎麼回事,先來看實現的效果:


這個波長比較短,還看不到起伏,只是蕩漾,把波長拉長再看一下:


已經可以看到起伏很明顯了,再拉長看一下:


這個的起伏感就比較強了。利用這個波動效果,可以用在繪製水位線的時候使用到,還可以做一個波動的進度條WaveUpProgress,比如這樣:


是不是很動感?

那這樣的波動效果是怎麼做的呢?前面講到的貝茲路徑到底是什麼呢?下面一一講解。想要用好貝茲路徑就得先理解它的運算式,為了形象描述,我從網上盜了些動圖。

首先看1階貝茲路徑的運算式:

                             

隨著t的變化,它實際是一條P0到P1的直線段:

                                

Android中Path的quadTo是3點的2階貝茲路徑,那麼2階的運算式是這樣的:

   

看起來很複雜,我把它拆分開來看:

        

然後再合并成這樣:

      

看到什麼了吧?如果看不出來再替換成這樣:

     

      

     

B0和B1分別是P0到P1和P1到P2的1階貝茲路徑。而2階貝茲路徑B就是B0到B1的1階貝茲路徑。顯然,它的動態圖表示出來就不難理解了:

                                          

紅色點的運動軌跡就是B的軌跡,這就是2階貝茲路徑了。當P1位於P0和P2的垂直平分線上時,B就是開口向上或向下的拋物線了。而在WaveView中就是用的開口向上和向下的拋物線類比水波。在Android裡用Path的方法,首先path.moveTo(P0),然後path.quadTo(P1, P2),canvas.drawPath(path, paint)曲線就出來了,如果想要繪製多個貝茲路徑就不斷的quadTo吧。

    講完貝茲路徑後就要開始講水波動的效果是怎麼來的了,首先要理解,機械波的傳輸就是通過介質的震動把波形往傳輸方向平移,每震動一個周期波形剛好平移一個波長,所有介質點又回到一個周期前的狀態。所以要實現水波動效果只需要把波形平移就可以了。

那麼WaveView的實現原理是這樣的:

    首先在View上根據View寬計算可以容納幾個完整波形,不夠一個的算一個,然後在View的不可見處預留一個完整的波形;然後波動開始的時候將所有點同時在x方向上移動相同的距離,這樣隱藏的波形就會被平移出來,當平移距離達到一個波長時,這時候將所有點的x座標又恢複到平移前的值,這樣就可以一個波形一個波形地往外傳輸。用草圖表示如下:


WaveView的原理在上圖很直觀的看出來了,P[2n+1],n>=0都是貝茲路徑的控制點,紅線為水位線。

知道原理以後可以看代碼了:

WaveView.java:

package com.jingchen.waveview;  import java.util.ArrayList; import java.util.List; import java.util.Timer; import java.util.TimerTask;  import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Paint.Style; import android.graphics.Region.Op; import android.graphics.Path; import android.graphics.RectF; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.view.View;  /**  * 水流波動控制項  *  * @author chenjing  *  */ public class WaveView extends View {   private int mViewWidth;  private int mViewHeight;   /**   * 水位線   */  private float mLevelLine;   /**   * 波浪起伏幅度   */  private float mWaveHeight = 80;  /**   * 波長   */  private float mWaveWidth = 200;  /**   * 被隱藏的最左邊的波形   */  private float mLeftSide;   private float mMoveLen;  /**   * 水波平移速度   */  public static final float SPEED = 1.7f;   private List<Point> mPointsList;  private Paint mPaint;  private Paint mTextPaint;  private Path mWavePath;  private boolean isMeasured = false;   private Timer timer;  private MyTimerTask mTask;  Handler updateHandler = new Handler()  {    @Override   public void handleMessage(Message msg)   {    // 記錄平移總位移    mMoveLen += SPEED;    // 水位上升    mLevelLine -= 0.1f;    if (mLevelLine < 0)     mLevelLine = 0;    mLeftSide += SPEED;    // 波形平移    for (int i = 0; i < mPointsList.size(); i++)    {     mPointsList.get(i).setX(mPointsList.get(i).getX() + SPEED);     switch (i % 4)     {     case 0:     case 2:      mPointsList.get(i).setY(mLevelLine);      break;     case 1:      mPointsList.get(i).setY(mLevelLine + mWaveHeight);      break;     case 3:      mPointsList.get(i).setY(mLevelLine - mWaveHeight);      break;     }    }    if (mMoveLen >= mWaveWidth)    {     // 波形平移超過一個完整波形後複位     mMoveLen = 0;     resetPoints();    }    invalidate();   }   };   /**   * 所有點的x座標都還原到初始狀態,也就是一個周期前的狀態   */  private void resetPoints()  {   mLeftSide = -mWaveWidth;   for (int i = 0; i < mPointsList.size(); i++)   {    mPointsList.get(i).setX(i * mWaveWidth / 4 - mWaveWidth);   }  }   public WaveView(Context context)  {   super(context);   init();  }   public WaveView(Context context, AttributeSet attrs)  {   super(context, attrs);   init();  }   public WaveView(Context context, AttributeSet attrs, int defStyle)  {   super(context, attrs, defStyle);   init();  }   private void init()  {   mPointsList = new ArrayList<Point>();   timer = new Timer();    mPaint = new Paint();   mPaint.setAntiAlias(true);   mPaint.setStyle(Style.FILL);   mPaint.setColor(Color.BLUE);    mTextPaint = new Paint();   mTextPaint.setColor(Color.WHITE);   mTextPaint.setTextAlign(Align.CENTER);   mTextPaint.setTextSize(30);    mWavePath = new Path();  }   @Override  public void onWindowFocusChanged(boolean hasWindowFocus)  {   super.onWindowFocusChanged(hasWindowFocus);   // 開始波動   start();  }   private void start()  {   if (mTask != null)   {    mTask.cancel();    mTask = null;   }   mTask = new MyTimerTask(updateHandler);   timer.schedule(mTask, 0, 10);  }   @Override  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  {   super.onMeasure(widthMeasureSpec, heightMeasureSpec);   if (!isMeasured)   {    isMeasured = true;    mViewHeight = getMeasuredHeight();    mViewWidth = getMeasuredWidth();    // 水位線從最底下開始上升    mLevelLine = mViewHeight;    // 根據View寬度計算波形峰值    mWaveHeight = mViewWidth / 2.5f;    // 波長等於四倍View寬度也就是View中只能看到四分之一個波形,這樣可以使起伏更明顯    mWaveWidth = mViewWidth * 4;    // 左邊隱藏的距離預留一個波形    mLeftSide = -mWaveWidth;    // 這裡計算在可見的View寬度中能容納幾個波形,注意n上取整    int n = (int) Math.round(mViewWidth / mWaveWidth + 0.5);    // n個波形需要4n+1個點,但是我們要預留一個波形在左邊隱藏地區,所以需要4n+5個點    for (int i = 0; i < (4 * n + 5); i++)    {     // 從P0開始初始化到P4n+4,總共4n+5個點     float x = i * mWaveWidth / 4 - mWaveWidth;     float y = 0;     switch (i % 4)     {     case 0:     case 2:      // 零點位於水位線上      y = mLevelLine;      break;     case 1:      // 往下波動的控制點      y = mLevelLine + mWaveHeight;      break;     case 3:      // 往上波動的控制點      y = mLevelLine - mWaveHeight;      break;     }     mPointsList.add(new Point(x, y));    }   }  }   @Override  protected void onDraw(Canvas canvas)  {    mWavePath.reset();   int i = 0;   mWavePath.moveTo(mPointsList.get(0).getX(), mPointsList.get(0).getY());   for (; i < mPointsList.size() - 2; i = i + 2)   {    mWavePath.quadTo(mPointsList.get(i + 1).getX(),      mPointsList.get(i + 1).getY(), mPointsList.get(i + 2)        .getX(), mPointsList.get(i + 2).getY());   }   mWavePath.lineTo(mPointsList.get(i).getX(), mViewHeight);   mWavePath.lineTo(mLeftSide, mViewHeight);   mWavePath.close();    // mPaint的Style是FILL,會填充整個Path地區   canvas.drawPath(mWavePath, mPaint);   // 繪製百分比   canvas.drawText("" + ((int) ((1 - mLevelLine / mViewHeight) * 100))     + "%", mViewWidth / 2, mLevelLine + mWaveHeight     + (mViewHeight - mLevelLine - mWaveHeight) / 2, mTextPaint);  }   class MyTimerTask extends TimerTask  {   Handler handler;    public MyTimerTask(Handler handler)   {    this.handler = handler;   }    @Override   public void run()   {    handler.sendMessage(handler.obtainMessage());   }   }   class Point  {   private float x;   private float y;    public float getX()   {    return x;   }    public void setX(float x)   {    this.x = x;   }    public float getY()   {    return y;   }    public void setY(float y)   {    this.y = y;   }    public Point(float x, float y)   {    this.x = x;    this.y = y;   }   }  } 

代碼中注釋寫的很多,不難看懂。
Demo的布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  android:layout_width="match_parent"  android:layout_height="match_parent"  android:background="#000000" >   <com.jingchen.waveview.WaveView   android:layout_width="100dp"   android:background="#ffffff"   android:layout_height="match_parent"   android:layout_centerInParent="true" />  </RelativeLayout> 

MainActivity的代碼:

package com.jingchen.waveview;  import android.os.Bundle; import android.app.Activity; import android.view.Menu;  public class MainActivity extends Activity {   @Override  protected void onCreate(Bundle savedInstanceState)  {   super.onCreate(savedInstanceState);   setContentView(R.layout.activity_main);  }   @Override  public boolean onCreateOptionsMenu(Menu menu)  {   getMenuInflater().inflate(R.menu.main, menu);   return true;  }  } 

代碼量很少,這樣就可以很簡單的做出水波效果啦。

源碼下載: 《Android實現水流波動效果》

以上就是本文的全部內容,希望對大家學習Android軟體編程有所協助。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.