Material Design 中的轻触反馈可在用户与界面元素互动时,在接触点上提供即时视觉确认。按钮的默认轻触反馈动画使用新的 RippleDrawable 类,以波纹效果实现不同状态间的过渡。
RippleDrawable是android5新增加的一种Drawable,所以,根据需求需要自定义的时候,可以自定义RippleDrawable来实现想要的功能。
既然是Drawable,那么也可以在drawable目录下,定义xml文件。RippleDrawable在xml中对应的标签是,它只有两个属性——color、radius。需要注意的是,这些功能是API21才能使用,所以需要定义在
drawable-v21
的目录下。
触摸反馈的效果是一种涟漪的效果,大致可以分为2种:有边界和无边界。
先看下一个简单的例子:
<Button
android:id="@+id/btn1"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_margin="16dp"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:text="有边界" />
<Button
android:id="@+id/btn2"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_margin="16dp"
android:background="?android:selectableItemBackgroundBorderless"
android:text="无边界" />
效果:
上面的代码,定义2个Button,主要区别在于background属性:
selectableItemBackground
:涟漪效果发生在当前Button限制的区域;表示有界的波纹。
selectableItemBackgroundBorderless
:涟漪效果不会局限于Button本身的大小。表示越过视图边界的波纹,它将在该视图的最近父视图(具有非空背景)上进行绘制并由该父视图设定边界。(该属性必须定义在API21)
使用触摸反馈就是这么简单,设置一下属性就可以了。
但,平时使用的时候,可能会遇到一些问题,如:
下面,详细介绍其用法,并解决以上问题!!!
首先,先看看有边界的几种情况。先看看效果:
给Button设置只有单独的波纹背景,
如下代码:
在xml中设置:
android:background="?android:attr/selectableItemBackground"
一般多是在xml设置会比较方便,如果想在代码中设置,如下(但感觉复杂好多):
int[] attrs = new int[]{R.attr.selectableItemBackground};
TypedArray typedArray = obtainStyledAttributes(attrs);
int backgroundResource = typedArray.getResourceId(0, 0);
mBtn.setBackgroundResource(backgroundResource);
前面看到,如果单独设置一个系统自带的波纹,默认是灰色的。修改波纹的颜色,有2中方式:
第一种:全局改变的波纹颜色:
在style主题中,设置背景属性
- #ff0000
这里设置为红色,默认的波纹颜色就变了。
第二种:局部改变的波纹颜色:
首先,在drawable-v21文件新建一个 ripple_limit_color_bg_mask.xml
文件
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/colorAccent">
<item android:id="@android:id/mask" android:drawable="@color/colorPrimary"></item>
</ripple>
其次,在代码中将该drawable设置给视图:
mBtn.setBackgroundResource(R.drawable.ripple_limit_color_bg_mask);
关于 mask,后面介绍
那么当前视图的波纹颜色就会改变。(局部波纹颜色 优先级高于 全局波纹颜色)
波纹效果是系统自带的效果,但我们经常遇到一个问题是:
给Button设置单独背景色之后,波纹效果不见了。
简单来说,在设置背景时,将波纹效果覆盖了。现在,我们把它找回来。
网上有说,可以通过foreground
属性解决,将波纹效果设置给foreground
:
<Button
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="200dp"
android:foreground="?android:attr/selectableItemBackground"
android:background="@color/colorPrimary"
android:text="用力点我"
android:textSize="28dp" />
操蛋的是,API23以上才有效果,试了好久~~
如果想兼容其他,还是通过drawable更方便。
在drawable-v21文件新建一个 ripple_limit_color_bg.xml
文件,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/colorAccent">
<item android:drawable="@color/colorPrimary"></item>
</ripple>
通过子标签,设置背景色
在有边界的3种效果中,其中 有边界+波纹颜色 和 有边界+波纹颜色+背景颜色这2种效果,对比代码之后,发现:
前者只是多了一个属性:android:id="@android:id/mask"
, 这里解释Mask为蒙版层。
什么是Mask?官方的介绍可以点击如下链接了解:
https://developer.android.com/reference/android/graphics/drawable/RippleDrawable
官方文档,我是看了很多次,最后根据上下文,大概理解了下,总结一下几个:
),不管有没有指定 android:id="@android:id/mask"
,都会限制波纹的范围;所以,大概知道Mask的作用之后,就可以分析:
在 有边界+波纹颜色效果中,添加了子图层,并设置了Mask,所以限制了波纹范围,但Mask层不会绘制到屏幕,所以不管什么颜色值,多是无效的。
在 有边界+波纹颜色+背景颜色效果中,添加了子图层,也限制了波纹范围,这时颜色值是有效的。
根据Mask的理解,也大概知道无边界的情况。下面就介绍无边界,先看效果图:
给Button设置只有单独的波纹背景,如下代码:
在xml中设置:
android:background="?android:selectableItemBackgroundBorderless"
如果想在代码中设置,如下(但感觉复杂好多):
int[] attrs1 = new int[]{R.attr.selectableItemBackgroundBorderless};
TypedArray typedArray1 = obtainStyledAttributes(attrs1);
int backgroundResource1 = typedArray1.getResourceId(0, 0);
mBtn.setBackgroundResource(backgroundResource1);
通过关于Mask的介绍,可以清楚知道,如果设置无边界的波纹,那么就不要设置子图层,单独设置波纹颜色即可。
首先,在drawable-v21文件新建一个 ripple_unlimit_color.xml
文件,代码如下
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/colorAccent">
</ripple>
其次在代码中设置:
mBtn.setBackgroundResource(R.drawable.ripple_unlimit_color);
其实,波纹的无边界,并非屏幕有多大,波纹就延伸多大。默认的情况下,也会限制一定的半径。(而且试了下,在xml设置 radius
属性,也不能限制。)
那么,是否就没有其他办法限制呢?如此猖狂,那就让他父亲管教一下吧。
官方文档,有明显说到,它将在该视图的最近父视图(具有非空背景)上进行绘制并由该父视图设定边界
所以,只要给Button添加一个父容器,并设置背景,即可。
首先在xml设置,如下:
<RelativeLayout
android:id="@+id/btn_parent_layout"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_below="@+id/spinner">
<Button
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="用力点我"
android:textSize="28dp" />
</RelativeLayout>
这里,设置父容器的高度是200dp,Button视图的高度为200dp。
其次,在代码中设置,将父容器添加透明背景:
findViewById(R.id.btn_parent_layout).setBackgroundResource(R.color.transparent);
mBtn.setBackgroundResource(R.drawable.ripple_unlimit_color);
那么,现在波纹就没有那么猖狂了。而且发现,这和有边界的效果直接一样了~~
前面介绍的,Button视图的背景只是单纯的颜色,但有时候,我们需要的是图片,或者是shape,还有selector。下面就简单介绍一下,其用法多是一样,多是写在
里面即可。
在drawable-v21文件新建一个 ripple_color_img.xml
文件,代码如下
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/colorAccent">
<!--图片背景 无Mask-->
<item android:drawable="@mipmap/signal"></item>
<!--shape背景 无Mask-->
<item android:drawable="@drawable/shape"></item>
<!--selector背景 无Mask-->
<item android:drawable="@drawable/ripple_selector"></item>
<!--图片背景 有Mask-->
<item android:id="@android:id/mask" android:drawable="@mipmap/signal"></item>
<!--shape背景 有Mask-->
<item android:id="@android:id/mask" android:drawable="@drawable/shape"></item>
<!--selector背景 有Mask-->
<item android:id="@android:id/mask" android:drawable="@drawable/ripple_selector"></item>
</ripple>
很简单,以前设置在background
属性,挪到这里来就可以了。
如果,前面的代码,每一条多测试一遍,会发现:
前面在测试背景颜色时,大概结论是Mask不会绘制在图层,但是,对于图片,却是可以看到该图层(在点击还未松开时)。也不知道是我对Mask的理解错误,还是bug,反正就是这么个现象。
具体效果如下:
好了,关于Ripple的详细理解就到这里,如果你项目中需要使用到这种动画,以上应该可以满足!
参考链接:
https://developer.android.com/reference/android/graphics/drawable/RippleDrawable