定制触摸反馈:RippleDrawable

bpkx3107 8年前
   <p>为了更好的在5.0及以上设备上使用波纹效果,简单跟大家聊下RippleDrawable的正确打开方式,如何方便快捷的定义不同的触摸反馈的范围以及波纹扩散的颜色,最后聊下5.0以下设备上的兼容方案</p>    <p>为了在5.0以上设备上实现波纹效果,我们可以通过给View设置background实现,而这个效果,官方已经有两个已经实现的效果供我们选择: ?android:attr/selectableItemBackground 和 ?android:attr/selectableItemBackgroundBorderless ,这两条属性不仅产生的效果不太一样,使用方式上也有些不同,第一条属性是可以随意用的,它是有向下兼容的能力,在5.0以下的设备上没有波纹效果,是普通的变色效果;而第二条属性只支持5.0及以上设备,RequiresApi = 21, 所以如果要使用它的话,一定要考虑兼容性问题。它们的效果也是不一样的:第一个的波纹效果会被限制在View的大小之内,而第二条属性的波纹扩散范围是圆形的,而且圆的大小是以View最远距离点距离View中心点距离为半径,就是说:波纹的扩散范围保证会覆盖整个View,而且会扩散出View。官方提供的解决方法有一点不好就是无法定制,我们不能很方便的自定义波纹效果的颜色以及大小,所以官方还是有另外一个类来让我们折腾,那就是:RippleDrawable,接下来我们将用自己的代码来一一实现我们上面刚刚讨论过的两种效果。</p>    <h3>RippleDrawable</h3>    <p>RippleDrawable 继承自 LayerDrawable , 可以实现多个Layer的层叠,今天只讨论用xml的形式来配置 Ripple ,就是普通的在drawable文件夹下新建一个drawable,它的最外层标签是 ripple :</p>    <pre>  <code class="language-java"><ripple xmlns:android="http://schemas.android.com/apk/res/android"          android:color="@color/colorAccent"          >        </ripple>  </code></pre>    <p>上面这段简单的代码就帮我们实现了一个 color 自定义的 ?android:attr/selectableItemBackgroundBorderless 的效果,直接给目标view设置background属性,就能看到熟悉的波纹效果,如下图返回键的波纹效果:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/dbb99a98f21e2e06e607d19c08a8c966.gif"></p>    <p>我们继续在它上面修改来实现第一条属性的效果:</p>    <pre>  <code class="language-java"><ripple xmlns:android="http://schemas.android.com/apk/res/android"          android:color="@color/colorAccent"          >      <item              android:id="@android:id/mask"              android:drawable=""@android:color/white"/>    </ripple></code></pre>    <p>只是在原来的代码中加上了一个 item 标签,还给这个标签设置了一个 id="@android:id/mask" ,后面的那个 drawable 属性设置了波纹效果的扩散范围,这时候再运行我们的程序,发现波纹扩散的范围果然被限制住了,如下图中返回按钮的效果所示:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/9880ad4006fe2d8d09072675c84d2dc5.gif"></p>    <p>但是,等等,设置background之后,我的View背景岂不是一篇空白?放心,我们继续改代码:</p>    <pre>  <code class="language-java"><ripple xmlns:android="http://schemas.android.com/apk/res/android"          android:color="@color/colorAccent"          >      <item              android:id="@android:id/mask"              android:drawable="@android:color/white"/>        <item android:drawable="@color/colorPrimary"/>    </ripple></code></pre>    <p>修改之后再次执行我们的程序,运行效果如下图中Simple Button的点击效果所示:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/1623aa0dbd02d30ead4bcdad8ec12ae6.gif"></p>    <p>总算大功告成!既有了我们定义的背景,还实现了点击时的波纹效果,唯一的不足可能就是不支持5.0以下的设备,这点因为系统的限制我们就不做考虑了,我们会在后面讨论怎么保证5.0以下设备不会崩溃。</p>    <p>我们来分析一下最后这段代码:定义在 ripple 节点的颜色定义的是最后我们想要的波纹的颜色,里面的两个 item 标签分别定义了波纹扩散的范围以及普通状态目标View的颜色,它是怎么区分两个 item 的呢?没错,就是通过 id ,因为在第一个 item 设置了 android:id="@android:id/mask" ,所以这个 item 不会出现被绘制在屏幕上,只会已蒙层的形式定义波纹的范围,如果没有这个定义这个id的item,那么 ripple 会根据综合所有的item最后得到波纹扩散的范围,再极端,如果一个 item 都没有呢?那就看到我们前面说到的第二种情况:波纹会扩散到parentView的范围内。</p>    <p>如果 ripple 中有多个普通的 item 标签,效果会和 layer-list 效果类似,多个item的drawable会叠加在一起,定义了 mask id的 item 和普通 item 之间不会有影响。</p>    <p>因为在测试的时候一直用的 Button 测试,偶然的机会在 ImageView 上使用,发现竟然不生效,搜索了一下才发现只有当View的clickable属性为true的时候,波纹效果才会生效。</p>    <h3>5.0以下的兼容方案</h3>    <p>既然5.0以上的设备可以实现效果了,5.0以下的设备也不能让它直接崩溃,下面有两种兼容方案:</p>    <ol>     <li> <p>layout-v21方案,相信大家都比较熟悉,写两份 ripple_drawable 文件,分别在layout-v21文件夹下和普通layout文件夹下,普通layout文件夹的 ripple_drawable 为空或者设置普通的点击效果,只要不使用 ripple 标签就好。</p> </li>     <li> <p>代码设置,相对省代码,但是比较费脑,不太跳进,具体大家可以看代码:</p> <pre>  <code class="language-java">if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {      int[] attrs = new int[] { android.R.attr.selectableItemBackgroundBorderless};      TypedArray ta = obtainStyledAttributes(attrs);      button1.setBackground(ta.getDrawable(0));      ta.recycle();  } else {      // do something on devices below 21  }  </code></pre> </li>    </ol>    <p> </p>    <p>来自:http://shaohui.xyz/2017/03/08/ripple_drawable/</p>    <p> </p>