如何让所有 View 都可以带上recyclerview点击效果的水波纹效果

3088人阅读
android 开发进阶(30)
Android5.0已经出了好久了,但是目前市场上的App好像没有多少用5.0上面的一些效果,依旧延续着之前的控件使用,但是既然新的东西已经出来了,就必定会淘汰旧的不好的,所以我们要与时俱进。其中Material Design真的很不错,其中有好多酷炫的动画,Android5.0的SwipeRefreshLayout会取代之前的PullToRefreshListView、RecyclerView,CardView也会取代ListView、MaterialEdittext也会取代Edittex以及一些FloatButton等等,以后会逐一介绍的。今天我们看一下RippleEffect水波纹点击效果,先上图:
大家可以看到按钮或者布局点击的时候会有水波涟漪的效果,很不错,用到你的app上一定会很高大上的。
下面我们分析一下源码,然后再看怎么使用,因为我觉得如果你光会用但是不了解怎么实现的你最多也就算个码农,所以我们要尝试着读懂源码,然后再尝试着自己定义view
首先在init()方法中初始化一些组件和styles,并设置相应的属性包括设置画布的抗锯齿标志、画图的实心空心、透明度颜色的设置。
然后创建手势,因为我们的点击有可能为长点击,我们用手势来做一些操作
接下来重写OnDraw()方法
这里面包括我们设置圆的颜色、半径大小,透明度(透明度是根据距离的增长而越来越透明的)
最重要的核心部分也就是创建动画了:
我们可以在这里面设置圆的最大半径,最大半径越大,我们得到的水波涟漪效果越快,越小,得到的水波涟漪效果越慢,也就是radiusMax /=1,这句代码。
那我们的动画怎么设置呢?当然用ScaleAnimation动画了
它的参数如下:
ScaleAnimation(float fromX, float toX, float fromY, float toY,int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)& &
参数说明: &&
float fromX 动画起始时 X坐标上的伸缩尺寸& &
float toX 动画结束时 X坐标上的伸缩尺寸& &
float fromY 动画起始时Y坐标上的伸缩尺寸& &
float toY 动画结束时Y坐标上的伸缩尺寸& &
int pivotXType 动画在X轴相对于物件位置类型& &
float pivotXValue 动画相对于物件的X坐标的开始位置& &
int pivotYType 动画在Y轴相对于物件位置类型& &
float pivotYValue 动画相对于物件的Y坐标的开始位置 &
好了,这样差不多就完成了我们的水波涟漪效果了。。。。
看一下怎么用吧?
如果你的开发IDE是Android Studio那么我们可以把github上的库集成到我们的项目中,
在我们的布局中引用RippleEffect就OK了
当然你也可以把库中的RippleView直接拷到我们的项目里面,还可以该里面的动画快慢速度等,注意也要把库里面的styles,attrs拷进来,放到自己的项目里面,就可以自己改一些配置了。
——————————————————————————————————————————————————————————————————————————————————————————————————————
下面再和大家说一下比较重要的一点吧,这个网上的demo都没有说,是我自己用的时候发现的
也就是我们的点击事件,这时候如果你还用普通的OnClickListener()是不行的,因为动画还没有结束,就直接startIntent()跳转界面了,如果你的界面没有finish()掉的话,返回的时候动画会继续执行完。
那么怎么破呢?
我们就需要给我们的RippleView设置监听事件而不是我们的控件设置监听事件了,因为我们的RippleView中有这样一个接口:
也就是动画完成的事件
这样我们就实现了动画完成之后才来实现界面跳转了
小伙伴们,快试一下吧。
当然我们的ListView的item点击也可以实现这样的效果,因为我们的RippleView中是支持Listview点击的
这里先提一下,以后会详细说怎么用的.....
github地址:
/traex/RippleEffect
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:224460次
积分:2821
积分:2821
排名:第10154名
原创:57篇
评论:252条
Github主页:
阅读:31137
阅读:52173
阅读:67765
阅读:21272
阅读:142874670人阅读
Android应用开发(178)
& & 转载请注明出处,本文来自。 我正在参加博客之星,,谢谢~ &&前言 自从Android 5.0问世以后,它的UI风格受到了大家普遍的赞美,简单、动感十足,但是由于工作比较忙,本人对于Android 5.0并没有太多的关注。前几天在知名博主任玉刚 ( &&) 帅哥的群中有同学问到实现Android 5.0 Material Design中的点击任意View时产生水波的效果,刚哥表示已经实现水波效果,但是需要过段时间才能开源出来。刚好本人在昨天写了声波支付的波纹效果,于是今天按照刚哥给出的实现思路弄了一下,于是也就有了今天的文章。可能效果不是很好,分享出来一是自我学习,二也是希望分享一下思路。
从目前的一些实现来看,主要有那么两个实现思路,第一种就是自定义View,比如继承Button,在Button的onDraw里面再动态绘制一层背景,然后改变背景的大小以及颜色,达到动态效果,这种实现使用比较局限,自定义一种类型的View,那么就只有这种View能够产生波纹效果;另一种是自定义布局,然后该布局中只有一个视图,也是同样的方法绘制背景,然后动画,但是也有局限性,就是一个布局中只能放一个视图,只有这个视图能够产生水波效果!
现实的情况是我们需要所有的视图在点击时都产生波纹效果,那么问题就来了,如何实现呢? 代码实现 其实大家的实现思路都是类似的,这是适用性、复杂度的问题。 我的实现思路是自定义一个布局,然后在用户触摸该布局时,通过该触摸点的坐标找到对应的子视图,找到该视图后我们在布局的dispatchDraw函数中裁剪一块区域,并且在这块区域中绘制波纹效果,使得背景图层的半径逐渐增大、透明度逐渐减小。这样点击某个视图时它的上面就产生了一个逐渐变大、颜色变浅的背景图层,不管是任何视图都会有这个动态效果!效果完成之后清除掉背景图层即可。 直接上代码吧。/*
* The MIT License (MIT)
* Copyright (c) 2015
( mr.simple )
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the &Software&), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED &AS IS&, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
package org.
import android.content.C
import android.content.res.TypedA
import android.graphics.C
import android.graphics.C
import android.graphics.P
import android.graphics.P
import android.graphics.RectF;
import android.util.AttributeS
import android.view.MotionE
import android.view.V
import android.view.ViewG
import android.widget.RelativeL
import org.simple.materiallayout.R;
* MaterialLayout是模拟Android 5.0中View被点击的波纹效果的布局,与其他的模拟Material
* Desigin效果的View不同,所有在MaterialLayout布局下的子视图被点击时都会产生波纹效果,而不是某个特定的View才会有这样的效果.
* @author mrsimple
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 =
* 视图的Rect
private RectF mTargetR
* 起始的圆形背景半径
private int mRadius = DEFAULT_RADIUS;
* 最大的半径
private int mMaxRadius = DEFAULT_RADIUS;
* 渐变的背景色
private int mCirclelColor = Color.LTGRAY;
* 每次重绘时半径的增幅
private int mRadiusStep = 1;
* 保存用户设置的alpha值
private int mBackupA
* 圆形半径针对于被点击视图的缩放比例,默认为0.8
private float mCircleScale = DEFAULT_SCALE;
* 颜色的alpha值, (0, 255)
private int mColorAlpha = DEFAULT_ALPHA;
* 每次动画Alpha的渐变递减值
private int mAlphaStep = DEFAULT_ALPHA_STEP;
private View mTargetV
* @param context
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()) {
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_color, 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 = mColorA
* 点击的某个坐标点是否在View的内部
* @param touchView
* @param x 被点击的x坐标
* @param y 被点击的y坐标
* @return 如果点击的坐标在该view内则返回true,否则返回false
private boolean isInFrame(View touchView, float x, float y) {
initViewRect(touchView);
return mTargetRectf.contains(x, y);
* 获取点中的区域,屏幕绝对坐标值,这个高度值也包含了状态栏和标题栏高度
* @param touchView
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 & childC 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 childV
private boolean isAnimEnd() {
return mRadius &= mMaxR
private void calculateMaxRadius(View view) {
// 取视图的最长边
int maxLength = Math.max(view.getWidth(), view.getHeight());
// 计算Ripple圆形的半径
mMaxRadius = (int) ((maxLength / 2) * mCircleScale);
int redrawCount = mDuration / mFrameR
// 计算每次动画半径的增值
mRadiusStep = (mMaxRadius - DEFAULT_RADIUS) / redrawC
// 计算每次alpha递减的值
mAlphaStep = (mColorAlpha - 100) / redrawC
* 处理ACTION_DOWN触摸事件, 注意这里获取的是Raw x, y,
* 即屏幕的绝对坐标,但是这个当屏幕中有状态栏和标题栏时就需要去掉这些高度,因此得到mTargetRectf后其高度需要减去该布局的top起点
* ,也就是标题栏和状态栏的总高度.
* @param event
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();
public boolean onInterceptTouchEvent(MotionEvent event) {
deliveryTouchDownEvent(event);
return super.onInterceptTouchEvent(event);
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
// 绘制Circle
drawRippleIfNecessary(canvas);
private void drawRippleIfNecessary(Canvas canvas) {
if (isFoundTouchedSubView()) {
// 计算新的半径和alpha值
mRadius += mRadiusS
mColorAlpha -= mAlphaS
// 裁剪一块区域,这块区域就是被点击的View的区域.通过clipRect来获取这块区域,使得绘制操作只能在这个区域范围内的进行,
// 即使绘制的内容大于这块区域,那么大于这块区域的绘制内容将不可见. 这样保证了背景层只能绘制在被点击的视图的区域
canvas.clipRect(mTargetRectf);
mPaint.setAlpha(mColorAlpha);
// 绘制背景圆形,也就是
canvas.drawCircle(mCenterPoint.x, mCenterPoint.y, mRadius, mPaint);
if (isAnimEnd()) {
invalidateDelayed();
* 发送重绘消息
private void invalidateDelayed() {
this.postDelayed(new Runnable() {
public void run() {
invalidate();
}, mFrameRate);
* 判断是否找到被点击的子视图
private boolean isFoundTouchedSubView() {
return mCenterPoint != null && mTargetView !=
private void reset() {
mCenterPoint =
mTargetRectf =
mRadius = DEFAULT_RADIUS;
mColorAlpha = mBackupA
mTargetView =
invalidate();
自定义的属性, attrs.xml&?xml version=&1.0& encoding=&utf-8&?&
&resources&
&declare-styleable name=&MaterialLayout&&
&attr name=&alpha& format=&integer& /&
&attr name=&alpha_step& format=&integer& /&
&attr name=&framerate& format=&integer& /&
&attr name=&duration& format=&integer& /&
&attr name=&color& format=&color& /&
&attr name=&scale& format=&float& /&
&/declare-styleable&
&/resources&使用示例 引用MaterialLayout工程或者将代码和attrs.xml拷贝到你的工程中,然后在你的布局xml中添加MaterialLayout布局,注意,不要忘了引用MaterialLayout自定义属性的命名空间,即下面的xmlns:ml这句。把com.example.materialdemo替换成你的包名就OK了。&org.simple.MaterialLayout xmlns:android=&/apk/res/android&
xmlns:ml=&/apk/res/com.example.materialdemo&
android:id=&@+id/layout&
android:layout_width=&match_parent&
android:layout_height=&match_parent&
android:layout_margin=&5dp&
android:background=&#f0f0f0&
android:gravity=&center&
ml:duration=&200&
ml:alpha=&200&
ml:scale=&1.2&
ml:color=&#FFD306& &
android:id=&@+id/my_button&
android:layout_width=&wrap_content&
android:layout_height=&wrap_content&
android:background=&#33CC99&
android:padding=&10dp&
android:text=&@string/click&
android:textSize=&20sp& /&
&ImageView
android:id=&@+id/my_imageview1&
android:layout_width=&100dp&
android:layout_height=&100dp&
android:layout_below=&@id/my_button&
android:layout_marginTop=&30dp&
android:background=&#33CC99&
android:contentDescription=&@string/app_name&
android:padding=&10dp&
android:src=&@drawable/ic_launcher& /&
&/org.simple.MaterialLayout&效果图
这个gif录得有点卡,真机上看起来还是不错的。大家可以到github上clone一份运行看看效果,如果觉得不行也别喷,给出你的github地址,本人也愿意学习您的优秀实现。在这里也期待刚哥早日开源出更好的实现。 github仓库
我正在参加博客之星,既然你已经看到这里,,谢谢~ &&
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:889986次
积分:12006
积分:12006
排名:第904名
原创:178篇
转载:120篇
译文:23篇
评论:930条
阅读:8160
阅读:24774
阅读:31110
阅读:14830
阅读:12221
文章:10篇
阅读:46560
Android 技术交流群2:
(1)(1)(5)(8)(1)(2)(1)(1)(10)(7)(8)(7)(8)(4)(1)(8)(5)(7)(1)(16)(14)(8)(2)(5)(3)(7)(11)(15)(7)(3)(6)(23)(19)(16)(3)(13)(8)(7)(2)(11)(10)(22)(4)10555人阅读
Android开发(1300)
Android UI(593)
开头先说说大家都知道的Material Design。
这里推荐的系列博客,介绍的很全面。
http://blog.csdn.net/a/article/category/2634371
Material Design:
Material Design是Google推出的一个全新的设计语言,它的特点就是拟物扁平化。
Material Design包含了很多内容,大致把它分为四部分:
主题和布局——
视图和阴影——
UI控件——
首先说一下主题:
&!-- res/values/styles.xml --&
&resources&
  &!-- your app's theme inherits from the Material theme --&
  &style name=&AppTheme& parent=&android:Theme.Material&&
    &!-- theme customizations --&
  &/style&
&/resources&
在最新的5.0中,google似乎不推荐使用Material Design主题了,而是由AppCompat代替。
&resources&
&!-- Base application theme. --&
&style name=&AppTheme& parent=&Theme.AppCompat.Light.DarkActionBar&&
&!-- Customize your theme here. --&
&item name=&colorPrimary&&@color/colorPrimary&/item&
&item name=&colorPrimaryDark&&@color/colorPrimaryDark&/item&
&item name=&colorAccent&&@color/colorAccent&/item&
&/resources&
自定义状态条和导航条:
material还允许你轻松的自定义状态条和导航条的颜色。
可以使用如下属性(参考下方图片):
android:statusBarColor,Window.setStatusBarColor
由于Material Theme只可以在Android L Developer Preview中使用。
所以在低版本使用的话就需要为其另设一套主题:
在老版本使用一套主题&res/values/styles.xml,在新版本使用Material主题res/values-v21/styles.xml.
系统自带点击事件的控件一般都具有默认的波纹效果,直接使用即可:
&RelativeLayout
android:layout_width=&match_parent&
android:layout_height=&wrap_content&
android:id=&@+id/myBtn&
android:layout_width=&match_parent&
android:layout_height=&wrap_content&
android:drawablePadding=&10dp&
android:gravity=&center_vertical&
android:paddingBottom=&15dip&
android:paddingLeft=&15dip&
android:paddingRight=&25dip&
android:paddingTop=&15dip&
android:text=&Click&
android:textColor=&@color/common_black_text&
android:textSize=&16sp& /&
&/RelativeLayout&
怎么为view添加点击波纹效果呢,先了解下面的东西。
触摸反馈:
在Android L5.0中加入了触摸反馈动画。
其中最明显,最具代表性的就是波纹动画,比如当点击按钮时会从点击的位置产生类似于波纹的扩散效果。
波纹效果(Ripple):
当你使用了Material主题后,波纹动画会自动应用在所有的控件上,我们当然可以来设置其属性来调整到我们需要的效果。
可以通过如下代码设置波纹的背景:
android:background=&?android:attr/selectableItemBackground&波纹有边界
android:background=&?android:attr/selectableItemBackgroundBorderless&波纹超出边界
使用效果如下:
B1是不设任何背景的按钮
B2设置了?android:attr/selectableItemBackground
B3设置了?android:attr/selectableItemBackgroundBorderless
我们也可以通过设置xml属性来调节动画颜色,从而可以适应不同的主题:
android:colorControlHighlight:设置波纹颜色
android:colorAccent:设置checkbox等控件的选中颜色
比如下面这个比较粉嫩的主题,就需要修改动画颜色来匹配(上面已经有介绍):
为view添加波纹效果:
&RelativeLayout
android:id=&@+id/user_info_layout&
android:layout_width=&match_parent&
android:layout_height=&wrap_content&
android:clickable=&true&
android:background=&?android:attr/selectableItemBackground&
android:layout_marginTop=&10dp&
android:paddingBottom=&15dip&
android:paddingTop=&15dip&&
android:id=&@+id/user_info_text&
android:layout_width=&wrap_content&
android:layout_height=&wrap_content&
android:drawablePadding=&10dp&
android:gravity=&center_vertical&
android:paddingLeft=&15dip&
android:paddingRight=&25dip&
android:text=&我的资料&
android:textSize=&16sp& /&
&ImageView
android:layout_width=&wrap_content&
android:layout_height=&wrap_content&
android:layout_alignParentRight=&true&
android:layout_centerInParent=&true&
android:contentDescription=&@null&
android:paddingRight=&15dip&
&/RelativeLayout&
为Textview添加波纹效果:
&LinearLayout
android:layout_width=&match_parent&
android:layout_height=&68dp&
android:weightSum=&4&
android:gravity=&center_vertical&&
android:id=&@+id/user_unpaid&
android:layout_width=&0dp&
android:layout_height=&wrap_content&
android:background=&?android:attr/selectableItemBackgroundBorderless&
android:drawableTop=&@mipmap/ic_user_paid&
android:drawablePadding=&5dp&
android:gravity=&center&
android:layout_weight=&1&
android:text=&待付款&
android:textSize=&12sp&
android:clickable=&true&/&
android:id=&@+id/user_paid&
android:layout_width=&0dp&
android:layout_height=&wrap_content&
android:background=&?android:attr/selectableItemBackgroundBorderless&
android:drawableTop=&@mipmap/ic_user_paid&
android:drawablePadding=&5dp&
android:layout_weight=&1&
android:gravity=&center&
android:text=&待发货&
android:textColor=&@color/common_black_text&
android:textSize=&12sp&
android:clickable=&true&/&
android:id=&@+id/user_unreceived&
android:layout_width=&0dp&
android:layout_height=&wrap_content&
android:background=&?android:attr/selectableItemBackgroundBorderless&
android:drawableTop=&@mipmap/ic_user_paid&
android:drawablePadding=&5dp&
android:gravity=&center&
android:layout_weight=&1&
android:text=&待收货&
android:textColor=&@color/common_black_text&
android:textSize=&12sp&
android:clickable=&true&/&
android:id=&@+id/user_completed&
android:layout_width=&0dp&
android:layout_height=&wrap_content&
android:background=&?android:attr/selectableItemBackgroundBorderless&
android:drawableTop=&@mipmap/ic_user_paid&
android:drawablePadding=&5dp&
android:gravity=&center&
android:layout_weight=&1&
android:text=&已完成&
android:textSize=&12sp&
android:clickable=&true&/&
&/LinearLayout&
这样就可以实现波纹效果啦!
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:7812990次
积分:65393
积分:65393
排名:第24名
原创:550篇
转载:1192篇
评论:2171条
如果您认为本博客不错,读后觉得有收获,不妨打赏赞助我一下,让我有动力继续写出高质量的博客。
赠人玫瑰,手有余香。分享技术,传递快乐。
QQ交流群:
推荐博客:
文章:29篇
阅读:74233
文章:72篇
阅读:280718

我要回帖

更多关于 cardview点击效果 的文章

 

随机推荐