0

接触点的涟漪

当应用程序开发人员在 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 厘米,然后在这个实现中取出那粒盐。

4

1 回答 1

0

当您显示或隐藏一组 UI 元素时,显示动画为用户提供视觉连续性。ViewAnimationUtils.createCircularReveal() 方法使您能够为剪切圆设置动画以显示或隐藏视图。

要使用此效果显示以前不可见的视图:

View myView = findViewById(R.id.my_view);

// get the center for the clipping circle
int cx = myView.getWidth() / 2;
int cy = myView.getHeight() / 2;

// get the final radius for the clipping circle
float finalRadius = (float) Math.hypot(cx, cy);

// create the animator for this view (the start radius is zero)
Animator anim =
    ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);

// make the view visible and start the animation
myView.setVisibility(View.VISIBLE);
anim.start();

要使用此效果隐藏以前可见的视图:

// previously visible view
final View myView = findViewById(R.id.my_view);

// get the center for the clipping circle
int cx = myView.getWidth() / 2;
int cy = myView.getHeight() / 2;

// get the initial radius for the clipping circle
float initialRadius = (float) Math.hypot(cx, cy);

// create the animation (the final radius is zero)
Animator anim =
    ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0);

// make the view invisible when the animation is done
anim.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        super.onAnimationEnd(animation);
        myView.setVisibility(View.INVISIBLE);
    }
});

// start the animation
anim.start();
于 2016-11-14T17:36:08.680 回答