1. 程式人生 > >Android 自定義控制元件實戰——水流波動效果的實現WaveView

Android 自定義控制元件實戰——水流波動效果的實現WaveView

   水流波動的波形都是三角波,曲線是正餘弦曲線,但是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:

  1. package com.jingchen.waveview;  
  2. import java.util.ArrayList;  
  3. import java.util.List;  
  4. import java.util.Timer;  
  5. import java.util.TimerTask;  
  6. import android.content.Context;  
  7. import android.graphics.Canvas;  
  8. import android.graphics.Color;  
  9. import android.graphics.Paint;  
  10. import android.graphics.Paint.Align;  
  11. import android.graphics.Paint.Style;  
  12. import android.graphics.Region.Op;  
  13. import android.graphics.Path;  
  14. import android.graphics.RectF;  
  15. import android.os.Handler;  
  16. import android.os.Message;  
  17. import android.util.AttributeSet;  
  18. import android.view.View;  
  19. /** 
  20.  * 水流波動控制元件 
  21.  *  
  22.  * @author chenjing 
  23.  *  
  24.  */
  25. publicclass WaveView extends View  
  26. {  
  27.     privateint mViewWidth;  
  28.     privateint mViewHeight;  
  29.     /** 
  30.      * 水位線 
  31.      */
  32.     privatefloat mLevelLine;  
  33.     /** 
  34.      * 波浪起伏幅度 
  35.      */
  36.     privatefloat mWaveHeight = 80;  
  37.     /** 
  38.      * 波長 
  39.      */
  40.     privatefloat mWaveWidth = 200;  
  41.     /** 
  42.      * 被隱藏的最左邊的波形 
  43.      */
  44.     privatefloat mLeftSide;  
  45.     privatefloat mMoveLen;  
  46.     /** 
  47.      * 水波平移速度 
  48.      */
  49.     publicstaticfinalfloat SPEED = 1.7f;  
  50.     private List<Point> mPointsList;  
  51.     private Paint mPaint;  
  52.     private Paint mTextPaint;  
  53.     private Path mWavePath;  
  54.     privateboolean isMeasured = false;  
  55.     private Timer timer;  
  56.     private MyTimerTask mTask;  
  57.     Handler updateHandler = new Handler()  
  58.     {  
  59.         @Override
  60.         publicvoid handleMessage(Message msg)  
  61.         {  
  62.             // 記錄平移總位移
  63.             mMoveLen += SPEED;  
  64.             // 水位上升
  65.             mLevelLine -= 0.1f;  
  66.             if (mLevelLine < 0)  
  67.                 mLevelLine = 0;  
  68.             mLeftSide += SPEED;  
  69.             // 波形平移
  70.             for (int i = 0; i < mPointsList.size(); i++)  
  71.             {  
  72.                 mPointsList.get(i).setX(mPointsList.get(i).getX() + SPEED);  
  73.                 switch (i % 4)  
  74.                 {  
  75.                 case0:  
  76.                 case2:  
  77.                     mPointsList.get(i).setY(mLevelLine);  
  78.                     break;  
  79.                 case1:  
  80.                     mPointsList.get(i).setY(mLevelLine + mWaveHeight);  
  81.                     break;  
  82.                 case3:  
  83.                     mPointsList.get(i).setY(mLevelLine - mWaveHeight);  
  84.                     break;  
  85.                 }  
  86.             }  
  87.             if (mMoveLen >= mWaveWidth)  
  88.             {  
  89.                 // 波形平移超過一個完整波形後復位
  90.                 mMoveLen = 0;  
  91.                 resetPoints();  
  92.             }  
  93.             invalidate();  
  94.         }  
  95.     };  
  96.     /** 
  97.      * 所有點的x座標都還原到初始狀態,也就是一個週期前的狀態 
  98. 相關推薦

    Android定義控制元件實戰——水流波動效果實現WaveView

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

    Android 定義控制元件實戰——水流波動效果實現WaveView

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

    android定義控制元件自動換行效果實現

         第一篇部落格裡面有介紹一篇關於自動換行實現諸多自定義控制元件跟各種效果的博文,但是礙於當初技術能力有限,寫的jar包裡的程式碼亂七八糟,在最近忙完了手頭的工作,不經意間翻看了之前的程式碼,真是慘不忍睹,隨決定重新封裝。重新編寫的android-custom-vg前

    Android定義控制元件實戰——滾動選擇器PickerView

      手機裡設定鬧鐘需要選擇時間,那個選擇時間的控制元件就是滾動選擇器,前幾天用手機刷了MIUI,發現自帶的那個時間選擇器效果挺好看的,於是就自己仿寫了一個,權當練手。先來看效果:                                              

    Android定義控制元件實戰——仿淘寶商品瀏覽介面

    用手機淘寶瀏覽商品詳情時,商品圖片是放在後面的,在第一個ScrollView滾動到最底下時會有提示,繼續拖動才能瀏覽圖片。仿照這個效果寫一個出來並不難,只要定義一個Layout管理兩個ScrollView就行了,當第一個ScrollView滑到底部時,再次向上滑動進入第二

    Android定義控制元件實戰——實現仿IOS下拉重新整理上拉載入 PullToRefreshLayout

             下拉重新整理控制元件,網上有很多版本,有自定義Layout佈局的,也有封裝控制元件的,各種實現方式的都有。但是很少有人告訴你具體如何實現的,今天我們就來一步步實現自己封裝的 PullToRefreshLayout 完美的解決下拉重新整理,上拉載入問題。  

    Android 定義控制元件:打造流佈局實現熱門搜尋標籤

    具體實現 1,自定義一個類繼承GridView /** * 自定義流佈局 * @author zhouyou */ public class ZFlowLayout extends ViewGroup{ // 儲存所有子View priva

    Android定義控制元件eBook翻書效果

    public class Book extends Activity {    /** Called when the activity is first created. */ eBook mBook;    public void onCreate(Bundle savedInstanceState) {

    Android 定義控制元件之打造流佈局實現熱門搜尋標籤

    最終效果 首先來看看效果圖: 其他地方很好實現,就是熱門搜尋有點麻煩,由於資料的不確定性,那麼像GridView明顯不能滿足了,這時候就只能自己來定義一個佈局了。 最終實現後的效果: 具體實現 1,自定義一個類繼承

    android定義控制元件系列教程----繼承ViewGroup實現帶阻力效果的可回彈的SrollView

    package com.example.scolview; import android.content.Context; import android.util.Log; import android.view.MotionEvent; import android.view.VelocityTracke

    我的新書《Android定義控制元件入門與實戰》出版啦

     前言:當你回首往事時,不以虛度年華而悔恨,不以碌碌無為而羞恥,那你就可以驕傲的跟自己講,你不負此生 念念碎: 兩年前,為了深入研究自定義控制元件,堅持在CSDN上,以每週或每兩週更新一篇部落格的頻率,出版了《Android自定義控制元件三部曲》系列,中間因為業務太忙

    Android定義控制元件專案實戰——桌布app-黃俊東-專題視訊課程

    Android自定義控制元件專案實戰——桌布app—4080人已學習 課程介紹        剔除繁雜的理論,注重實踐,深入淺出使用自定義控制元件的知識實現一個桌布類的app課程收益    每一個學員都能開發出該app講師介紹    黃俊東更多講師課程    Android研

    android-定義控制元件

    自定義控制元件兩種方式 1、繼承ViewGroup 例如:ViewGroup , LinearLayout, FrameLayout, RelativeLayout等。 2、繼承View 例如:View, TextView, ImageView, Button等。 View的測量

    Android 定義控制元件-星級評分

    在學習自定義控制元件時需要一些例子來練練手,本文這個控制元件就是在這種環境下產生的(可能有BUG); 這個控制元件設計的特點: 1,可以任意修改星星數量 2,可以星星大小會隨控制元件大小而縮小,在控制元件足夠大的情況可以任意設定星星大小 3,滑動監聽,根據滑動距離選擇星級 4,可

    [Android定義控制元件] Android定義控制元件

    轉載自: http://blog.163.com/[email protected]/blog/static/103242241201382210910473/ 開發自定義控制元件的步驟: 1、瞭解View的工作原理  2、

    Android 定義控制元件釋義

    由於經常在android的開發過過程中與控制元件打交道,所以有些android控制元件並不能滿足我們的需求, 進而需要自定義一 些為我們所用,但是本文只是一些名詞解析,具體實現可以網路搜尋獲得答案,在此只是個人的筆錄 1:自定義控制元件方式: 1.1:繼承view,button,

    Android定義控制元件】炫酷的底部導航欄

    https://github.com/WakeHao/NavBar 基本使用 使用這個控制元件,只需要簡單的幾部 引入該控制元件到你的專案中 compile 'com.chen.wakehao.library:bottom-navigation-bar:1.0.0'

    Android定義控制元件之區域性圖片放大鏡--BiggerView

    零、前言: 本文的知識點一覽 1.自定義控制元件及自定義屬性的寫法,你也將對onMesure有更深的認識 2.關於bitmap的簡單處理,及canvas區域裁剪 3.本文會實現兩個自定義控制元件:FitImageView(圖片自適應)和BiggerView(放大鏡),前者為後者作為鋪墊。 4.最後會

    Android學習(十)—— Android定義控制元件

    Android自定義控制元件 安卓在使用中大多數使用已有的一些控制元件,用法比較簡單,還有一部分是比較複雜的、使用者自己想的控制元件,這些就需要進行自定義控制元件,今天就來簡單說一下自定義控制元件。 1、繪製過程 建立一個類,繼承View類 onMeasure()方法,測量計算檢視的大小

    Android定義控制元件-不同形狀的ImageView

    實現分析: 依然是用到Paint的圖層混合模式, 1、畫一個你想要的形狀 || 圖片 2、修改Paint的圖層混合模式,畫圖片。 3、然後就能得到你想要的形狀的ImageView paint.setXfermode(new PorterDuffXfermode(Mode