Android中按鈕的水波紋點選效果的實現
阿新 • • 發佈:2019-01-03
關於按鈕水波紋的點選效果,這個是我在http://blog.csdn.net/singwhatiwanna/article/details/42614953這篇文章讀到的。寫得真心不錯,我只是站在巨人的肩上而已。
我加了一些註釋,以至於我們更好的理解這篇不錯的文章
下面是主要原始碼:
注:public class RevealLayout extends LinearLayout implements Runnable { private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // 被點選的控制元件的寬高 private int mTargetWidth; private int mTargetHeight; // 在被選中的控制元件長寬中的最大值和最小值 private int mMinBetweenWidthAndHeight; private int mMaxBetweenWidthAndHeight; // mMaxRadius為繪製的水波紋圓圈最大的半徑 private int mMaxRevealRadius; // mRevealRadiusGap為每次重新繪製半徑增加的值 private int mRevealRadiusGap; // mRevealRadius為初始的數值 private int mRevealRadius = 0; // 使用者點選處的座標 private float mCenterX; private float mCenterY; // 獲取自定義控制元件MyRevealLayout 在螢幕上的位置 private int[] mLocationInScreen = new int[2]; // 是否執行動畫 private boolean mShouldDoAnimation = false; // 是否被按下 private boolean mIsPressed = false; // 重新繪製的時間 單位毫秒 private int INVALIDATE_DURATION = 50; // mTouchTarget指的是使用者點選的那個view private View mTouchTarget; // 鬆手的事件分發執行緒 private DispatchUpTouchEventRunnable mDispatchUpTouchEventRunnable = new DispatchUpTouchEventRunnable(); public RevealLayout(Context context) { super(context); init(); } public RevealLayout(Context context, AttributeSet attrs) { super(context, attrs); init(); } @TargetApi(Build.VERSION_CODES.HONEYCOMB) public RevealLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { setWillNotDraw(false); mPaint.setColor(getResources().getColor(R.color.reveal_color)); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); this.getLocationOnScreen(mLocationInScreen); } private void initParametersForChild(MotionEvent event, View view) { mCenterX = event.getX(); mCenterY = event.getY(); mTargetWidth = view.getMeasuredWidth(); mTargetHeight = view.getMeasuredHeight(); mMinBetweenWidthAndHeight = Math.min(mTargetWidth, mTargetHeight); mMaxBetweenWidthAndHeight = Math.max(mTargetWidth, mTargetHeight); mRevealRadius = 0; mShouldDoAnimation = true; mIsPressed = true; mRevealRadiusGap = mMinBetweenWidthAndHeight / 8; int[] location = new int[2]; view.getLocationOnScreen(location); int left = location[0] - mLocationInScreen[0]; int transformedCenterX = (int) mCenterX - left; mMaxRevealRadius = Math.max(transformedCenterX, mTargetWidth - transformedCenterX); } /** * 繪製水波 */ protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (!mShouldDoAnimation || mTargetWidth <= 0 || mTouchTarget == null) { return; } if (mRevealRadius > mMinBetweenWidthAndHeight / 2) { mRevealRadius += mRevealRadiusGap * 4; } else { mRevealRadius += mRevealRadiusGap; } this.getLocationOnScreen(mLocationInScreen); int[] location = new int[2]; mTouchTarget.getLocationOnScreen(location); // 獲得要繪製View的left, top, right, bottom值 int left = location[0] - mLocationInScreen[0]; int top = location[1] - mLocationInScreen[1]; int right = left + mTouchTarget.getMeasuredWidth(); int bottom = top + mTouchTarget.getMeasuredHeight(); canvas.save(); canvas.clipRect(left, top, right, bottom); canvas.drawCircle(mCenterX, mCenterY, mRevealRadius, mPaint); canvas.restore(); if (mRevealRadius <= mMaxRevealRadius) { postInvalidateDelayed(INVALIDATE_DURATION, left, top, right, bottom); } else if (!mIsPressed) { mShouldDoAnimation = false; postInvalidateDelayed(INVALIDATE_DURATION, left, top, right, bottom); } } @Override public boolean dispatchTouchEvent(MotionEvent event) { // 獲得相對於螢幕的座標 int x = (int) event.getRawX(); int y = (int) event.getRawY(); // 獲得動作 int action = event.getAction(); if (action == MotionEvent.ACTION_DOWN) { View touchTarget = getTouchTarget(this, x, y); if (touchTarget != null && touchTarget.isClickable() && touchTarget.isEnabled()) { mTouchTarget = touchTarget; initParametersForChild(event, touchTarget); // 重新整理介面,延遲執行時間 postInvalidateDelayed(INVALIDATE_DURATION); } } else if (action == MotionEvent.ACTION_UP) { mIsPressed = false; postInvalidateDelayed(INVALIDATE_DURATION); mDispatchUpTouchEventRunnable.event = event; postDelayed(mDispatchUpTouchEventRunnable, 40); return true; } else if (action == MotionEvent.ACTION_CANCEL) { mIsPressed = false; postInvalidateDelayed(INVALIDATE_DURATION); } return super.dispatchTouchEvent(event); } /** * 遍歷view樹找到使用者所點選的那個view * * @param view * @param x * @param y * @return */ private View getTouchTarget(View view, int x, int y) { View target = null; ArrayList<View> TouchableViews = view.getTouchables(); for (View child : TouchableViews) { if (isTouchPointInView(child, x, y)) { target = child; break; } } return target; } /** * 判斷事件的xy是否落在view的上下左右四個角之內 * * @param view * @param x * @param y * @return */ private boolean isTouchPointInView(View view, int x, int y) { int[] location = new int[2]; view.getLocationOnScreen(location); int left = location[0]; int top = location[1]; int right = left + view.getMeasuredWidth(); int bottom = top + view.getMeasuredHeight(); if (view.isClickable() && y >= top && y <= bottom && x >= left && x <= right) { return true; } return false; } // 使用程式碼主動去呼叫控制元件的點選事件(模擬人手去觸控控制元件) @Override public boolean performClick() { postDelayed(this, 400); return true; } @Override public void run() { super.performClick(); } private class DispatchUpTouchEventRunnable implements Runnable { public MotionEvent event; @Override public void run() { if (mTouchTarget == null || !mTouchTarget.isEnabled()) { return; } if (isTouchPointInView(mTouchTarget, (int) event.getRawX(), (int) event.getRawY())) { // 使用程式碼主動去呼叫控制元件的點選事件(模擬人手去觸控控制元件) mTouchTarget.performClick(); } } }; }
在Android中實現view的更新有中方法,一種是invalidate,另一種是postInvalidate,
其中:前者是在UI執行緒自身中使用,而後者在非UI執行緒中使用。
當invalidate方法被呼叫的時候,會告訴系統,當前的view發生改變,應該儘可能快的來進行重繪。並且執行在UI執行緒
當postInvalidate方法被呼叫的時候,將會發送訊息到主執行緒,當主執行緒的訊息佇列輪詢到當前訊息的時候,這個方法才會被呼叫,
重新整理介面並不能保證馬上重新整理。只是儘可能快的進行重新整理。尤其是在postInvalidate方法中,這種情況會出現機率更大。
當我們想要使用這種效果時候,只需要將我的控制元件包裹在這個佈局下面即可,如下:
<包名.RevealLayout android:id="@+id/layout1" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <Button android:id="@+id/button1" android:layout_width="200dp" android:layout_height="40dp" android:enabled="true" android:text="Button" /> </包名.RevealLayout>
這樣我們就可以實現按鈕的水波紋效果了