Ripple 水波纹效果
Posted 白乾涛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Ripple 水波纹效果相关的知识,希望对你有一定的参考价值。
背景+波纹
对于有边界限制的Ripple,我们就需要给他提供一个范围,即添加一个item标签。
- 如果在一个ripple标签中,添加一个item标签,在item中添加如下属性:
- 【android:drawable="@color/***"】水波效果会限定在本身矩形区域内部
- 【android:drawable="@drawable/png等"】水波效果会限定在图片中非透明部分对应的区域内部
- 【android:drawable="@color/shape"】水波效果会限定在shape对应的区域内部
- 如果在一个ripple标签中,添加一个item标签,在item中添加一个selector标签,那么将同时具有水波效果和selector效果
item带不带id的区别
添item时,如果不指定id为@android:id/mask,那么不点击时会显示出该item指定的drawable
添item时,如果指定id为@android:id/mask,那么不点击时不会显示出该item指定的drawable,而仅仅在点击的时候才出现
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#F00" >
<item
android:id="@android:id/mask"
android:drawable="@drawable/ic_launcher"/>
</ripple>实践中发现,这两个属性都是一样的,且和没设置时的效果一样
- 【android:background="?android:attr/selectableItemBackground"】有边界波纹
- 【android:background="?android:attr/selectableItemBackgroundBorderless"】超出边界波纹
当ripple标签内只指定一个android:color属性时,则该ripple效果的绘制会溢出其所在View的边界,直接绘制在父控件的背景之上。
如果父控件没有设置背景,则会进一步绘制在父控件的上一级父控件的背景之上。
Ripple生效条件
当 View 有设置 OnClickListener 的情况下被点击,或者获得/失去焦点变化时,将出现Ripple效果
如果点击效果没有,很可能是该控件本身点击没开启,设置如下属性即可【android:clickable="true"】
不适用Ripple的场景
点击之后就立马消失的组件(setVisibility:gone invisible 或 remove)不适合使用
因为当组件恢复为visiable后,未播放完的Ripple动画会继续播放,会产生疑惑
硬件加速开关对无边界Ripple的影响:
在Android 3.0 (API level 11)引入的硬件加速功能默认在application/Activity/View这三个层级上都是开启的。
但如果手动关闭了,则无边界Ripple不会生效。
子层(Child Layer)
由于View在不同的交互下有不同的state,常见的为pressed、focused或normal这三种状态。
所以Ripple通过多个item来表示不同state下的显示,每个item都是一个子层(Child Layer),能够直接显示color、shape、drawable/image 及 selector。
当Ripple存在一个或多个子层时,则ripple效果则被限定在当前View的边界内了。无边界效果(unbounded ripple)失效。
Mask层(Mask Layer)
可以设置指定子层item的android:id="@android:id/mask"来设定当前Ripple的Mask。
Mask的内容并不会被绘制到屏幕上,它的作用是限定Ripple效果的绘制区域。
mask所在的的子层限制了Ripple效果的最大范围只能是View的边界,不会扩散到父组件。
与ClickableSpan冲突
如果Layout有包含ClickableSpan的TextView,则发现该Layout设置Ripple的效果无法响应。
这个现象可以推断出MotionEvent这个事件在TextView这一层级被消耗了,下一步应该为找出该事件为什么被消耗?
通过debug源码,发现当点击事件传递到TextView时,会进一步传递给LinkMovementMethod::onTouchEvent(),如果点击位置处于ClickableSpan以外,则返回Touch.onTouchEvent(widget, buffer, event);
该方法在处理MotionEvent::ACTION_DOWN时默认返回true,导致Ripple失效。
那么解决思路也就简单了,重写LinkedMovementMethod::onTouchEvent()方法,当且仅当点击到ClickableSpan时,才返回true即可。
具体详见https://github.com/sodino/Ripple/blob/master/app/src/main/java/com/sodino/ripple/RippleTextView.java
布局
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical" >
<Button
style="@style/textview"
android:text="在5.0以上,Button默认自带Ripple点击效果" />
<View style="@style/line" />
<Button
style="@style/textview"
android:background="@drawable/ic_launcher"
android:text="但是如果设置了其他的background \n Ripple点击效果就没了" />
<View style="@style/line" />
<TextView
style="@style/textview"
android:text="TextView默认是没有Ripple点击效果的" />
<View style="@style/line" />
<TextView
style="@style/textview2"
android:background="@drawable/_1_default"
android:text="默认是圆形的、超出边界的波纹\n波纹的直径为控件宽高中的最大值" />
<View style="@style/line" />
<TextView
style="@style/textview2"
android:background="@drawable/_1_default2"
android:text=" 但当波纹遇到其他控件的背景时,不会遮挡住其他控件的背景(比如不会挡住下面控件的背景)" />
<View style="@style/line" />
<TextView
style="@style/textview"
android:background="@drawable/_2_color_without_id"
android:text="用颜色作为Mask,不指定id\n此时item中的颜色会被用来作为点击前的背景颜色" />
<View style="@style/line" />
<TextView
style="@style/textview"
android:background="@drawable/_2_color_with_id"
android:text="用颜色作为Mask,指定id\n此时item中的颜色没任何卵用,但可以用来限定边界" />
<View style="@style/line" />
<TextView
style="@style/textview3"
android:background="@drawable/_3_pic_without_id"
android:text="用图片作为Mask\n不指定id" />
<View style="@style/line" />
<TextView
style="@style/textview3"
android:background="@drawable/_3_pic_with_id"
android:text="用图片作为Mask\n指定id" />
<View style="@style/line" />
<TextView
style="@style/textview3"
android:background="@drawable/_4_shape_without_id"
android:text="用shape作为Mask\n不指定id,矩形" />
<View style="@style/line" />
<TextView
style="@style/textview3"
android:background="@drawable/_4_shape_with_id"
android:text="用shape作为Mask\n指定id,圆形" />
<View style="@style/line" />
<TextView
style="@style/textview"
android:background="@drawable/_5_selector_without_id"
android:text="搭配selector使用,不指定id \n将同时具有水波效果和selector效果" />
<View style="@style/line" />
<TextView
style="@style/textview"
android:background="@drawable/_5_selector_with_id"
android:text="搭配selector使用,指定id \n和上面的一样,指定id后选择器效果将丢失" />
<View style="@style/line" />
<TextView
style="@style/textview"
android:background="@drawable/_5_selector_fuza"
android:text="这是一种负责的情况" />
</LinearLayout>
</ScrollView>样式
<resources>
<style name="textview">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">50dp</item>
<item name="android:gravity">center</item>
<item name="android:clickable">true</item>
</style>
<style name="textview2">
<item name="android:layout_width">300dp</item>
<item name="android:layout_height">50dp</item>
<item name="android:gravity">center</item>
<item name="android:clickable">true</item>
</style>
<style name="textview3">
<item name="android:layout_width">120dp</item>
<item name="android:layout_height">50dp</item>
<item name="android:gravity">center</item>
<item name="android:clickable">true</item>
</style>
<style name="line">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">1dp</item>
<item name="android:background">#000</item>
</style>
</resources>附件列表
以上是关于Ripple 水波纹效果的主要内容,如果未能解决你的问题,请参考以下文章
android - 如何给Imageview 设置水波纹效果