先上圖,看看接下來我要向大家介紹的是個什么東西,例如以下圖:
接下來要介紹的就是怎樣實現上述圖中的波紋效果。這樣的效果假設大家沒有體驗過的話,能夠看看百度手機衛士或者360手機衛士,里面的按鈕點擊效果都是這樣的,另外Android 5.0以上的版本號也出現了這樣的效果。
不多說,以下聊聊詳細的怎么實現。
首先大家看到的是三個button,水波紋的出現給我們的錯覺是直接將波紋繪制在button上面的,可是這樣能做到嗎?首先button自己有background和src,假設把半透明的水波紋當作background或者src繪制到button上面,肯定是會損失button原有的樣式的。可能有朋友猜想那就把水波紋繪制在屏幕上唄,恭喜這位朋友答對了。至少我是這么干的,詳細思路就是,我們自己實現一個layout,在layout中捕捉事件,並對事件進行對應的處理,在down事件中尋找當前用戶點擊的是哪個view,找出view所在的矩形區域,將一個透明的圓環繪制到這個矩形區域,在up事件中,延時分發view的onclick事件。
1、自己實現一個layout:
2、重寫layout的dispatchTouchEvent方法,在down事件中找出被點擊的view。
public View findTargetView(float x, float y, View anchorView) {
ArrayList<View> touchablesView = anchorView.getTouchables();
View targetView = null;
for (View child : touchablesView) {
RectF rectF = getViewRectF(child);
if (rectF.contains(x, y) && child.isClickable()) {
// 這說明被點擊的view找到了
targetView = child;
break;
}
}
return targetView;
}
接着找出view所在的矩形區域,由於要將波紋繪制到該區域:
public RectF getViewRectF(View view) {
int[] location = new int[2];
view.getLocationOnScreen(location);
int childLeft = location[0];
int childTop = location[1];
int childRight = childLeft + view.getMeasuredWidth();
int childBottom = childTop + view.getMeasuredHeight();
return new RectF(childLeft, childTop, childRight, childBottom);
}
矩形區域找到之后,這個區域就是我們要繪制的博波紋所在地。上面也說過了,波紋事實上就是圓環,繪制圓的畫是須要知道圓心坐標和圓的半徑,圓心坐標肯定就是down時候的x和y了。可是半徑怎么計算合適?大家看到上面的圖知道假設view的寬度大於高度,點擊view的左下角或者右下角,那么半徑基本上就是等於view的寬度。點擊view的上部或者下部分,半徑就是在0和view的高度之間,詳細的計算方式看下圖:
那么依據上圖。半徑的計算方式就應該是:
float left = circleCenterX - targetTouchRectF.left;
float right = targetTouchRectF.right - circleCenterX;
float top = circleCenterY - targetTouchRectF.top;
float bottom = targetTouchRectF.bottom - circleCenterY;
// 計算出最大的值則為半徑
rawRadius = Math.max(bottom, Math.max(Math.max(left, right), top));
半徑算出來了。雖說圓心就是down時的x和y,可是有個地方還是須要注意的,在繪制圓環的時候提供的圓心坐標的x和y是在本文中是相對於layout的,所以在計算y的時候是須要進行一定處理的:
/** * 獲取圓環的中心坐標 */
public float[] getCircleCenterPostion(float x,float y){
int[] location = new int[2];
float[] mDownPositon = new float[2];
getLocationOnScreen(location );
mDownPositon[0] = x;
mDownPositon[1] = y -location[1];
return mDownPositon;
}
圓心坐標和半徑都計算好了,記下來就能夠繪制圓形波紋了。那么在哪里繪制這個波紋比較合適呢?有朋友立刻就說肯定是在onDraw方法里面繪制了,那么恭喜你在我看來你是答錯了,我們的layout中是非常有非常多childview的,而layout是個viewGroup,viewGroup在繪制的時候,是先繪制自身的背景。再繪制自身,再繪制childview,假設在onDraw中繪制波紋。也就意味者后面繪制出來的childView會將我們的波紋遮蓋,所以我們就應該等到childview繪制完成后再來繪制波紋,這樣能夠保證childview在最頂層。
重寫dispatchDraw方法:
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
/** * 繪制完子元素后開始繪制波紋 */
if (mTargetTouchView != null) {
RectF clipRectF = clipRectF(mTargetTouchView);
canvas.save();
// 為了不讓繪制的圓環超出所要繪制的范圍
canvas.clipRect(clipRectF);
if(drawedRadius < rawRadius){
drawedRadius += rawRadius / drawingRadiusDegrees;
canvas.drawCircle(mDownPositon[0], mDownPositon[1], drawedRadius, mHalfTransPaint);
postInvalidateDelayed(INVALID_DURATION);
}else{
canvas.drawCircle(mDownPositon[0], mDownPositon[1], rawRadius, mTransPaint);
post(delayedRunnable);
}
canvas.restore();
}
}
在分發繪制事件中大家能夠看到,波紋是一段一段的繪制。形例如以下圖:
而這一段段的波紋正是通過繪制一個個的圓環實現的,所以在沒繪制完成一個圓環的時候。都須要延時又一次繪制下一個圓環。
通過上面波紋效果基本上完成了,可是按鈕是有點擊事件的,像360手機衛士或者百度手機衛士等都是等波紋效果播放完成后才會響應點擊事件,所以我們這里也要對這個點擊事件進行延時響應。
在up事件中,記錄此次事件的event,而且返回true,表示消費此次的事件,然后再圓環繪制完成后。再利用找到的view去分發這個event:
if (ev.getAction() == MotionEvent.ACTION_UP) {
// 須要讓波紋繪制完成后再運行在up中運行的方法
// if(drawedRadius==0){
// return false;
// }
// long totalTime = (long) (INVALID_DURATION * (drawingRadiusDegrees+5));
// // 離波紋結束的時間
// long time = (long) (totalTime - drawedRadius*totalTime / rawRadius);
delayedRunnable.event = ev;
return true;
}
class postUpEventDelayed implements Runnable{
private MotionEvent event;
@Override
public void run() {
if(mTargetTouchView!=null && mTargetTouchView.isClickable()
&& event!=null){
mTargetTouchView.dispatchTouchEvent(event);// 分發
}
}
}
在dispatchDraw方法中。推斷假設繪制完成就post(delayedRunnable);運行childView的事件延時分發。
本文就寫到這里了。本文案例的實現同一時候也參考了http://m.blog.csdn.net/blog/singwhatiwanna/42614953。在此對博主表示感謝。
源代碼下載連接:Android button點擊水波紋效果