Lottie如何绑定事件?

前言

大家在网上查看关于Lottie使用时,其中在讲述它的优点时,就会提到“绑定事件”,例如:Lottie的使用和源码详解,但是从网上并未找到相关的文章。一次我在和同事分享关于Lottie使用时,也被问到了这个问题,于是乎秉着自己动手丰衣足食的原则,查看了源码,写下了这篇文章。

一、试问世间事件是何物?

作为一名Android开发攻城狮,一提到事件首先会想到的可能就是OnClickListener、OnLongClickListener事件等等。所以也就跟着这个思路到Lottie中找相关的函数,结果老天不负有心人,找了好久什么都没有找到。

就在要放弃的时候,突然想到了Lottie官方Demo,git clone后发现其中有一个“Bullseye”的示例。

你通过手势对浅蓝色小圆进行控制,从而达到对图形的控制,看到这个效果,突然恍然大悟,这就是Lottie中口口相传的绑定事件。

它本质上不是View本身提供对动画操作的事件,而是通过对动画中的KeyPath进行操作,从而达到对动画的操作。也就是说需要通过其他的View事件来绑定KeyPath的操作,而从达到绑定事件的效果。

二、绑定事件

为了更好帮助大家对绑定事件的理解,下面我会分解“Bullseye”示例代码,以便更好的讲解。

1、布局代码

<com.airbnb.lottie.samples.views.InterceptingFrameLayout  //这是自定义布局主要就是通过它的事件对动画效果的控制
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/containerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.airbnb.lottie.LottieAnimationView   //这个就不用讲了吧,它是Lottie提供的自定义View
        android:id="@+id/animationView"
        android:layout_width="256dp"
        android:layout_height="256dp"
        app:lottie_url="https://raw.githubusercontent.com/airbnb/lottie-android/master/LottieSample/src/main/res/raw/bullseye.json"
        app:lottie_autoPlay="true"
        app:lottie_loop="true"
        android:layout_gravity="center"/>

    <com.airbnb.lottie.samples.views.BackgroundColorView   //这个是浅蓝色的小圆圈
        android:id="@+id/targetView"
        android:layout_width="16dp"
        android:layout_height="16dp"
        android:background="#00ddff"
        android:layout_gravity="center"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|center_horizontal"
        android:layout_marginBottom="8dp"
        android:text="@string/bullseye_drag_dot"
        android:textColor="@color/text_color"/>

</com.airbnb.lottie.samples.views.InterceptingFrameLayout>

其中InterceptingFrameLayout主要是通过ViewDragHelper进行手势识别控制,而BackgroundColorView只是绘制了一个圆,并未做其他操作。

2、关键技术点

Lottie在解析json文件时,最终会把json属性解析成对应的类型Layer,比如说TextLayer、SolidLayer等,以及Layer包含的TransformKeyframeAnimation(关键帧动画),具体请查看:Lottie的使用和源码详解

Lottie对外提供了可以修改某一个属性的回调方法,代码如下:

/**
   * Add a property callback for the specified {@link KeyPath}. This {@link KeyPath} can resolve
   * to multiple contents. In that case, the callback's value will apply to all of them.
   *
   * Internally, this will check if the {@link KeyPath} has already been resolved with
   * {@link #resolveKeyPath(KeyPath)} and will resolve it if it hasn't.
   */
  public <T> void addValueCallback(KeyPath keyPath, T property, LottieValueCallback<T> callback) {
    lottieDrawable.addValueCallback(keyPath, property, callback);
  }

看到这个方法,大家可能会疑惑,对其中的两个参数产生疑问(property和callback),而且官方并未做解释。我在查看源码得知:

property含义:

它的含义是指想要设置的属性,比如说COLOR、POSITION等等啦,具体请看下面代码块:

public interface LottieProperty {
  /** ColorInt **/
  Integer COLOR = 1;
  Integer STROKE_COLOR = 2;
  /** Opacity value are 0-100 to match after effects **/
  Integer TRANSFORM_OPACITY = 3;
  /** [0,100] */
  Integer OPACITY = 4;

  /** In Px */
  PointF TRANSFORM_ANCHOR_POINT = new PointF();
  /** In Px */
  PointF TRANSFORM_POSITION = new PointF();
  /** In Px */
  PointF ELLIPSE_SIZE = new PointF();
  /** In Px */
  PointF POSITION = new PointF();

  ScaleXY TRANSFORM_SCALE = new ScaleXY();
...
}

它的使用就像我们使用的属性动画效果是一样的,设置不同属性的值,达到不同的效果。不过有一点需要注意的,不是所有的属性都可以在不同的Layer使用的。比如说:TextLayer支持的为(LottieProperty.COLOR、LottieProperty.STROKE_COLOR、LottieProperty.STROKE_WIDTH、LottieProperty.TEXT_TRACKING),而SolidLayer支持的为LottieProperty.COLOR_FILTER。所以大家在设置属性的时候一定要分清楚。

callback含义:

它的含义是指设置属性值的回调类,也就是说只需要给它设置相关的值,它会自动调用刷新。它包含了两个方法:

public class LottieValueCallback<T> {
...
@Nullable
  public T getValue(LottieFrameInfo<T> frameInfo) {
    return value;
  }

  public final void setValue(@Nullable T value) {
    this.value = value;
    if (animation != null) {
      animation.notifyListeners();
    }
  }
  ...

我们只需要调用setValue函数,即可更改属性值。它的子类中包含常用的三个类:LottieRelativeFloatValueCallback,LottieRelativeIntegerValueCallback,LottieRelativePointValueCallback。我们可以使用这些类,对属性进行修改,当然也可以直接使用LottieValueCallback类进行修改。

3、示例讲解:

下面我们需要修改三个关键路径,所以需要新增是三个Callback。json源文件

代码如下:

val largeValueCallback = LottieRelativePointValueCallback(PointF(0f, 0f))
animationView.addValueCallback(KeyPath("First"), LottieProperty.TRANSFORM_POSITION, largeValueCallback)

val mediumValueCallback = LottieRelativePointValueCallback(PointF(0f, 0f))
animationView.addValueCallback(KeyPath("Fourth"), LottieProperty.TRANSFORM_POSITION, mediumValueCallback)

val smallValueCallback = LottieRelativePointValueCallback(PointF(0f, 0f))
animationView.addValueCallback(KeyPath("Seventh"), LottieProperty.TRANSFORM_POSITION, smallValueCallback)

我们通过KeyPath找到名为"First"关键帧,设置LottieProperty.TRANSFORM_POSITION,并通过largeValueCallback对它的属性值进行设置,具体代码如下:

val viewDragHelper = ViewDragHelper.create(containerView, object : ViewDragHelper.Callback() {
            override fun tryCaptureView(child: View, pointerId: Int) = child == targetView

            override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int {
                return top
            }

            override fun clampViewPositionHorizontal(child: View, left: Int, dx: Int): Int {
                return left
            }

            override fun onViewPositionChanged(changedView: View, left: Int, top: Int, dx: Int, dy: Int) {
                totalDx += dx
                totalDy += dy
                smallValueCallback.setValue(getPoint(totalDx, totalDy, 1.2f))
                mediumValueCallback.setValue(getPoint(totalDx, totalDy, 1f))
                largeValueCallback.setValue(getPoint(totalDx, totalDy, 0.75f))
            }
        })

至此整个Lottie的绑定事件讲解完毕,如果大家有任何问题,可以在评论区留言

你可能感兴趣的:(android)