1. 程式人生 > >Android自定義View——擴散波浪按鈕

Android自定義View——擴散波浪按鈕

前言

勞動節快樂!!!O(∩_∩)O(本文寫於2017年勞動節假期的最後一天。)
雖然現在不是一個值得慶祝的時間,因為美好的白天已經過去了,再過不久大家就要回到公司或者課堂了。/(ㄒoㄒ)/~~
想做一個隨即匹配按鈕,同學建議是做一個像波浪一樣向外擴散的按鈕,同學在網上找了一個效果圖,看上去挺簡單的,就自己做了一個,下面是效果圖:
效果圖
我覺得用在只需要一個大按鈕的介面裡面,是挺合適的。
下面就來分享一下思路與程式碼。

分析動畫

  1. 中間一個圓是不動的,裡面有一個文字區域
  2. 外面的擴散的圓圈透明度是變化的,從裡面向外面越來越透明。
  3. 最開始是隻有一條波紋的,慢慢才變成了3條。

思路

  1. 先畫中間的不動的圓圈。
  2. 繪製文字區域。
  3. 繪製周圍的圓圈,但是半徑要慢慢變大,如果有波紋超出了區域,那麼繪製到裡面,形成一條新的波紋。
  4. 不斷重複上述過程。

程式碼

為了節省空間,我省去了初始化程式碼、onMeasure()函式的程式碼、還有一些很容易懂的成員變數,大家有需要可以在這裡檢視完整的原始碼。

/**
 * Created by ICELEE on 5/1/2017.
 */

public class WaveButton extends View {

    private static final String TAG = "ICE";
    private Paint mPaint;
    private
int mRadius;//裡面圓圈的半徑 private int mWidth;//控制元件的寬度 private int mStrokeWidth;//波浪的寬度 private int mFillColor;//圓圈填充顏色 private int mCircleStrokeColor;//圓圈邊緣顏色 private int gapSize;//波浪之間的距離 private int firstRadius;//第一個圓圈的半徑 private int numberOfCircle;//顯示波浪的數量 private int mLineColor;//波浪線的顏色
private boolean isFirstTime = true;//是否是第一次開啟動畫 private OnClickListener mClickListener;//點選事件監聽器 private float mDownX,mDownY;//手指按下的座標 //省略了前面兩個少引數的建構函式 原始碼裡面是用前面兩個呼叫這個 public WaveButton(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context){ //省略了各個成員變數的初始化過程 } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //省略了測量的程式碼 在原始碼裡面有簡單的測量 } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.translate(mWidth/2,mWidth/2);//平移 //畫中間的圓 mPaint.setAlpha(255); mPaint.setColor(mFillColor); mPaint.setStyle(Paint.Style.FILL); canvas.drawCircle(0,0,mRadius,mPaint); //畫圓的邊 mPaint.setStrokeWidth(mStrokeWidth); mPaint.setColor(mCircleStrokeColor); mPaint.setStyle(Paint.Style.STROKE); canvas.drawCircle(0,0,mRadius,mPaint); //畫文字 Rect rect = new Rect();//文字的區域 mTextPaint.getTextBounds(mText,0,mText.length(),rect); int height = rect.height(); int width = rect.width(); canvas.drawText(mText,-width/2,height/2,mTextPaint); //畫周圍的波浪 firstRadius += 3;//每次重新整理半徑增加3畫素 firstRadius %= (mWidth/2);//控制在控制元件的範圍中 if(firstRadius<mRadius) isFirstTime =false; firstRadius = checkRadius(firstRadius);//檢查半徑的範圍 mPaint.setColor(mLineColor); mPaint.setStyle(Paint.Style.STROKE); //畫波浪 for (int i = 0; i < numberOfCircle; i++) { int radius = (firstRadius + i*gapSize ) % (mWidth/2); if(isFirstTime && radius>firstRadius) continue; radius = checkRadius(radius);//檢查半徑的範圍 //用半徑來計算透明度 半徑越大 越透明 double x = (mWidth/2 -radius)*1.0 /(mWidth/2 - mRadius); mPaint.setAlpha((int) (255*x)); canvas.drawCircle(0,0,radius,mPaint); } } //檢查波浪的半徑 如果小於圓圈,那麼加上圓圈的半徑 private int checkRadius(int radius) { if(radius<mRadius){ return radius+mRadius + gapSize; } return radius; } //dp轉畫素 public int dip2px(float dpValue) { final float scale = mContext.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } //不斷重繪 展示出波浪效果 public void startAnimation(){ Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { postInvalidate(); } },0,50); } @Override public void setOnClickListener(OnClickListener l) { mClickListener = l; } //設定只有點選圓圈才有點選效果 點選波浪不能觸發點選效果 @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: mDownX = event.getX(); mDownY = event.getY(); return checkIsInCircle((int)mDownX,(int)mDownY); case MotionEvent.ACTION_UP: int upX = (int) event.getX(),upY = (int) event.getY(); if(checkIsInCircle(upX,upY) && mClickListener!=null){ mClickListener.onClick(this);//觸發點選事件 } break; } return true; } /** * 檢查點x,y是否落在圓圈內 * @param x * @param y * @return */ private boolean checkIsInCircle(int x, int y){ int centerX = (getRight() + getLeft())/2; int centerY = (getTop() + getBottom())/2; return Math.pow(x-centerX,2)+Math.pow(y-centerY,2) < Math.pow(mRadius,2); } }

懂了思路其實挺容易就能寫出這個,主要是如何讓波浪動起來。這裡再畫個圖解釋解釋:

基本圖示
我們要把波浪的半徑radius模上mWidth/2,如果模後的值小於mRadius,也就是radius超出了邊界,那麼我們再加上mRadius就相當於又出來了一條新的波浪。

後記

這算是一個小demo,還有很多需要完善的地方,就當做是簡單記錄一下程式碼與思路吧。