Material Design 动画——Ripple波纹动画

1. 介绍

Material Design 中的轻触反馈可在用户与界面元素互动时,在接触点上提供即时视觉确认。按钮的默认轻触反馈动画使用新的 RippleDrawable 类,以波纹效果实现不同状态间的过渡。

RippleDrawable是android5新增加的一种Drawable,所以,根据需求需要自定义的时候,可以自定义RippleDrawable来实现想要的功能。

既然是Drawable,那么也可以在drawable目录下,定义xml文件。RippleDrawable在xml中对应的标签是,它只有两个属性——color、radius。需要注意的是,这些功能是API21才能使用,所以需要定义在 drawable-v21的目录下。

2.使用

触摸反馈的效果是一种涟漪的效果,大致可以分为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="无边界" />

效果:

Material Design 动画——Ripple波纹动画_第1张图片

上面的代码,定义2个Button,主要区别在于background属性:

selectableItemBackground:涟漪效果发生在当前Button限制的区域;表示有界的波纹。
selectableItemBackgroundBorderless:涟漪效果不会局限于Button本身的大小。表示越过视图边界的波纹,它将在该视图的最近父视图(具有非空背景)上进行绘制并由该父视图设定边界。(该属性必须定义在API21)

使用触摸反馈就是这么简单,设置一下属性就可以了。

但,平时使用的时候,可能会遇到一些问题,如:

  1. 如何修改默认的波纹颜色?
  2. Button本来有颜色背景,默认的触摸反馈动画失效
  3. 如何自定一些特殊的效果?

下面,详细介绍其用法,并解决以上问题!!!

有边界

首先,先看看有边界的几种情况。先看看效果:

Material Design 动画——Ripple波纹动画_第2张图片

1.有边界

给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.有边界+波纹颜色

前面看到,如果单独设置一个系统自带的波纹,默认是灰色的。修改波纹的颜色,有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,后面介绍

那么当前视图的波纹颜色就会改变。(局部波纹颜色 优先级高于 全局波纹颜色)

3.有边界+波纹颜色+背景颜色

波纹效果是系统自带的效果,但我们经常遇到一个问题是:

给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>

通过子标签,设置背景色

关于Mask

在有边界的3种效果中,其中 有边界+波纹颜色有边界+波纹颜色+背景颜色这2种效果,对比代码之后,发现:
前者只是多了一个属性:android:id="@android:id/mask" , 这里解释Mask为蒙版层。

什么是Mask?官方的介绍可以点击如下链接了解:
https://developer.android.com/reference/android/graphics/drawable/RippleDrawable

官方文档,我是看了很多次,最后根据上下文,大概理解了下,总结一下几个:

  1. RippleDrawable 可包含多个子图层,包括未绘制到屏幕的特殊蒙版图层(即指定Mask);
  2. 在子图层中(即添加了),不管有没有指定 android:id="@android:id/mask",都会限制波纹的范围;
  3. 没有设置子图层或者没有指定Mask,波纹可以延伸到本视图之外(如果父视图设置了背景,则会限制),即前面说的,无边界波纹。
Material Design 动画——Ripple波纹动画_第3张图片

所以,大概知道Mask的作用之后,就可以分析:

有边界+波纹颜色效果中,添加了子图层,并设置了Mask,所以限制了波纹范围,但Mask层不会绘制到屏幕,所以不管什么颜色值,多是无效的。
有边界+波纹颜色+背景颜色效果中,添加了子图层,也限制了波纹范围,这时颜色值是有效的。

无边界

根据Mask的理解,也大概知道无边界的情况。下面就介绍无边界,先看效果图:

Material Design 动画——Ripple波纹动画_第4张图片

1.无边界

给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);

2.无边界+波纹颜色

通过关于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);

3.无边界+父背景

其实,波纹的无边界,并非屏幕有多大,波纹就延伸多大。默认的情况下,也会限制一定的半径。(而且试了下,在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

你可能感兴趣的:(android,Material,Design,动画,Ripple,波纹动画)