Flutter Animation动画开发之——AnimationStatusListener动画状态监听

源码分析

在之前的文章中我们介绍过Animation的addListener可以用来监听动画每一帧的变化

而如果要监听动画的状态变化,就要用到Animation的void addStatusListener(AnimationStatusListener listener)方法

addStatusListener方法是定义在AnimationLocalStatusListenersMixin中的,Animation抽象类的实现都有withAnimationLocalStatusListenersMixin,比如AnimationController

class AnimationController extends Animation<double>
  with AnimationEagerListenerMixin, AnimationLocalListenersMixin, AnimationLocalStatusListenersMixin {

}

我们再看下withAnimationLocalStatusListenersMixin的源码

  • didRegisterListener():在添加动画状态监听前调用,该方法需融合withAnimationLocalStatusListenersMixin的类来实现

  • addStatusListener(AnimationStatusListener listener):添加动画状态监听

  • removeStatusListener(AnimationStatusListener listener):移除动画状态监听

  • didUnregisterListener():在移除动画状态监听后调用,该方法需融合withAnimationLocalStatusListenersMixin的类来实现

  • notifyStatusListeners(AnimationStatus status):动画状态变化的通知,通知所有注册的AnimationStatusListener

/// A mixin that implements the addStatusListener/removeStatusListener protocol
/// and notifies all the registered listeners when notifyStatusListeners is
/// called.
///
/// This mixin requires that the mixing class provide methods [didRegisterListener]
/// and [didUnregisterListener]. Implementations of these methods can be obtained
/// by mixing in another mixin from this library, such as [AnimationLazyListenerMixin].
mixin AnimationLocalStatusListenersMixin {
  final ObserverList<AnimationStatusListener> _statusListeners = ObserverList<AnimationStatusListener>();

  /// Called immediately before a status listener is added via [addStatusListener].
  ///
  /// At the time this method is called the registered listener is not yet
  /// notified by [notifyStatusListeners].
  void didRegisterListener();

  /// Called immediately after a status listener is removed via [removeStatusListener].
  ///
  /// At the time this method is called the removed listener is no longer
  /// notified by [notifyStatusListeners].
  void didUnregisterListener();

  /// Calls listener every time the status of the animation changes.
  ///
  /// Listeners can be removed with [removeStatusListener].
  void addStatusListener(AnimationStatusListener listener) {
    didRegisterListener();
    _statusListeners.add(listener);
  }

  /// Stops calling the listener every time the status of the animation changes.
  ///
  /// Listeners can be added with [addStatusListener].
  void removeStatusListener(AnimationStatusListener listener) {
    final bool removed = _statusListeners.remove(listener);
    if (removed) {
      didUnregisterListener();
    }
  }

  /// Calls all the status listeners.
  ///
  /// If listeners are added or removed during this function, the modifications
  /// will not change which listeners are called during this iteration.
  void notifyStatusListeners(AnimationStatus status) {
    final List<AnimationStatusListener> localListeners = List<AnimationStatusListener>.from(_statusListeners);
    for (AnimationStatusListener listener in localListeners) {
      try {
        if (_statusListeners.contains(listener))
          listener(status);
      } catch (exception, stack) {
        FlutterError.reportError(FlutterErrorDetails(
          exception: exception,
          stack: stack,
          library: 'animation library',
          context: ErrorDescription('while notifying status listeners for $runtimeType'),
          informationCollector: () sync* {
            yield DiagnosticsProperty<AnimationLocalStatusListenersMixin>(
              'The $runtimeType notifying status listeners was',
              this,
              style: DiagnosticsTreeStyle.errorProperty,
            );
          },
        ));
      }
    }
  }
}

接下来我们在看下AnimationStatus的源码,AnimationStatus是一个枚举类,共定义了如下几种动画状态

  •   dismissed:回到动画起点处
  •   forward:从起点往终点方向执行
  •   reverse:从终点往起点反方向执行
  •   completed:到达动画终点处
/// The status of an animation
enum AnimationStatus {
  /// The animation is stopped at the beginning
  dismissed,

  /// The animation is running from beginning to end
  forward,

  /// The animation is running backwards, from end to beginning
  reverse,

  /// The animation is stopped at the end
  completed,
}

示例代码

看完源码分析,接下我我们写一个示例跑一下

一下示例中演示了一个绿色的方块,宽高从100变化到200,由于监听了动画的状态,当动画执行到终点时会反向执行,当反向执行到起点时又会再正向执行,如此循环往复就可以实现无限循环动画(当然,AnimationController.repeat()也可实现动画循环播放)

绿色方块汇总还有两个Text

当动画到达起点时第一个Text显示“起点”,当动画到达终点时第一个Text显示“终点”

当动画往前正向执行时第二个Text显示“正向”,当动画往后反向执行时第二个Text显示“反向”

大家可以直接拷贝如下整段代码运行看下效果

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: AnimationRoute(),
    );
  }
}

class AnimationRoute extends StatefulWidget {
  @override
  AnimationRouteState createState() => AnimationRouteState();
}

class AnimationRouteState extends State<AnimationRoute> with SingleTickerProviderStateMixin {

  AnimationController controller;
  String position = '';
  String direction = '';

  initState() {
    super.initState();
    // Controller设置动画时长
    // vsync设置一个TickerProvider,当前State 混合了SingleTickerProviderStateMixin就是一个TickerProvider
    controller = AnimationController(
        lowerBound: 100,
        upperBound: 200,
        duration: Duration(seconds: 5),
        vsync: this //
    )..addStatusListener((status){
        if (status == AnimationStatus.dismissed) {
          controller.forward();
          setState(() {
            position = '起点';
          });
        } else if (status == AnimationStatus.forward) {
          setState(() {
            direction = '正向';
          });
        } else if (status == AnimationStatus.reverse) {
          setState(() {
            direction = '反向';
          });
        } else if (status == AnimationStatus.completed) {
          controller.reverse();
          setState(() {
            position = '终点';
          });
        }
      });
    //启动动画(正向执行)
    controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
        animation: controller,
        builder: (BuildContext ctx, Widget child) {
          return Center(
            child: Container(
              color: Colors.green,
              alignment: Alignment.center,
              width: controller.value,
              height: controller.value,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Text(position,
                    style: TextStyle(
                      color: Colors.black,
                      fontSize: 18.0,
                    ),
                  ),
                  Text(direction,
                    style: TextStyle(
                      color: Colors.black,
                      fontSize: 18.0,
                    ),
                  )
                ],
              ),
            ),
          );
        }
    );
  }

  @override
  void dispose() {
    // 释放资源
    controller.dispose();
    super.dispose();
  }
}

 

 

 

posted @ 2019-11-07 10:14  野猿新一  阅读(112)  评论(0编辑  收藏  举报