Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android特效之水波紋的實現

Android特效之水波紋的實現

編輯:關於Android編程

前言

水波紋特效,想必大家或多或少見過,在我的印象中,大致有如下幾種:

     支付寶 "咻咻咻" 式

     流量球 "蕩漾" 式

     真實的水波紋效果,基於Bitmap處理式

話不多說,先來看看效果:

填充式水波紋,間距相等

非填充式水波紋,間距相等

非填充式水波紋,間距不斷變大

填充式水波紋,間距不斷變小

想必大家已經知道基本的原理了,就是用Canvas來畫嘛,但可不是簡單的畫哦,請往下看。

分析

這種類型的水波紋,其實無非就是畫圓而已,在給定的矩形中,一個個圓由最小半徑擴大到最大半徑,伴隨著透明度從1.0變為0.0。我們假定這種擴散是勻速的,則一個圓從創建(透明度為1.0)到消失(透明度為0.0)的時長就是定值,那麼某一時刻某一個圓的半徑以及透明度完全可以由擴散時間(當前時間 - 創建時間)決定。

實現

按照上面的分析,我們寫出以下Circle類來表示一個圓:

private class Circle {
 private long mCreateTime;

 public Circle() {
 this.mCreateTime = System.currentTimeMillis();
 }

 public int getAlpha() {
 float percent = (System.currentTimeMillis() - mCreateTime) * 1.0f / mDuration;
 return (int) ((1.0f - percent) * 255);
 }

 public float getCurrentRadius() {
 float percent = (System.currentTimeMillis() - mCreateTime) * 1.0f / mDuration;
 return mInitialRadius + percent * (mMaxRadius - mInitialRadius);
 }
}

自然而然,在WaveView中,要有一個List保存當前正在顯示的圓:

private List<Circle> mCircleList = new ArrayList<Circle>();

我們定義一個start方法,用來啟動擴散:

public void start() {
 if (!mIsRunning) {
 mIsRunning = true;
 mCreateCircle.run();
 }
}

private Runnable mCreateCircle = new Runnable() {
 @Override
 public void run() {
 if (mIsRunning) {
 newCircle();
 postDelayed(mCreateCircle, mSpeed); // 每隔mSpeed毫秒創建一個圓
 }
 }
};

private void newCircle() {
 long currentTime = System.currentTimeMillis();
 if (currentTime - mLastCreateTime < mSpeed) {
 return;
 }
 Circle circle = new Circle();
 mCircleList.add(circle);
 invalidate();
 mLastCreateTime = currentTime;
}

start方法只是簡單的創建了一個圓並添加到了mCircleList中,同時開啟了循環創建圓的Runnable,然後通知界面刷新,我們再看看onDraw方法:

protected void onDraw(Canvas canvas) {
 Iterator<Circle> iterator = mCircleList.iterator();
 while (iterator.hasNext()) {
 Circle circle = iterator.next();
 if (System.currentTimeMillis() - circle.mCreateTime < mDuration) {
 mPaint.setAlpha(circle.getAlpha());
 canvas.drawCircle(getWidth() / 2, getHeight() / 2, circle.getCurrentRadius(), mPaint);
 } else {
 iterator.remove();
 }
 }
 if (mCircleList.size() > 0) {
 postInvalidateDelayed(10);
 }
}

onDraw方法遍歷了每一個Circle,判斷Circle的擴散時間是否超過了設定的擴散時間,如果是則移除,如果不是,則計算Circle當前的透明度和半徑並繪制出來。我們添加了一個延時刷新來不斷重繪界面,以達到連續的波紋擴散效果。

現在運行程序,應該能看到圖2中的效果了,不過有點別扭,按常識,水波的間距是越來越大的,如何做到呢?

技巧

要讓水波紋的半徑非勻速變大,我們只能去修改Circle.getCurrentRadius()方法了。我們再次看看這個方法:

public float getCurrentRadius() {
 float percent = (System.currentTimeMillis() - mCreateTime) * 1.0f / mDuration;
 return mInitialRadius + percent * (mMaxRadius - mInitialRadius);
}

percent表示Circle當前擴散時間和總擴散時間的一個百分比,考慮到當前擴散時間超過總擴散時間時Circle會被移除,因此percent的實際區間為[0, 1],看到[0, 1],我不知道大家想到的是什麼,我首先想到的就是差值器(Interpolator),我們可以通過定義差值器來實現對Circle半徑變化的控制!

我們修改代碼:

private Interpolator mInterpolator = new LinearInterpolator();

public void setInterpolator(Interpolator interpolator) {
 mInterpolator = interpolator;
 if (mInterpolator == null) {
 mInterpolator = new LinearInterpolator();
 }
}

private class Circle {
 private long mCreateTime;

 public Circle() {
 this.mCreateTime = System.currentTimeMillis();
 }

 public int getAlpha() {
 float percent = (System.currentTimeMillis() - mCreateTime) * 1.0f / mDuration;
 return (int) ((1.0f - mInterpolator.getInterpolation(percent)) * 255);
 }

 public float getCurrentRadius() {
 float percent = (System.currentTimeMillis() - mCreateTime) * 1.0f / mDuration;
 return mInitialRadius + mInterpolator.getInterpolation(percent) * (mMaxRadius - mInitialRadius);
 }
}

這樣,外部使用WaveView時,只需調用setInterpolator()來定義不同的插值器即可實現不同的效果。

圖3效果的代碼:

mWaveView = (WaveView) findViewById(R.id.wave_view);
mWaveView.setDuration(5000);
mWaveView.setStyle(Paint.Style.STROKE);
mWaveView.setSpeed(400);
mWaveView.setColor(Color.parseColor("#ff0000"));
mWaveView.setInterpolator(new AccelerateInterpolator(1.2f));
mWaveView.start();

圖4效果的代碼:

mWaveView = (WaveView) findViewById(R.id.wave_view);
mWaveView.setDuration(5000);
mWaveView.setStyle(Paint.Style.FILL);
mWaveView.setColor(Color.parseColor("#ff0000"));
mWaveView.setInterpolator(new LinearOutSlowInInterpolator());
mWaveView.start();

附上WaveView的所有代碼:

/**
 * 水波紋特效
 * Created by hackware on 2016/6/17.
 */
public class WaveView extends View {
 private float mInitialRadius; // 初始波紋半徑
 private float mMaxRadiusRate = 0.85f; // 如果沒有設置mMaxRadius,可mMaxRadius = 最小長度 * mMaxRadiusRate;
 private float mMaxRadius; // 最大波紋半徑
 private long mDuration = 2000; // 一個波紋從創建到消失的持續時間
 private int mSpeed = 500; // 波紋的創建速度,每500ms創建一個
 private Interpolator mInterpolator = new LinearInterpolator();

 private List<Circle> mCircleList = new ArrayList<Circle>();
 private boolean mIsRunning;

 private boolean mMaxRadiusSet;

 private Paint mPaint;
 private long mLastCreateTime;

 private Runnable mCreateCircle = new Runnable() {
 @Override
 public void run() {
 if (mIsRunning) {
 newCircle();
 postDelayed(mCreateCircle, mSpeed);
 }
 }
 };

 public WaveView(Context context) {
 this(context, null);
 }

 public WaveView(Context context, AttributeSet attrs) {
 super(context, attrs);
 mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 setStyle(Paint.Style.FILL);
 }

 public void setStyle(Paint.Style style) {
 mPaint.setStyle(style);
 }

 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
 if (!mMaxRadiusSet) {
 mMaxRadius = Math.min(w, h) * mMaxRadiusRate / 2.0f;
 }
 }

 public void setMaxRadiusRate(float maxRadiusRate) {
 this.mMaxRadiusRate = maxRadiusRate;
 }

 public void setColor(int color) {
 mPaint.setColor(color);
 }

 /**
 * 開始
 */
 public void start() {
 if (!mIsRunning) {
 mIsRunning = true;
 mCreateCircle.run();
 }
 }

 /**
 * 停止
 */
 public void stop() {
 mIsRunning = false;
 }

 protected void onDraw(Canvas canvas) {
 Iterator<Circle> iterator = mCircleList.iterator();
 while (iterator.hasNext()) {
 Circle circle = iterator.next();
 if (System.currentTimeMillis() - circle.mCreateTime < mDuration) {
 mPaint.setAlpha(circle.getAlpha());
 canvas.drawCircle(getWidth() / 2, getHeight() / 2, circle.getCurrentRadius(), mPaint);
 } else {
 iterator.remove();
 }
 }
 if (mCircleList.size() > 0) {
 postInvalidateDelayed(10);
 }
 }

 public void setInitialRadius(float radius) {
 mInitialRadius = radius;
 }

 public void setDuration(long duration) {
 this.mDuration = duration;
 }

 public void setMaxRadius(float maxRadius) {
 this.mMaxRadius = maxRadius;
 mMaxRadiusSet = true;
 }

 public void setSpeed(int speed) {
 mSpeed = speed;
 }

 private void newCircle() {
 long currentTime = System.currentTimeMillis();
 if (currentTime - mLastCreateTime < mSpeed) {
 return;
 }
 Circle circle = new Circle();
 mCircleList.add(circle);
 invalidate();
 mLastCreateTime = currentTime;
 }

 private class Circle {
 private long mCreateTime;

 public Circle() {
 this.mCreateTime = System.currentTimeMillis();
 }

 public int getAlpha() {
 float percent = (System.currentTimeMillis() - mCreateTime) * 1.0f / mDuration;
 return (int) ((1.0f - mInterpolator.getInterpolation(percent)) * 255);
 }

 public float getCurrentRadius() {
 float percent = (System.currentTimeMillis() - mCreateTime) * 1.0f / mDuration;
 return mInitialRadius + mInterpolator.getInterpolation(percent) * (mMaxRadius - mInitialRadius);
 }
 }

 public void setInterpolator(Interpolator interpolator) {
 mInterpolator = interpolator;
 if (mInterpolator == null) {
 mInterpolator = new LinearInterpolator();
 }
 }
}

總結

想必大家看完這篇文章會覺得原來插值器還可以這麼用。其實,有些時候我們使用系統提供的API,往往過於局限其中,有時候換個思路,說不定會得到奇妙的效果。以上就是在Android實現水波紋特效的全部內容,希望對大家開發Android有所幫助。

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