上1篇中我們講了自定義ripple 水波紋效果,先來回顧1下效果吧!
看了以后感覺沒什么問題,我1開始也覺得很滿意了,那好,我們拿Android 5.0自帶的效果來對照1下
發現了不同的地方沒?點擊中間的時候是看不出甚么區分,但是點擊兩邊的時候,就很明顯了,我們自定義的效果,波紋向兩邊同速度的分散,所以就會出現,如果點擊點不在中心的時候,距離短的1邊波紋先到達,而距離長的1邊后到達,不能同時到達邊沿!而系統自帶的則不存在這類情況,所以這是1個優化點;另外一個優化點是:我們自定義的效果,在波紋全部覆蓋以后,按鈕的選中效果沒了。
有兩處需要優化的
1、實現不論是否點擊中間點都能實現波紋同步到達邊沿
2、當手指未松開時,選中效果不消失
實現:
第2點好實現,我們主要講1下第1點,
第1點我們視察系統的效果,看似兩邊速度不1致致使的,其實我們知道實現原理的話,很容易想到,它是不斷改變圓的圓心來實現,我們上1篇中的實現方法是圓形固定,就是在我們手指按下的位置,而不斷改變半徑來實現,
很明顯,這里也需要改變半徑來實現,我記得我們上1篇中半徑的最大值是需要計算,而這類效果是不需要計算的,由于其最大值是固定的,就是按鈕對角線的1半!
肯定了半徑的最大值,我們還需要肯定圓心X、Y的偏移量,相當于步長吧,其圓心從按下的點到按鈕正中間的時間因該是和半徑從0到最大值的時間保持1致,所以我們可以通過1下代碼來獲得圓心的偏移量和最大半徑。
/*最大半徑*/
mRadius = (float) Math.sqrt(mRect.width() / 2 * mRect.width() / 2 + mRect.height() / 2 * mRect.height() / 2);
/*半徑的偏移量*/
mStepRadius = mRadius / mCycle;
/*圓心X的偏移量*/
mStepOriginX = (mRect.width() / 2 - mInitX) / mCycle;
/*圓心Y的偏移量*/
mStepOriginY = (mRect.height() / 2 - mInitY) / mCycle;
全部實現代碼
package eyeclip.myapplication;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.LinearLayout;
/**
* Created by moon.zhong on 2015/4/27.
*/
public class RippleLinearLayout extends LinearLayout {
/*起始點*/
private int mInitX;
private int mInitY;
private float mCurrentX;
private float mCurrentY;
/*高度和寬度*/
private int mWidth;
private int mHeight;
/*繪制的半徑*/
private float mRadius;
private float mStepRadius;
private float mStepOriginX;
private float mStepOriginY;
private float mDrawRadius;
private boolean mDrawFinish;
private final int DURATION = 150;
private final int FREQUENCY = 10;
private float mCycle;
private final Rect mRect = new Rect();
private boolean mPressUp = false;
private Paint mRevealPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
public RippleLinearLayout(Context context) {
super(context);
initView(context);
}
public RippleLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public RippleLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
private void initView(Context context) {
mRevealPaint.setColor(0x25000000);
mCycle = DURATION / FREQUENCY;
final float density = getResources().getDisplayMetrics().density ;
mCycle = (density*mCycle);
mDrawFinish = true;
}
@Override
protected void onDraw(Canvas canvas) {
if (mDrawFinish) {
super.onDraw(canvas);
return;
}
canvas.drawColor(0x15000000);
super.onDraw(canvas);
if (mStepRadius == 0) {
return;
}
mDrawRadius = mDrawRadius + mStepRadius;
mCurrentX = mCurrentX + mStepOriginX;
mCurrentY = mCurrentY + mStepOriginY;
if (mDrawRadius > mRadius) {
mDrawRadius = 0;
canvas.drawCircle(mRect.width() / 2, mRect.height() / 2, mRadius, mRevealPaint);
mDrawFinish = true;
if (mPressUp)
invalidate();
return;
}
canvas.drawCircle(mCurrentX, mCurrentY, mDrawRadius, mRevealPaint);
ViewCompat.postInvalidateOnAnimation(this);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
}
private void updateDrawData() {
// int radiusLeftTop = (int) Math.sqrt((mRect.left - mInitX) * (mRect.left - mInitX) +
// (mRect.top - mInitY) * (mRect.top - mInitY));
// int radiusRightTop = (int) Math.sqrt((mRect.right - mInitX) * (mRect.right - mInitX) +
// (mRect.top - mInitY) * (mRect.top - mInitY));
// int radiusLeftBottom = (int) Math.sqrt((mRect.left - mInitX) * (mRect.left - mInitX) +
// (mRect.bottom - mInitY) * (mRect.bottom - mInitY));
// int radiusRightBottom = (int) Math.sqrt((mRect.right - mInitX) * (mRect.right - mInitX) +
// (mRect.bottom - mInitY) * (mRect.bottom - mInitY));
// mRadius = getMax(radiusLeftTop, radiusRightTop, radiusLeftBottom, radiusRightBottom);
/*最大半徑*/
mRadius = (float) Math.sqrt(mRect.width() / 2 * mRect.width() / 2 + mRect.height() / 2 * mRect.height() / 2);
;
/*半徑的偏移量*/
mStepRadius = mRadius / mCycle;
/*圓心X的偏移量*/
mStepOriginX = (mRect.width() / 2 - mInitX) / mCycle;
/*圓心Y的偏移量*/
mStepOriginY = (mRect.height() / 2 - mInitY) / mCycle;
mCurrentX = mInitX;
mCurrentY = mInitY;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
final int action = MotionEventCompat.getActionMasked(event);
switch (action) {
case MotionEvent.ACTION_DOWN: {
mPressUp = false;
mDrawFinish = false;
int index = MotionEventCompat.getActionIndex(event);
int eventId = MotionEventCompat.getPointerId(event, index);
if (eventId != -1) {
mInitX = (int) MotionEventCompat.getX(event, index);
mInitY = (int) MotionEventCompat.getY(event, index);
updateDrawData();
invalidate();
}
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mStepRadius = (int) (5 * mStepRadius);
mStepOriginX = (int) (5 * mStepOriginX);
mStepOriginY = (int) (5 * mStepOriginY);
mPressUp = true;
invalidate();
break;
}
return super.onTouchEvent(event);
}
private int getMax(int... radius) {
if (radius.length == 0) {
return 0;
}
int max = radius[0];
for (int m : radius) {
if (m > max) {
max = m;
}
}
return max;
}
@Override
public boolean performClick() {
postDelayed(new Runnable() {
@Override
public void run() {
RippleLinearLayout.super.performClick();
}
}, 150);
return true;
}
}
效果圖對照
這篇主要是對上1篇的內容進行優化,固然你覺得不優化也行!
demo下載
上一篇 選擇排序