当前位置: 移动技术网 > IT编程>移动开发>Android > ObjectAnimator属性动画源码分析篇

ObjectAnimator属性动画源码分析篇

2019年07月30日  | 移动技术网IT编程  | 我要评论

昆明三对夫妻混战,聚企网b2b,阿努巴尔的束缚

又和大家见面了,这几天一直在忙大创项目,所以没有更新博客,而且我发现看源码这个东西必须写个博客或者笔记啊,这之前一段时机笔者已经看了valueanimator和objectanimator的源码了,但是这才过了几天,搞了会别的事情就忘得几乎一干二净了。现在又要重头看一遍很痛苦额-。+。

另外,笔者已经在简书写了关于属性动画的比较系统的详细的文章,之后会陆续在csdn上重新写的(是重新写,不是复制过去哦,因为第一次写的实在是太烂了-。=)

好了不继续扯皮了,我们看来一下今天想要讲的东西——objectanimator的源码分析(使用部分)。

objectanimator使用部分源码

我们都知道属性动画使用分为三部分:创建、添加属性、启动。而我们今天要讲的就是关于创建和添加属性。首先来看创建的源码吧:

创建

首先看一下今天所有用到的背景:

写了一个自定义的view——pointview,用来实现一个小球的移动效果,pointview代码如下(可以不用看-。+):

public class pointview extends view {
  private point mcurrentpoint;
  private paint paint;
  /**
   * 两个构造方法
   **/
  public pointview(context context) {
    super(context);
    paint = new paint(paint.anti_alias_flag);
    paint.setcolor(color.cyan);
    paint.setstrokewidth(5);
    paint.setstyle(paint.style.stroke);
  }
  public pointview(context context, @nullable attributeset attrs) {
    super(context, attrs);
    paint = new paint(paint.anti_alias_flag);
    paint.setcolor(color.cyan);
    paint.setstrokewidth(5);
    paint.setstyle(paint.style.stroke);
  }
  /**
   * ondraw开始使用画笔,如果mcurrentpoint为空,就创建point对象,
   * 否则就直接调用drawpoint方法
   **/
  @override
  protected void ondraw(canvas canvas) {
    super.ondraw(canvas);
    if (mcurrentpoint == null) {
      mcurrentpoint = new point(500, 500);
      drawpoint(canvas);
    } else {
      drawpoint(canvas);
    }
  }
  //设置一个属性添加的方法
  public void setmove(point point) {
    mcurrentpoint = point;
    invalidate();
  }
  //启动动画
  private void startanimation() {
    objectanimator animator = objectanimator.ofobject(pointview.this, "move", new ballevaluator(),
        new point(500, 500),
        new point(500, 800), new point(700, 800));
    animator.setinterpolator(new linearinterpolator());
    animator.setduration(5000);
    animator.start();
  }
  //在外部调用的方法,通过此方法,开启小球动作的动画。
  public void moveball() {
    startanimation();
  }
  private void drawpoint(canvas canvas) {
    canvas.drawcircle(mcurrentpoint.getx(), mcurrentpoint.gety(), 30, paint);
  }
  //小球对象的估值器
  public class ballevaluator implements typeevaluator<point> {
    float x;
    float y;
    @override
    public point evaluate(float fraction, point startvalue, point endvalue) {
      float startx = startvalue.getx();
      float starty = startvalue.gety();
      float endx = endvalue.getx();
      float endy = endvalue.gety();
      x = fraction * (endx - startx);
      y = fraction * (endy - starty);
      log.e("asdsad", "x = " + x + "y = " + y);
      return new point(startx + x, starty + y);
    }
  }
}

代码可能有点多,不过这不是主要的,我们今天关注的只是属性动画,所以我们只需要看里面的startanimation方法和setmove方法就好:

  • setmove:由于我们知道属性动画objectanimator类是通过将propertyname拼接成对应的set方法,然后通过反射机制去调用该方法,所以我们需要有一个对应的set方法。
  • startanimation:这个方法我们用来设置我们的动画以及启动动画。setmove方法,很简单,我们只是将传入的新的小球对象赋值给了mcurrentball,然后调用invalidate方法重新绘制。下面看一下startanimation方法:
objectanimator animator = objectanimator.ofobject(pointview.this, "move", new ballevaluator(),
        new point(500, 500),
        new point(500, 800), new point(700, 800));
    animator.setinterpolator(new linearinterpolator());
    animator.setduration(5000);
    animator.start();

我们看一下先进入ofobject方法(相信ofobject里面的参数大家都看得懂):

public static objectanimator ofobject(object target, string propertyname,
      typeevaluator evaluator, object... values) {
    objectanimator anim = new objectanimator(target, propertyname);
    anim.setobjectvalues(values);
    anim.setevaluator(evaluator);
    return anim;
  }

我们发现ofobject是一个静态方法:他在里面创建了一个objectanimator对象。然后调用了setobjeectvalues和setevaluator方法,分别添加了数据和估值器。

也就是这个ofobject里面有三个入口等着我们进去:

  • objectanimator的构造方法
  • setobjectvalues方法
  • setevaluator方法

那我们先从objectanimator的构造方法开始看吧。

objectanimator构造方法:

  private objectanimator(object target, string propertyname) {
    settarget(target);
    setpropertyname(propertyname);
  }

又是两个方法,一个一个去看,先进入settarget:

  @override
  public void settarget(@nullable object target) {
    final object oldtarget = gettarget();
    if (oldtarget != target) {
      if (isstarted()) {
        cancel();
      }
      mtarget = target == null ? null : new weakreference<object>(target);
      // new target should cause re-initialization prior to starting
      minitialized = false;
    }
  }

这些代码都能够知道什么意思,这个方法我们只需要注意两点:

  • 我们将传入的target传给了mtarget这个弱引用。
  • minitialized = false;这个属性我们可以这么理解,在他的objectanimator没有准备就绪(初始化过程尚未完成时),他一直都是false。

这个方法跳过。

然后我们看setpropertyname方法:

  public void setpropertyname(@nonnull string propertyname) {
    // mvalues could be null if this is being constructed piecemeal. just record the
    // propertyname to be used later when setvalues() is called if so.
    if (mvalues != null) {
      propertyvaluesholder valuesholder = mvalues[0];
      string oldname = valuesholder.getpropertyname();
      valuesholder.setpropertyname(propertyname);
      mvaluesmap.remove(oldname);
      mvaluesmap.put(propertyname, valuesholder);
    }
    mpropertyname = propertyname;
    // new property/values/target should cause re-initialization prior to starting
    minitialized = false;
  }

首先介绍一下mvalues和mvaluesmap这两个属性,他们都是存储propertyvalueholder的属性,而且储存的都一样,只是mvaluesmap可以让我们通过propertyname来查找对应的propertyvalueholder。

propertyvaluesholder[] mvalues;
hashmap<string, propertyvaluesholder> mvaluesmap;

这个方法只是将propertyname放入propertyvalueholder中(具体逻辑如上,先判断mvalues是否为空,如果不为空就将propertyname放入mvalues和mvaluesmap中,最后将propertyname赋值给mpropertyname),可以过了。

现在我们的objectanimator构造方法看完了,我们接着看setobjectvalues方法:

anim.setobjectvalues:

  @override
  public void setobjectvalues(object... values) {
    if (mvalues == null || mvalues.length == 0) {
      // no values yet - this animator is being constructed piecemeal. init the values with
      // whatever the current propertyname is
      if (mproperty != null) {
        setvalues(propertyvaluesholder.ofobject(mproperty, (typeevaluator) null, values));
      } else {
        setvalues(propertyvaluesholder.ofobject(mpropertyname,
            (typeevaluator) null, values));
      }
    } else {
      super.setobjectvalues(values);
    }
  }

这段代码的总体逻辑只有一个:如果mvalues没有值,那么就调用setvalues方法,否则就调用父类的setobjectvalues方法。

感觉很乱啊,稳住!   我们这是第一次创建的对象,所以肯定是为空的,所以我们只需要看setvalues方法就好了,但是注意,这里还有propertyvalueholder,所以我们决定先看一下propertyvalueholder的ofobject方法:

propertyvalueholder.ofobject:

  public static <v> propertyvaluesholder ofobject(property property,
      typeevaluator<v> evaluator, v... values) {
    propertyvaluesholder pvh = new propertyvaluesholder(property);
    pvh.setobjectvalues(values);
    pvh.setevaluator(evaluator);
    return pvh;
  }

跟上面objectanimator的ofobject差异不多,我们就不多说了,有两条路可以选:

  • setobjectvalues
  • setevaluator

先看setobjectvalues:

propertyvalueholder.setobjectvalues:

  public void setobjectvalues(object... values) {
    mvaluetype = values[0].getclass();
    mkeyframes = keyframeset.ofobject(values);
    if (mevaluator != null) {
      mkeyframes.setevaluator(mevaluator);
    }
  }

我知道大家都快吐了,现在keyframes又出来了,头皮发麻对吧,稳住,我们坚持住!

这个keyframes是keyframeset的接口,我们看一下keyframeset的ofobject方法:

keyframeset.ofobject方法:

  public static keyframeset ofobject(object... values) {
    int numkeyframes = values.length;
    objectkeyframe keyframes[] = new objectkeyframe[math.max(numkeyframes,2)];//创建了一个至少为两位的objectkeyframe对象
    if (numkeyframes == 1) {
      keyframes[0] = (objectkeyframe) keyframe.ofobject(0f);
      keyframes[1] = (objectkeyframe) keyframe.ofobject(1f, values[0]);
    } else {
      keyframes[0] = (objectkeyframe) keyframe.ofobject(0f, values[0]);
      for (int i = 1; i < numkeyframes; ++i) {
        keyframes[i] = (objectkeyframe) keyframe.ofobject((float) i / (numkeyframes - 1), values[i]);
      }
    }
    return new keyframeset(keyframes);
  }

我们终于看到了一个比较熟悉的类,keyframe,这个叫做关键帧的类终于出现了,我们简单分析一下这个方法:

首先创建了一个至少为两位的objectkeyframe对象,然后对values的长度进行判断,如果只有一个值,那么就将唯一的一个值添加到最后一位(此时也就是第二位),否则就依次添加。最后将objectkeyframe的数组转换成keyframeset类型返回。

现在我们回到propertyvalueholder的setobjectvalues方法中,接下来我们要看一下setevaluator方法(需要在keyframeset中查看)

keyframeset.setevaluator方法:

  public void setevaluator(typeevaluator evaluator) {
    mevaluator = evaluator;
  }

这个不用多说,直接过。

现在我们的propertyvalueholder的ofobject方法已经看完了,我们跳回anim.setobjectvalues方法,看一下setvalues方法:

setvalues:

  public void setvalues(propertyvaluesholder... values) {
    int numvalues = values.length;
    mvalues = values;
    mvaluesmap = new hashmap<string, propertyvaluesholder>(numvalues);
    for (int i = 0; i < numvalues; ++i) {
      propertyvaluesholder valuesholder = values[i];
      mvaluesmap.put(valuesholder.getpropertyname(), valuesholder);
    }
    // new property/values/target should cause re-initialization prior to starting
    minitialized = false;
  }

这个完全就是我们刚才说的储存propertyvalueholder的两个属性mvalues和mvaluesmap添加数据的过程,过。

现在我们只差最后一个大方法了,anim.setevaluator了,激动!!!

anim.setevaluator方法:

  public void setevaluator(typeevaluator value) {
    if (value != null && mvalues != null && mvalues.length > 0) {
      mvalues[0].setevaluator(value);
    }
  }

这个方法是给每个propertyvalueholder对象都执行setevaluator方法,我们点进去这个方法看一下:

propertyvalueholder.setevaluator方法:

  public void setevaluator(typeevaluator evaluator) {
    mevaluator = evaluator;
    mkeyframes.setevaluator(evaluator);
  }

又进入了keyframes的setevaluator,我们接着看一下keyframeset的setevaluator方法:

keyframeset.setevaluator方法:

  public void setevaluator(typeevaluator evaluator) {
    mevaluator = evaluator;
  }

这个方法不用说了。

最后我们返回了创建的anim的对象,到现在为止,我们得到我们想要的objectanimator对象了。

这个过程是有点繁琐,我们现在屡一下思路:

调用了objectanimator.ofobject之后

  1. 首先new一个objectanimator对象,进入objectanimator的构造方法中:在构造方法中,我们执行了两个方法:settarget和setpropertyname。
  2. 然后调用了objectanimator的setobjectvalues方法:在这个方法中我们首先实例化了propertyvalueholder对象,然后调用setvalues方法将propertyvalueholder传入。
  3. 之后调用了objectanimator的setevaluator方法:添加了估值器。
  4. 最后返回了objectanimator对象。

到这里我们就完成了objectanimator对象实例的创建。

到这里创建部分就全部完成了,接下来我们看一下添加属性,这个就很简单了。

添加属性

我们就举一个方法为例吧,拿setinterpolator方法为例:

  public void setinterpolator(timeinterpolator value) {
    if (value != null) {
      minterpolator = value;
    } else {
      minterpolator = new linearinterpolator();
    }
  }

这个方法是我们添加插值器的方法,我们注意到他只是给minterpolator赋值而已,如果传入为空,则添加线性插值器。

其他的添加属性的方法,像setduration、setrepeatcount等都是如此,大家下去就自己看一下吧。

由于还不会用 staruml,所以现在还没发画一张时序图(只是手画的,估计大家也不想看哈哈),等学完uml之后会给大家补上的,希望这篇文章大家喜欢。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对移动技术网的支持。如果你想了解更多相关内容请查看下面相关链接

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网