Android實現(xiàn)波浪效果 - WaveView

效果圖

先上效果圖


screenshot.gif

實現(xiàn)

WaveView的屬性

WaveView的屬性
  • Wate Level(水位) - 波浪靜止時水面距離底部的高度
  • Amplitude(振幅) - 波浪垂直振動時偏離水面的最大距離
  • Wave Length(波長) - 一個完整的波浪的水平長度
  • Wave Shift(偏移) - 波浪相對于初始位置的水平偏移

實現(xiàn)思路

設(shè)想我們有一個畫好波形的圖片宝冕,那么我們只需要用這張圖片填充(X軸方向重復(fù)扁掸,Y軸方向延伸)整個View撼班,然后水平移動圖片,就可以得到波浪效果了宋列。

所以要做的事很簡單:繪制一個波形圖,填充到View里,移動波形圖。

1. 繪制初始波形

private void createShader() {
    ...

    Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);

    // Draw default waves into the bitmap
    // y=Asin(ωx+φ)+h
    float waveX1 = 0;
    final float wave2Shift = mDefaultWaveLength / 4;
    final float endX = getWidth();
    final float endY = getHeight();

    ...

    while (waveX1 < endX) {
        double wx = waveX1 * mDefaultAngularFrequency;
        int startY = (int) (mDefaultWaterLevel + mDefaultAmplitude * Math.sin(wx));

        // draw bottom wave with the alpha 40
        canvas.drawLine(waveX1, startY, waveX1, endY, wavePaint1);
        // draw top wave with the alpha 60
        float waveX2 = (waveX1 + wave2Shift) % endX;
        canvas.drawLine(waveX2, startY, waveX2, endY, wavePaint2);

        waveX1++;
    }

    // use the bitamp to create the shader
    mWaveShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
    mViewPaint.setShader(mWaveShader);
}

首先一個長寬恰等于WaveView的Bitmap:Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888)政模。

在Bitmap中使用默認的屬性繪制出初始波形。初始波形的屬性:Wate Level(水位)為WaveView高度的1/2沿腰;Amplitude(振幅)為WaveView高度的1/20览徒;Wave Length(波長)等于WaveView的寬度。

繪制好的初始波形是下面這個樣子:

初始波形

代碼第 9 ~ 27 行進行初始波形的繪制颂龙。波形由wave1和wave2兩個波組成习蓬,wave2就是wave1向左偏移1/4的wave length纽什,所以不需要重復(fù)計算。

最后把這個Bitmap設(shè)置成為Paint的Shader躲叼。設(shè)置Shader相當于設(shè)定畫筆的形狀芦缰,使用設(shè)置了Shader的Paint繪制圖形時,實際上是在使用Bitmap填充繪制的區(qū)域枫慷。X軸的填充方式為TileMode.REPEAT让蕾,即重復(fù)填充;Y軸的填充方式為TileMode.CLAMP或听,即使用邊緣的色值延伸填充探孝。

2. 調(diào)整Bitmap的大小并填充到WaveView

有了初始波形,當WaveView的屬性改變時誉裆,只需要對初始波形進行相應(yīng)的拉伸/壓縮和位移就可以得到用戶想要的波形顿颅。

// sacle shader according to mWaveLengthRatio and mAmplitudeRatio
// this decides the size(mWaveLengthRatio for width, mAmplitudeRatio for height) of waves
mShaderMatrix.setScale(
        mWaveLengthRatio / DEFAULT_WAVE_LENGTH_RATIO,
        mAmplitudeRatio / DEFAULT_AMPLITUDE_RATIO,
        0,
        mDefaultWaterLevel);
// translate shader according to mWaveShiftRatio and mWaterLevelRatio this decides the start position(mWaveShiftRatio for x, mWaterLevelRatio for 
// this decides the start position(mWaveShiftRatio for x, mWaterLevelRatio for y) of waves
mShaderMatrix.postTranslate(
        mWaveShiftRatio * getWidth(),
        (DEFAULT_WATER_LEVEL_RATIO - mWaterLevelRatio) * getHeight());

// assign matrix to invalidate the shader
mWaveShader.setLocalMatrix(mShaderMatrix);

float radius = getWidth() / 2f
        - (mBorderPaint == null ? 0f : mBorderPaint.getStrokeWidth());
canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, radius, mViewPaint);

代碼 3 ~ 6 行對Shader進行拉伸/壓縮,10 ~ 12 行對Shader進行水平/豎直平移足丢。

代碼 17 ~ 19 行用Shader填充成想要的形狀粱腻。

3. 動畫

// horizontal animation.
// wave waves infinitely.
ObjectAnimator waveShiftAnim = ObjectAnimator.ofFloat(
        mWaveView, "waveShiftRatio", 0f, 1f);
waveShiftAnim.setRepeatCount(ValueAnimator.INFINITE);
waveShiftAnim.setDuration(1000);
waveShiftAnim.setInterpolator(new LinearInterpolator());
animators.add(waveShiftAnim);

// vertical animation.
// water level increases from 0 to center of WaveView
ObjectAnimator waterLevelAnim = ObjectAnimator.ofFloat(
        mWaveView, "waterLevelRatio", 0f, 0.5f);
waterLevelAnim.setDuration(10000);
waterLevelAnim.setInterpolator(new DecelerateInterpolator());
animators.add(waterLevelAnim);

// amplitude animation.
// wave grows big then grows small, repeatedly
ObjectAnimator amplitudeAnim = ObjectAnimator.ofFloat(
        mWaveView, "amplitudeRatio", 0f, 0.05f);
amplitudeAnim.setRepeatCount(ValueAnimator.INFINITE);
amplitudeAnim.setRepeatMode(ValueAnimator.REVERSE);
amplitudeAnim.setDuration(5000);
amplitudeAnim.setInterpolator(new LinearInterpolator());
animators.add(amplitudeAnim);

代碼 3 ~ 8 行讓波形一直向右移動,效果就是波形一直在波動斩跌。

代碼 12 ~ 16 行讓水位從0逐漸漲到WaveView高度的一半绍些。

代碼 20 ~ 26 行波浪的大小從大變小,再從小變大耀鸦。


源代碼

代碼在github:WaveView


打個廣告

美團平臺及酒旅事業(yè)群招人啦柬批,歡迎加入我們!
我可以幫忙內(nèi)推揭糕,簡歷請發(fā)到我郵箱gelitenight@gmail.com

【美團網(wǎng)】高級Android開發(fā)工程師
工作內(nèi)容: 負責美團酒店萝快、旅游產(chǎn)品 Android 客戶端的設(shè)計、開發(fā)與改進著角。

  1. 3年以上工作經(jīng)驗揪漩,2年以上Android開發(fā)經(jīng)驗;
  2. 熟悉Android系統(tǒng)吏口,熟悉Android軟件的開發(fā)奄容、測試、分發(fā)流程产徊;
  3. 良好的編程風格昂勒,扎實的編程基礎(chǔ)和數(shù)據(jù)結(jié)構(gòu)算法基礎(chǔ);
  4. 熟悉移動網(wǎng)絡(luò)的特性舟铜,對網(wǎng)絡(luò)編程和常用網(wǎng)絡(luò)協(xié)議有較深刻理解和經(jīng)驗戈盈;
  5. 有一定的架構(gòu)設(shè)計能力,良好的編碼能力,編寫文檔能力塘娶;
  6. 熱愛互聯(lián)網(wǎng)和新技術(shù)归斤,具有極強的快速學(xué)習(xí)能力;
  7. 有以下特征優(yōu)先考慮:
    • 有開源作品或技術(shù)博客(需原創(chuàng)技術(shù)文章)刁岸;
    • 熟悉Socket編程脏里。

北京、上海虹曙、廈門迫横、成都都有職位,更多職位請見職位列表酝碳。


本文遵循“署名-非商業(yè)性使用-相同方式共享”的創(chuàng)作共同協(xié)議矾踱,歡迎轉(zhuǎn)載,轉(zhuǎn)載時請注明作者和出處击敌。
作者: gelitenight
出處: http://gelitenight.github.io/wave-view/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末介返,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子沃斤,更是在濱河造成了極大的恐慌,老刑警劉巖刃宵,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件衡瓶,死亡現(xiàn)場離奇詭異,居然都是意外死亡牲证,警方通過查閱死者的電腦和手機哮针,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來坦袍,“玉大人,你說我怎么就攤上這事∽嗜幔” “怎么了绘证?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長奠宜。 經(jīng)常有香客問我包颁,道長,這世上最難降的妖魔是什么压真? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任娩嚼,我火速辦了婚禮,結(jié)果婚禮上滴肿,老公的妹妹穿的比我還像新娘岳悟。我一直安慰自己,他們只是感情好泼差,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布贵少。 她就那樣靜靜地躺著和屎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪春瞬。 梳的紋絲不亂的頭發(fā)上柴信,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機與錄音宽气,去河邊找鬼随常。 笑死,一個胖子當著我的面吹牛萄涯,可吹牛的內(nèi)容都是我干的绪氛。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼涝影,長吁一口氣:“原來是場噩夢啊……” “哼枣察!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起燃逻,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤序目,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后伯襟,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體猿涨,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年姆怪,在試婚紗的時候發(fā)現(xiàn)自己被綠了叛赚。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡稽揭,死狀恐怖俺附,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情溪掀,我是刑警寧澤事镣,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站膨桥,受9級特大地震影響蛮浑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜只嚣,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一沮稚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧册舞,春花似錦蕴掏、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽挽荡。三九已至,卻和暖如春即供,著一層夾襖步出監(jiān)牢的瞬間定拟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工逗嫡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留青自,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓驱证,卻偏偏與公主長得像延窜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子抹锄,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容