安卓学习笔记---Android源码解析--Material Design之水波纹点击效果RippleEffect使用
2016-09-12 14:53
661 查看
今天老板跟我说,能不能加一个点击效果,要不然用户不知道他是否已经点击了,我说好的,刚开始我是使用的selector添加效果的,UI看了之后说好丑啊,能不能改的好看点啊,哎,,,果然好难啊,咋滴都得好看呐
,于是就另找方法吧
我记得以前看过那个点击之后显示水波纹的效果感觉很酷炫啊,我觉得我要是加上的话,感觉会不会好点,哈哈,说干就干。
这个好像是5.0添加的特性,当我看到这篇文章,眼前一亮:
转载文章地址:
http://blog.csdn.net//lyhhj/article/details/48505041
Android5.0已经出了好久了,但是目前市场上的App好像没有多少用5.0上面的一些效果,依旧延续着之前的控件使用,但是既然新的东西已经出来了,就必定会淘汰旧的不好的,所以我们要与时俱进。其中Material Design真的很不错,其中有好多酷炫的动画,Android5.0的SwipeRefreshLayout会取代之前的PullToRefreshListView、RecyclerView,CardView也会取代ListView、MaterialEdittext也会取代Edittex以及一些FloatButton等等,以后会逐一介绍的。今天我们看一下RippleEffect水波纹点击效果,先上图:
大家可以看到按钮或者布局点击的时候会有水波涟漪的效果,很不错,用到你的app上一定会很高大上的。
下面我们分析一下源码,然后再看怎么使用,因为我觉得如果你光会用但是不了解怎么实现的你最多也就算个码农,所以我们要尝试着读懂源码,然后再尝试着自己定义view
首先在init()方法中初始化一些组件和styles,并设置相应的属性包括设置画布的抗锯齿标志、画图的实心空心、透明度颜色的设置。
[java] view
plain copy
<span style="font-size:14px;"><span style="white-space: pre;"> </span>paint = new Paint();
paint.setAntiAlias(true); //设置画布抗锯齿标志
paint.setStyle(Paint.Style.FILL); //设置画图实心
paint.setColor(rippleColor); //设置画图颜色
paint.setAlpha(rippleAlpha); //设置透明度
this.setWillNotDraw(false); //设置将不绘画</span>
然后创建手势,因为我们的点击有可能为长点击,我们用手势来做一些操作
[java] view
plain copy
<span style="font-size:14px;"><span style="white-space:pre"> </span>/**
* 创建新的手势
*/
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public void onLongPress(MotionEvent event) {
super.onLongPress(event);
animateRipple(event); //创建动画
sendClickEvent(true); //发送长点击事件
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
this.setDrawingCacheEnabled(true); //更新cache,提高绘图速度
this.setClickable(true);</span>
接下来重写OnDraw()方法
[java] view
plain copy
<span style="font-size:14px;">@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (animationRunning) {
if (rippleDuration <= timer * frameRate) {
animationRunning = false;
timer = 0;
durationEmpty = -1;
timerEmpty = 0;
canvas.restore();
invalidate();
if (onCompletionListener != null) onCompletionListener.onComplete(this);
return;
} else
canvasHandler.postDelayed(runnable, frameRate);
if (timer == 0)
canvas.save();
canvas.drawCircle(x, y, (radiusMax * (((float) timer * frameRate) / rippleDuration)), paint); //画圆的半径
paint.setColor(Color.parseColor("#ffff4444")); //设置颜色
if (rippleType == 1 && originBitmap != null && (((float) timer * frameRate) / rippleDuration) > 0.4f) {
if (durationEmpty == -1)
durationEmpty = rippleDuration - timer * frameRate;
timerEmpty++;
//创建圆的bitmap
final Bitmap tmpBitmap = getCircleBitmap((int) ((radiusMax) * (((float) timerEmpty * frameRate) / (durationEmpty))));
canvas.drawBitmap(tmpBitmap, 0, 0, paint);
tmpBitmap.recycle();
}
paint.setColor(rippleColor);
if (rippleType == 1) {
if ((((float) timer * frameRate) / rippleDuration) > 2f)
paint.setAlpha((int) (rippleAlpha - ((rippleAlpha) * (((float) timerEmpty * frameRate) / (durationEmpty)))));
else
paint.setAlpha(rippleAlpha);
}
else
paint.setAlpha((int) (rippleAlpha - ((rippleAlpha) * (((float) timer * frameRate) / rippleDuration))));
timer++;
}
}</span>
这里面包括我们设置圆的颜色、半径大小,透明度(透明度是根据距离的增长而越来越透明的)
最重要的核心部分也就是创建动画了:
[java] view
plain copy
<span style="font-size:18px;"> </span><span style="font-size:14px;">/**
* Create Ripple animation centered at x, y
*
* @param x Horizontal position of the ripple center
* @param y Vertical position of the ripple center
*/
private void createAnimation(final float x, final float y) {
if (this.isEnabled() && !animationRunning) {
if (hasToZoom)
this.startAnimation(scaleAnimation);
radiusMax = Math.max(WIDTH, HEIGHT);
if (rippleType != 2)
radiusMax /= 1;
radiusMax -= ripplePadding;
if (isCentered || rippleType == 1) {
this.x = getMeasuredWidth() ;
this.y = getMeasuredHeight() ;
} else {
this.x = x;
this.y = y;
}
animationRunning = true;
if (rippleType == 1 && originBitmap == null)
originBitmap = getDrawingCache(true);
invalidate();
}
}</span>
我们可以在这里面设置圆的最大半径,最大半径越大,我们得到的水波涟漪效果越快,越小,得到的水波涟漪效果越慢,也就是radiusMax /=1,这句代码。
那我们的动画怎么设置呢?当然用ScaleAnimation动画了
[java] view
plain copy
<span style="font-size:14px;">@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
WIDTH = w;
HEIGHT = h;
scaleAnimation = new ScaleAnimation(2.0f, zoomScale, 2.0f, zoomScale, w / 2, h / 2);
scaleAnimation.setDuration(zoomDuration);
scaleAnimation.setRepeatMode(Animation.REVERSE);
scaleAnimation.setRepeatCount(1);
}</span>
它的参数如下:
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上的库集成到我们的项目中,
[java] view
plain copy
<span style="font-size:14px;">dependencies {
compile 'com.github.traex.rippleeffect:library:1.2.3'
} </span>
在我们的布局中引用RippleEffect就OK了
[java] view
plain copy
<span style="font-size:14px;"><com.Hankkin.library.RippleView
android:id="@+id/more"
android:layout_width="?android:actionBarSize"
android:layout_height="?android:actionBarSize"
android:layout_toLeftOf="@+id/more2"
android:layout_margin="5dp"
ripple:rv_centered="true">
<ImageView
android:layout_width="?android:actionBarSize"
android:layout_height="?android:actionBarSize"
android:src="@android:drawable/ic_menu_edit"
android:layout_centerInParent="true"
android:padding="10dp"
android:background="@android:color/holo_blue_dark"/>
</com.Hankkin.library.RippleView> </span>
当然你也可以把库中的RippleView直接拷到我们的项目里面,还可以该里面的动画快慢速度等,注意也要把库里面的styles,attrs拷进来,放到自己的项目里面,就可以自己改一些配置了。
——————————————————————————————————————————————————————————————————————————————————————————————————————
下面再和大家说一下比较重要的一点吧,这个网上的demo都没有说,是我自己用的时候发现的
也就是我们的点击事件,这时候如果你还用普通的OnClickListener()是不行的,因为动画还没有结束,就直接startIntent()跳转界面了,如果你的界面没有finish()掉的话,返回的时候动画会继续执行完。
那么怎么破呢?
我们就需要给我们的RippleView设置监听事件而不是我们的控件设置监听事件了,因为我们的RippleView中有这样一个接口:
[java] view
plain copy
<span style="font-size:14px;">public interface OnRippleCompleteListener {
void onComplete(RippleView rippleView);
} </span>
也就是动画完成的事件
[java] view
plain copy
<span style="font-size:14px;">RippleView view = (RippleView) findViewById(R.id.reView);
view.setOnRippleCompleteListener(new RippleView.OnRippleCompleteListener() {
@Override
public void onComplete(RippleView rippleView) {
Intent intent = new Intent(getApplicationContext(),HelloActivity.class);
startActivity(intent);
}
}); </span>
这样我们就实现了动画完成之后才来实现界面跳转了
小伙伴们,快试一下吧。
当然我们的ListView的item点击也可以实现这样的效果,因为我们的RippleView中是支持Listview点击的
[java] view
plain copy
/**
* Send a click event if parent view is a Listview instance
* 若为Listview发送点击事件
* @param isLongClick Is the event a long click ?
*/
private void sendClickEvent(final Boolean isLongClick) {
if (getParent() instanceof AdapterView) {
final AdapterView adapterView = (AdapterView) getParent();
final int position = adapterView.getPositionForView(this);
final long id = adapterView.getItemIdAtPosition(position);
if (isLongClick) {
if (adapterView.getOnItemLongClickListener() != null)
adapterView.getOnItemLongClickListener().onItemLongClick(adapterView, this, position, id);
} else {
if (adapterView.getOnItemClickListener() != null)
adapterView.getOnItemClickListener().onItemClick(adapterView, this, position, id);
}
}
}
这里先提一下,以后会详细说怎么用的.....
github地址:
https://github.com/traex/RippleEffect
博主讲的很透彻啊,果断使用到自己的项目中去了,看了看效果,感觉效果还不错呢
虽然博主已经提供资源地址,我自己还是上传到了自己的资源上,好方便下载吗,嘿嘿,感谢哦。
下载地址:
点击打开链接
,于是就另找方法吧
我记得以前看过那个点击之后显示水波纹的效果感觉很酷炫啊,我觉得我要是加上的话,感觉会不会好点,哈哈,说干就干。
这个好像是5.0添加的特性,当我看到这篇文章,眼前一亮:
转载文章地址:
http://blog.csdn.net//lyhhj/article/details/48505041
Android5.0已经出了好久了,但是目前市场上的App好像没有多少用5.0上面的一些效果,依旧延续着之前的控件使用,但是既然新的东西已经出来了,就必定会淘汰旧的不好的,所以我们要与时俱进。其中Material Design真的很不错,其中有好多酷炫的动画,Android5.0的SwipeRefreshLayout会取代之前的PullToRefreshListView、RecyclerView,CardView也会取代ListView、MaterialEdittext也会取代Edittex以及一些FloatButton等等,以后会逐一介绍的。今天我们看一下RippleEffect水波纹点击效果,先上图:
大家可以看到按钮或者布局点击的时候会有水波涟漪的效果,很不错,用到你的app上一定会很高大上的。
下面我们分析一下源码,然后再看怎么使用,因为我觉得如果你光会用但是不了解怎么实现的你最多也就算个码农,所以我们要尝试着读懂源码,然后再尝试着自己定义view
首先在init()方法中初始化一些组件和styles,并设置相应的属性包括设置画布的抗锯齿标志、画图的实心空心、透明度颜色的设置。
[java] view
plain copy
<span style="font-size:14px;"><span style="white-space: pre;"> </span>paint = new Paint();
paint.setAntiAlias(true); //设置画布抗锯齿标志
paint.setStyle(Paint.Style.FILL); //设置画图实心
paint.setColor(rippleColor); //设置画图颜色
paint.setAlpha(rippleAlpha); //设置透明度
this.setWillNotDraw(false); //设置将不绘画</span>
然后创建手势,因为我们的点击有可能为长点击,我们用手势来做一些操作
[java] view
plain copy
<span style="font-size:14px;"><span style="white-space:pre"> </span>/**
* 创建新的手势
*/
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public void onLongPress(MotionEvent event) {
super.onLongPress(event);
animateRipple(event); //创建动画
sendClickEvent(true); //发送长点击事件
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
});
this.setDrawingCacheEnabled(true); //更新cache,提高绘图速度
this.setClickable(true);</span>
接下来重写OnDraw()方法
[java] view
plain copy
<span style="font-size:14px;">@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (animationRunning) {
if (rippleDuration <= timer * frameRate) {
animationRunning = false;
timer = 0;
durationEmpty = -1;
timerEmpty = 0;
canvas.restore();
invalidate();
if (onCompletionListener != null) onCompletionListener.onComplete(this);
return;
} else
canvasHandler.postDelayed(runnable, frameRate);
if (timer == 0)
canvas.save();
canvas.drawCircle(x, y, (radiusMax * (((float) timer * frameRate) / rippleDuration)), paint); //画圆的半径
paint.setColor(Color.parseColor("#ffff4444")); //设置颜色
if (rippleType == 1 && originBitmap != null && (((float) timer * frameRate) / rippleDuration) > 0.4f) {
if (durationEmpty == -1)
durationEmpty = rippleDuration - timer * frameRate;
timerEmpty++;
//创建圆的bitmap
final Bitmap tmpBitmap = getCircleBitmap((int) ((radiusMax) * (((float) timerEmpty * frameRate) / (durationEmpty))));
canvas.drawBitmap(tmpBitmap, 0, 0, paint);
tmpBitmap.recycle();
}
paint.setColor(rippleColor);
if (rippleType == 1) {
if ((((float) timer * frameRate) / rippleDuration) > 2f)
paint.setAlpha((int) (rippleAlpha - ((rippleAlpha) * (((float) timerEmpty * frameRate) / (durationEmpty)))));
else
paint.setAlpha(rippleAlpha);
}
else
paint.setAlpha((int) (rippleAlpha - ((rippleAlpha) * (((float) timer * frameRate) / rippleDuration))));
timer++;
}
}</span>
这里面包括我们设置圆的颜色、半径大小,透明度(透明度是根据距离的增长而越来越透明的)
最重要的核心部分也就是创建动画了:
[java] view
plain copy
<span style="font-size:18px;"> </span><span style="font-size:14px;">/**
* Create Ripple animation centered at x, y
*
* @param x Horizontal position of the ripple center
* @param y Vertical position of the ripple center
*/
private void createAnimation(final float x, final float y) {
if (this.isEnabled() && !animationRunning) {
if (hasToZoom)
this.startAnimation(scaleAnimation);
radiusMax = Math.max(WIDTH, HEIGHT);
if (rippleType != 2)
radiusMax /= 1;
radiusMax -= ripplePadding;
if (isCentered || rippleType == 1) {
this.x = getMeasuredWidth() ;
this.y = getMeasuredHeight() ;
} else {
this.x = x;
this.y = y;
}
animationRunning = true;
if (rippleType == 1 && originBitmap == null)
originBitmap = getDrawingCache(true);
invalidate();
}
}</span>
我们可以在这里面设置圆的最大半径,最大半径越大,我们得到的水波涟漪效果越快,越小,得到的水波涟漪效果越慢,也就是radiusMax /=1,这句代码。
那我们的动画怎么设置呢?当然用ScaleAnimation动画了
[java] view
plain copy
<span style="font-size:14px;">@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
WIDTH = w;
HEIGHT = h;
scaleAnimation = new ScaleAnimation(2.0f, zoomScale, 2.0f, zoomScale, w / 2, h / 2);
scaleAnimation.setDuration(zoomDuration);
scaleAnimation.setRepeatMode(Animation.REVERSE);
scaleAnimation.setRepeatCount(1);
}</span>
它的参数如下:
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上的库集成到我们的项目中,
[java] view
plain copy
<span style="font-size:14px;">dependencies {
compile 'com.github.traex.rippleeffect:library:1.2.3'
} </span>
在我们的布局中引用RippleEffect就OK了
[java] view
plain copy
<span style="font-size:14px;"><com.Hankkin.library.RippleView
android:id="@+id/more"
android:layout_width="?android:actionBarSize"
android:layout_height="?android:actionBarSize"
android:layout_toLeftOf="@+id/more2"
android:layout_margin="5dp"
ripple:rv_centered="true">
<ImageView
android:layout_width="?android:actionBarSize"
android:layout_height="?android:actionBarSize"
android:src="@android:drawable/ic_menu_edit"
android:layout_centerInParent="true"
android:padding="10dp"
android:background="@android:color/holo_blue_dark"/>
</com.Hankkin.library.RippleView> </span>
当然你也可以把库中的RippleView直接拷到我们的项目里面,还可以该里面的动画快慢速度等,注意也要把库里面的styles,attrs拷进来,放到自己的项目里面,就可以自己改一些配置了。
——————————————————————————————————————————————————————————————————————————————————————————————————————
下面再和大家说一下比较重要的一点吧,这个网上的demo都没有说,是我自己用的时候发现的
也就是我们的点击事件,这时候如果你还用普通的OnClickListener()是不行的,因为动画还没有结束,就直接startIntent()跳转界面了,如果你的界面没有finish()掉的话,返回的时候动画会继续执行完。
那么怎么破呢?
我们就需要给我们的RippleView设置监听事件而不是我们的控件设置监听事件了,因为我们的RippleView中有这样一个接口:
[java] view
plain copy
<span style="font-size:14px;">public interface OnRippleCompleteListener {
void onComplete(RippleView rippleView);
} </span>
也就是动画完成的事件
[java] view
plain copy
<span style="font-size:14px;">RippleView view = (RippleView) findViewById(R.id.reView);
view.setOnRippleCompleteListener(new RippleView.OnRippleCompleteListener() {
@Override
public void onComplete(RippleView rippleView) {
Intent intent = new Intent(getApplicationContext(),HelloActivity.class);
startActivity(intent);
}
}); </span>
这样我们就实现了动画完成之后才来实现界面跳转了
小伙伴们,快试一下吧。
当然我们的ListView的item点击也可以实现这样的效果,因为我们的RippleView中是支持Listview点击的
[java] view
plain copy
/**
* Send a click event if parent view is a Listview instance
* 若为Listview发送点击事件
* @param isLongClick Is the event a long click ?
*/
private void sendClickEvent(final Boolean isLongClick) {
if (getParent() instanceof AdapterView) {
final AdapterView adapterView = (AdapterView) getParent();
final int position = adapterView.getPositionForView(this);
final long id = adapterView.getItemIdAtPosition(position);
if (isLongClick) {
if (adapterView.getOnItemLongClickListener() != null)
adapterView.getOnItemLongClickListener().onItemLongClick(adapterView, this, position, id);
} else {
if (adapterView.getOnItemClickListener() != null)
adapterView.getOnItemClickListener().onItemClick(adapterView, this, position, id);
}
}
}
这里先提一下,以后会详细说怎么用的.....
github地址:
https://github.com/traex/RippleEffect
博主讲的很透彻啊,果断使用到自己的项目中去了,看了看效果,感觉效果还不错呢
虽然博主已经提供资源地址,我自己还是上传到了自己的资源上,好方便下载吗,嘿嘿,感谢哦。
下载地址:
点击打开链接
相关文章推荐
- Android源码解析--Material Design之水波纹点击效果RippleEffect使用
- 安卓学习笔记---Android点击空白区域,隐藏输入法软键盘
- 【Android开源项目解析】背景有波浪效果的TextView——从Titanic项目学习BitmapShader的使用
- 安卓学习笔记---Android-PickerView实现 3D滚轮效果(时间选择器、省市区三级联动,单项选择效果)
- 安卓学习笔记---Android仿美团加载数据、小人奔跑进度动画对话框(以及顺丰快递员奔跑效果)
- 安卓学习笔记---使用AndroidStudio出现的问题错误(错误: -source 1.6 中不支持 diamond 运算符 (请使用 -source 7 或更高版本以启用 diamond 运算)
- 学习使用Material Design控件(四)Android实现标题栏自动缩放、放大效果
- Scala中隐式参数与隐式转换的联合使用实战详解及其在Spark中的应用源码解析之Scala学习笔记-51
- android菜鸟学习笔记25----与服务器端交互(二)解析服务端返回的json数据及使用一个开源组件请求服务端数据
- 安卓学习笔记---Android图片加载框架最全解析(一),Glide的基本用法
- soot学习笔记-2.使用soot解析Android apk.
- 安卓手机卫士学习笔记系列——两个Activity之间切换动画效果overridependingtransition的使用
- Android 笔记 imagebutton 点击水波纹背景效果
- 安卓学习笔记---Android反编译工具jadx的使用
- 安卓学习笔记---Android项目源码芝麻信用分快递综合评价雷达图
- 安卓学习笔记--- Android加载Gif动画android-gif-drawable的使用
- android material design之点击波纹效果(ripple)
- 《Android 源码设计模式解析与实战》学习笔记
- Android Material Design控件学习(三)——使用TextInputLayout实现酷市场登录效果
- 安卓学习笔记--- Android 6.0运行时权限的申请使用及EasyPermissions的使用