Android自定義View——擴散波浪按鈕
阿新 • • 發佈:2019-01-04
前言
勞動節快樂!!!O(∩_∩)O(本文寫於2017年勞動節假期的最後一天。)
雖然現在不是一個值得慶祝的時間,因為美好的白天已經過去了,再過不久大家就要回到公司或者課堂了。/(ㄒoㄒ)/~~
想做一個隨即匹配按鈕,同學建議是做一個像波浪一樣向外擴散的按鈕,同學在網上找了一個效果圖,看上去挺簡單的,就自己做了一個,下面是效果圖:
我覺得用在只需要一個大按鈕的介面裡面,是挺合適的。
下面就來分享一下思路與程式碼。
分析動畫
- 中間一個圓是不動的,裡面有一個文字區域
- 外面的擴散的圓圈透明度是變化的,從裡面向外面越來越透明。
- 最開始是隻有一條波紋的,慢慢才變成了3條。
思路
- 先畫中間的不動的圓圈。
- 繪製文字區域。
- 繪製周圍的圓圈,但是半徑要慢慢變大,如果有波紋超出了區域,那麼繪製到裡面,形成一條新的波紋。
- 不斷重複上述過程。
程式碼
為了節省空間,我省去了初始化程式碼、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,還有很多需要完善的地方,就當做是簡單記錄一下程式碼與思路吧。