天天看點

android 點選效果圖,Android MaterialDesign之水波點選效果的幾種實作方法

添加一個自定義的布局類 MaterialLayout.class

public class MaterialLayout extends RelativeLayout {

private static final int DEFAULT_RADIUS = 10;

private static final int DEFAULT_FRAME_RATE = 10;

private static final int DEFAULT_DURATION = 200;

private static final int DEFAULT_ALPHA = 255;

private static final float DEFAULT_SCALE = 0.8f;

private static final int DEFAULT_ALPHA_STEP = 5;

private int mFrameRate = DEFAULT_FRAME_RATE;

private int mDuration = DEFAULT_DURATION;

private Paint mPaint = new Paint();

private Point mCenterPoint = null;

private RectF mTargetRectf;

private int mRadius = DEFAULT_RADIUS;

private int mMaxRadius = DEFAULT_RADIUS;

private int mCirclelColor = Color.LTGRAY;

private int mRadiusStep = 1;

private int mBackupAlpha;

private float mCircleScale = DEFAULT_SCALE;

private int mColorAlpha = DEFAULT_ALPHA;

private int mAlphaStep = DEFAULT_ALPHA_STEP;

private View mTargetView;

public MaterialLayout(Context context) {

this(context, null);

}

public MaterialLayout(Context context, AttributeSet attrs) {

super(context, attrs);

init(context, attrs);

}

public MaterialLayout(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

init(context, attrs);

}

private void init(Context context, AttributeSet attrs) {

if (isInEditMode()) {

return;

}

if (attrs != null) {

initTypedArray(context, attrs);

}

initPaint();

this.setWillNotDraw(false);

this.setDrawingCacheEnabled(true);

}

private void initTypedArray(Context context, AttributeSet attrs) {

final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MaterialLayout);

mCirclelColor = typedArray.getColor(R.styleable.MaterialLayout_mycolor, Color.LTGRAY);

mDuration = typedArray.getInteger(R.styleable.MaterialLayout_duration, DEFAULT_DURATION);

mFrameRate = typedArray.getInteger(R.styleable.MaterialLayout_framerate, DEFAULT_FRAME_RATE);

mColorAlpha = typedArray.getInteger(R.styleable.MaterialLayout_alpha, DEFAULT_ALPHA);

mCircleScale = typedArray.getFloat(R.styleable.MaterialLayout_scale, DEFAULT_SCALE);

typedArray.recycle();

}

private void initPaint() {

mPaint.setAntiAlias(true);

mPaint.setStyle(Paint.Style.FILL);

mPaint.setColor(mCirclelColor);

mPaint.setAlpha(mColorAlpha);

// 備份alpha屬性用于動畫完成時重置

mBackupAlpha = mColorAlpha;

}

private boolean isInFrame(View touchView, float x, float y) {

initViewRect(touchView);

return mTargetRectf.contains(x, y);

}

private void initViewRect(View touchView) {

int[] location = new int[2];

touchView.getLocationOnScreen(location);

// 視圖的區域

mTargetRectf = new RectF(location[0], location[1], location[0] + touchView.getWidth(),

location[1] + touchView.getHeight());

}

private void removeExtraHeight() {

int[] location = new int[2];

this.getLocationOnScreen(location);

// 減去兩個該布局的top,這個top值就是狀态欄的高度

mTargetRectf.top -= location[1];

mTargetRectf.bottom -= location[1];

// 計算中心點坐标

int centerHorizontal = (int) (mTargetRectf.left + mTargetRectf.right) / 2;

int centerVertical = (int) ((mTargetRectf.top + mTargetRectf.bottom) / 2);

// 擷取中心點

mCenterPoint = new Point(centerHorizontal, centerVertical);

}

private View findTargetView(ViewGroup viewGroup, float x, float y) {

int childCount = viewGroup.getChildCount();

// 疊代查找被點選的目标視圖

for (int i = 0; i < childCount; i++) {

View childView = viewGroup.getChildAt(i);

if (childView instanceof ViewGroup) {

return findTargetView((ViewGroup) childView, x, y);

} else if (isInFrame(childView, x, y)) { // 否則判斷該點是否在該View的frame内

return childView;

}

}

return null;

}

private boolean isAnimEnd() {

return mRadius >= mMaxRadius;

}

private void calculateMaxRadius(View view) {

// 取視圖的最長邊

int maxLength = Math.max(view.getWidth(), view.getHeight());

// 計算Ripple圓形的半徑

mMaxRadius = (int) ((maxLength / 2) * mCircleScale);

int redrawCount = mDuration / mFrameRate;

// 計算每次動畫半徑的增值

mRadiusStep = (mMaxRadius - DEFAULT_RADIUS) / redrawCount;

// 計算每次alpha遞減的值

mAlphaStep = (mColorAlpha - 100) / redrawCount;

}

private void deliveryTouchDownEvent(MotionEvent event) {

if (event.getAction() == MotionEvent.ACTION_DOWN) {

mTargetView = findTargetView(this, event.getRawX(), event.getRawY());

if (mTargetView != null) {

removeExtraHeight();

// 計算相關資料

calculateMaxRadius(mTargetView);

// 重繪視圖

invalidate();

}

}

}

@Override

public boolean onInterceptTouchEvent(MotionEvent event) {

deliveryTouchDownEvent(event);

return super.onInterceptTouchEvent(event);

}

@Override

protected void dispatchDraw(Canvas canvas) {

super.dispatchDraw(canvas);

// 繪制Circle

drawRippleIfNecessary(canvas);

}

private void drawRippleIfNecessary(Canvas canvas) {

if (isFoundTouchedSubView()) {

// 計算新的半徑和alpha值

mRadius += mRadiusStep;

mColorAlpha -= mAlphaStep;

// 裁剪一塊區域,這塊區域就是被點選的View的區域.通過clipRect來擷取這塊區域,使得繪制操作隻能在這個區域範圍内的進行,

// 即使繪制的内容大于這塊區域,那麼大于這塊區域的繪制内容将不可見. 這樣保證了背景層隻能繪制在被點選的視圖的區域

canvas.clipRect(mTargetRectf);

mPaint.setAlpha(mColorAlpha);

// 繪制背景圓形,也就是

canvas.drawCircle(mCenterPoint.x, mCenterPoint.y, mRadius, mPaint);

}

if (isAnimEnd()) {

reset();

} else {

invalidateDelayed();

}

}

private void invalidateDelayed() {

this.postDelayed(new Runnable() {

@Override

public void run() {

invalidate();

}

}, mFrameRate);

}

private boolean isFoundTouchedSubView() {

return mCenterPoint != null && mTargetView != null;

}

private void reset() {

mCenterPoint = null;

mTargetRectf = null;

mRadius = DEFAULT_RADIUS;

mColorAlpha = mBackupAlpha;

mTargetView = null;

invalidate();

}

}