前言
勞動節(jié)快樂Q汀!档礁!O(∩_∩)O
雖然現(xiàn)在不是一個值得慶祝的時間角钩,因為美好的白天已經(jīng)過去了,再過不久大家就要回到公司或者課堂了。/(ㄒoㄒ)/~~
想做一個隨即匹配按鈕递礼,同學建議是做一個像波浪一樣向外擴散的按鈕惨险,同學在網(wǎng)上找了一個效果圖,看上去挺簡單的脊髓,就自己做了一個辫愉,下面是效果圖:
效果圖
我覺得用在只需要一個大按鈕的界面里面,是挺合適的将硝。
下面就來分享一下思路與代碼恭朗。
分析動畫
- 中間一個圓是不動的,里面有一個文字區(qū)域
- 外面的擴散的圓圈透明度是變化的依疼,從里面向外面越來越透明痰腮。
- 最開始是只有一條波紋的,慢慢才變成了3條律罢。
思路
- 先畫中間的不動的圓圈膀值。
- 繪制文字區(qū)域。
- 繪制周圍的圓圈弟翘,但是半徑要慢慢變大虫腋,如果有波紋超出了區(qū)域,那么繪制到里面稀余,形成一條新的波紋悦冀。
- 不斷重復上述過程。
代碼
為了節(jié)省空間睛琳,我省去了初始化代碼盒蟆、onMeasure()函數(shù)的代碼、還有一些很容易懂的成員變量师骗,大家有需要可以在這里查看完整的源代碼历等。
/**
* 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;//顯示波浪的數(shù)量
private int mLineColor;//波浪線的顏色
private boolean isFirstTime = true;//是否是第一次開啟動畫
private OnClickListener mClickListener;//點擊事件監(jiān)聽器
private float mDownX,mDownY;//手指按下的坐標
//省略了前面兩個少參數(shù)的構(gòu)造函數(shù) 源碼里面是用前面兩個調(diào)用這個
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();//文字的區(qū)域
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轉(zhuǎn)像素
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;
}
//設置只有點擊圓圈才有點擊效果 點擊波浪不能觸發(fā)點擊效果
@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);//觸發(fā)點擊事件
}
break;
}
return true;
}
/**
* 檢查點x,y是否落在圓圈內(nèi)
* @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就相當于又出來了一條新的波浪。