Android中按钮的水波纹点击效果的实现
2015-02-08 11:53
246 查看
关于按钮水波纹的点击效果,这个是我在http://blog.csdn.net/singwhatiwanna/article/details/42614953这篇文章读到的。写得真心不错,我只是站在巨人的肩上而已。
我加了一些注释,以至于我们更好的理解这篇不错的文章
下面是主要源代码:
在Android中实现view的更新有中方法,一种是invalidate,另一种是postInvalidate,
其中:前者是在UI线程自身中使用,而后者在非UI线程中使用。
当invalidate方法被调用的时候,会告诉系统,当前的view发生改变,应该尽可能快的来进行重绘。并且运行在UI线程
当postInvalidate方法被调用的时候,将会发送消息到主线程,当主线程的消息队列轮询到当前消息的时候,这个方法才会被调用,
刷新界面并不能保证马上刷新。只是尽可能快的进行刷新。尤其是在postInvalidate方法中,这种情况会出现几率更大。
当我们想要使用这种效果时候,只需要将我的控件包裹在这个布局下面即可,如下:
这样我们就可以实现按钮的水波纹效果了
我加了一些注释,以至于我们更好的理解这篇不错的文章
下面是主要源代码:
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>
这样我们就可以实现按钮的水波纹效果了
相关文章推荐
- Android中按钮的水波纹点击效果的实现
- 【Android 界面效果3】Android_UI_点击按钮切换背景效果实现
- Android按钮点击效果的实现(selector)
- Android下实现win8的按钮点击效果
- android checkBox背景样式及用颜色值实现按钮点击效果
- android 按钮点击效果实现
- Android_UI_点击按钮切换背景效果实现
- Android_UI_点击按钮切换背景效果实现
- Android 中实现5.0按钮水波纹反馈效果
- android如何实现按钮的点击水波纹效果:
- Android_UI_点击按钮切换背景效果实现
- Android_UI_点击按钮切换背景效果实现
- Android 图片实现按钮点击切换效果
- 【Android 界面效果3】Android_UI_点击按钮切换背景效果实现
- Android_UI_点击按钮切换背景效果实现
- android仿IOS按钮点击效果,一张图片实现
- Android XML中实现按钮点击效果以及背景平铺
- Android_UI_点击按钮切换背景效果实现
- Android中图片实现按钮点击效果
- Android 图片实现按钮点击切换效果