RippleEffect(水波纹效果)的实现 |
您所在的位置:网站首页 › 水波纹元素分析 › RippleEffect(水波纹效果)的实现 |
Demo 下载: https://github.com/CodingForAndroid/RippleEffect 学习 谷歌 material design的交互设计、向新技术靠拢~
由于谷歌的只有在5.0+才可以有这个效果~ 而我们手头手机大部分还是4.+的、因此自己去实现这个效果、让各种版本的都可以用 无疑是挺好的、 这个Demo 实现了以下几点功能: ①:要实现水波纹效果,首先这个View 必须是可点击的,也就是说clickable :true 才可以触发 比如默认 Button 的Clickable =true,TextView, ImageView =false,但是可以手动设置 true。 ②:这个是一个布局、可以包裹 需要实现水波纹效果的 view,任何View 只要是可点击的,都可以包裹进来。 ③:可以保证,当手指按下在当前View 上,如果手滑动出了当前View,不会触发该 点击事件, 也就是只有手按下,和手抬起,都是同一个View 才触发 点击事件。 ④:这个效果,保证是 水波纹 结束以后,再去响应 View的点击事件 。 ⑤:一个布局 包裹这么多View,怎么区分每一个的点击事件呢? 答: 根据 每个View 的id 去响应不同的 事件。 大体就这么多,具体的可以自己尝试,与补充。这个效果参考了 singwhatiwanna 写的、 大体思路 可以去看他的博客、写的很详细、http://blog.csdn.net/singwhatiwanna/article/details/42614953、自己整理 ,为了积累加深一遍。 水波纹效果、就是一圈一圈 向外扩散的圆、实现思路 、就是 以手指触摸的位置为圆心、不断的改变圆的半径、向外画圆、以此达到效果。 而为每一个View 去实现这样一个效果、显然比较费精力,有这样一个布局,可以让其包裹的子View 实现 Ripple 效果,就比较符合我们的需求。 实现思想首先我们自定义一个layout,这里我们选取LinearLayout,至于原因,文章下面会进行分析。当用户点击一个可点击的元素时,比如button,我们需要得到用户点击的元素的信 息,包含:用户点击了哪个元素、用户点击的那个元素的宽、高、位置信息等。得到了button的信息后,我就可以确定水波纹的范围,然后通过layout进行重绘去绘制水波纹, 这样水波纹效果就实现了,当然,这只是大概步骤,中间还是有一些细节需要处理的。 实现过程实现过程主要是如下几个问题的解决: ①. 如何得知用户点击了哪个元素 ②. 如何取得被点击元素的信息 ③. 如何通过layout进行重绘绘制水波纹 ④. 如果延迟up事件的分发 如何得知用户点击了哪个元素这个问题好弄,为了得知用户点击了哪个元素(这个元素一般来说要是可点击的,否则是无意义的),我们要提前拦截所有的点击事件,于是,我们应该重写layout中的 dispatchTouchEvent方法,注意,这里不推荐用onInterceptTouchEvent,因为onInterceptTouchEvent不是一直会被回调的。 然后当用户点击的时候,会有一系列的down、move、up事件,我们要在down的时候来确定事件落在哪个元素上,down的元素就是用户点击的元素,当然为了严谨,我们还 要判断up的时候是否也落在同一个元素上面,因为,系统click事件的判断规则就是:down和up同时落在同一个可点击的元素上。 @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.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, 400); return true; } else if (action == MotionEvent.ACTION_CANCEL) { mIsPressed = false; postInvalidateDelayed(INVALIDATE_DURATION); } return super.dispatchTouchEvent(event); }通过上述代码,我们可以知道,当down的时候,我们取出点击事件的屏幕坐标,然后去遍历view树找到用户所点击的那个view,代码如下,就是判断事件的坐标是否落在view 的范围内,这个不再多说了,比较好理解。需要注意的是,事件的坐标我们不能用getX和getY,而要用getRawX和getRawY,二者的区别是:前者是相对于被点击view的坐 标,后者是相对于屏幕的坐标,而我们的目标view具体位于layout的哪一层我们无法知道,所以,必须用屏幕的绝对坐标来进行计算。而有了事件的坐标,再根据view在屏幕中 的绝对坐标,只要判断事件的xy是否落在view的上下左右四个角之内,就可以知道事件是否落在view上,从而取出用户所点击的那个view。 private View getTouchTarget(View view, int x, int y) { View target = null; ArrayList TouchableViews = view.getTouchables(); for (View child : TouchableViews) { if (isTouchPointInView(child, x, y)) { target = child; break; } } return target; } 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 = left && x |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |