接触点的涟漪
当应用程序开发人员在 Material Design 风格的 UI 中使用涟漪动画时,谷歌已经开始向他们指出,这些涟漪似乎并不是从触发动画的接触点发出的。
使用 Google 的搜索引擎,我发现唯一似乎可以解释如何进行这项工作的内容是对 Lucas Rocha 的 TwoWayView 库上的一个问题的评论。幸运的是,这条评论足以让我创造出一些似乎有效的东西。
正如 Butcher 先生在他的推文中暗示的那样,关键似乎是 Drawable 上的 setHotspot() 方法。在 API 级别 21 中添加,这教会了可绘制对象一个“热点”,而 RippleDrawable 显然将其用作涟漪效果的发射点。setHotspot() 采用一对浮点值,大概是着眼于在 OnTouchListener 内部使用 setHotspot(),因为 MotionEvent 使用浮点值报告触摸事件的 X/Y 位置。
这个示例应用程序演示了在 RecyclerView 上下文中使用 setHotspot(),作为我正在为下一本书更新编写的 50 多页章节的一部分。这个 RecyclerView 使用 LinearLayoutManager,来复制经典 ListView 的基本结构。我的行基于 CardView:
```xml
因此,如果用户触摸一行,我会将触摸点作为其热点传播到背景。我特别返回 false 表示我们没有使用触摸事件,因此如果需要,它将继续处理其余的事件处理程序。
如果没有此 setHotspot() 调用,RippleDrawable 似乎默认为可绘制对象的中心。因此,对于列表样式 RecyclerView 的行,涟漪效应将从行的中心发出。setHotspot(),绑定到触摸事件,改变发射点。
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:cardview="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
cardview:cardCornerRadius="4dp">
<LinearLayout
android:id="@+id/row_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="?android:attr/selectableItemBackground">
<!-- other widgets in here -->
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
```
The LinearLayout that the CardView wraps has ?android:attr/selectableItemBackground as its background, pulling in a theme attribute. If this app runs on API Level 21, that background will be a RippleDrawable.
When I set up the row, I attach an OnTouchListener to set the hotspot, but only on API Level 21+ devices (as setHotspot() does not exist before then):
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
row.setOnTouchListener(new View.OnTouchListener() {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public boolean onTouch(View v, MotionEvent event) {
v
.findViewById(R.id.row_content)
.getBackground()
.setHotspot(event.getX(), event.getY());
return(false);
}
});
}
这似乎有效。我不知道这是否是官方的正确答案,因为我还没有看到 Google 提供的有关如何实现此功能的任何代码(例如,搜索 setHotspot() 的 SDK 示例一无所获)。特别是,直接在后台调用 setHotspot() 让我担心(我们应该先调用 mutate() 吗?)。所以,想象一粒盐,边长大约 15 厘米,然后在这个实现中取出那粒盐。