Android ValueAnimator时长错乱或者不起作用的解决方法以及问题分析

今天在低端机上面测试,发现里面的属性动画竟然无法正常播放, 一下子就从起点跳到终点。

一番调试无果后,就感觉是手机本身问题, 于是就想看看其他应用的属性动画在这台手机上是否也这样:

记得之开布局边界时,看到 网易云音乐客户端 的音乐播放界面中的光盘旋转效果是属性动画,于是打开应用后,随便播放一首歌,发现光盘果然是不转了,这时忽然想到 设置-开发者选项中有几个动画时长缩放的选项,打开一看,原来是 “动画程序时长缩放” 这个选项选择了关闭动画,重新打开后果然恢复正常了。

但是实际应用中总不能提示用户手动开启吧, 究竟设置中的这个选项是怎样影响到我们应用中的动画时长的呢。

还是先说解决方法吧:(经测试,每次在activity recreate后都会重置动画时长为系统指定的值, 哈哈,所以如果想偷懒,只在Application onCreate里面调用下面的方法的话,依然是不起作用的,建议每次播放动画前重置一下。)

public class ValueAnimatorUtil {

    /**
     * 如果动画被禁用,则重置动画缩放时长
     */
    public static void resetDurationScaleIfDisable() {
        if (getDurationScale() == 0)
            resetDurationScale();
    }

    /**
     * 重置动画缩放时长
     */
    public static void resetDurationScale() {
        try {
            getField().setFloat(null, 1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static float getDurationScale() {
        try {
            return getField().getFloat(null);
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    @NonNull
    private static Field getField() throws NoSuchFieldException {
        Field field = ValueAnimator.class.getDeclaredField("sDurationScale");
        field.setAccessible(true);
        return field;
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

下面说说原因:
打开ValueAnimator源码,看到一个私有的静态变量 sDurationScale:

public class ValueAnimator extends Animator implements AnimationHandler.AnimationFrameCallback {
    private static final String TAG = "ValueAnimator";
    private static final boolean DEBUG = false;

    /**
     * Internal constants
     */
    private static float sDurationScale = 1.0f;
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

难道是这个东西在作怪?看看下面的方法:
这个方法是处理动画帧的方法, 更新了mLastFrameTime后, 判断动画是否已经播放完毕,
如果播放完毕,就进行结束动画的一系列处理: 回调接口、重置状态等

  /**
     * Processes a frame of the animation, adjusting the start time if needed.
     *
     * @param frameTime The frame time.
     * @return true if the animation has ended.
     * @hide
     */
    public final boolean doAnimationFrame(long frameTime) {
       ....
       ....

       mLastFrameTime = frameTime;
       final long currentTime = Math.max(frameTime, mStartTime);

       boolean finished = animateBasedOnTime(currentTime);
       if (finished) {
       endAnimation();
       } 
       return finished;
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

我们来看看他是怎样判断动画是否播放完毕的:
这次我们要关注的是getScaledDuration这个方法, 因为这个方法返回的值能决定是否播放完毕

    boolean animateBasedOnTime(long currentTime) {
        boolean done = false;
        if (mRunning) {
            final long scaledDuration = getScaledDuration();
            final float fraction = scaledDuration > 0 ?
                    (float)(currentTime - mStartTime) / scaledDuration : 1f;
            final float lastFraction = mOverallFraction;
            final boolean newIteration = (int) fraction > (int) lastFraction;
            final boolean lastIterationFinished = (fraction >= mRepeatCount + 1) &&
                    (mRepeatCount != INFINITE);
            if (scaledDuration == 0) {
                // 0 duration animator, ignore the repeat count and skip to the end
                done = true;
            } else if (newIteration && !lastIterationFinished) {
                // Time to repeat
                if (mListeners != null) {
                    int numListeners = mListeners.size();
                    for (int i = 0; i < numListeners; ++i) {
                        mListeners.get(i).onAnimationRepeat(this);
                    }
                }
            } else if (lastIterationFinished) {
                done = true;
            }
            mOverallFraction = clampFraction(fraction);
            float currentIterationFraction = getCurrentIterationFraction(
                    mOverallFraction, mReversing);
            animateValue(currentIterationFraction);
        }
        return done;
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

getScaledDuration方法:
是用我们设定的动画时长 * mDurationScale, 如果mDurationScale为0, 那么就满足上面的条件,直接认为动画播放完毕了

 private long getScaledDuration() {
        return (long)(mDuration * sDurationScale);
    }
 
 
  • 1
  • 2
  • 3

下面我们来验证一下 设置-开发者选项-动画时长缩放 这个选项是否跟mDurationScale有关:

      try {
            Field field = ValueAnimator.class.getDeclaredField("sDurationScale");
            field.setAccessible(true);
            LogUtil.print(field.getFloat(null));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

先是默认的:
这里写图片描述
日志输出:

1.0
 
 
  • 1

再试试5x的:
这里写图片描述
日志输出:

5.0
 
 
  • 1

没错了,最后试试关闭动画:
这里写图片描述
日志输出果然是0.0。 我们再播放一个ValueAnimator来测试下:

     ValueAnimator animator = ValueAnimator.ofInt(0, 100).setDuration(1000);
     animator.addUpdateListener(animation -> LogUtil.print(animation.getAnimatedValue()));
     animator.start();
 
 
  • 1
  • 2
  • 3

输出:

11-24 21:22:59.925 9244-9244/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 0
11-24 21:23:00.087 9244-9244/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 100
 
 
  • 1
  • 2

输出0之后,直接是100了,这就对应了开头描述的情况:一下子就从起点跳到终点。

最后我们试试在动画开始前重置mDurationScale:

     ValueAnimator animator = ValueAnimator.ofInt(0, 100).setDuration(1000);
      animator.addUpdateListener(animation -> LogUtil.print(animation.getAnimatedValue()));
      ValueAnimatorUtil.resetDurationScale();
      animator.start();
 
 
  • 1
  • 2
  • 3
  • 4

输出:

11-24 21:30:35.102 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 0
11-24 21:30:35.261 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 0
11-24 21:30:35.332 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 1
11-24 21:30:35.393 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 5
11-24 21:30:35.406 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 6
11-24 21:30:35.424 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 7
11-24 21:30:35.442 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 9
11-24 21:30:35.460 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 11
11-24 21:30:35.478 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 13
11-24 21:30:35.496 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 14
11-24 21:30:35.515 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 17
11-24 21:30:35.534 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 19
11-24 21:30:35.551 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 21
11-24 21:30:35.569 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 24
11-24 21:30:35.587 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 26
11-24 21:30:35.605 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 28
11-24 21:30:35.623 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 31
11-24 21:30:35.641 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 34
11-24 21:30:35.659 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 36
11-24 21:30:35.677 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 39
11-24 21:30:35.696 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 42
11-24 21:30:35.713 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 45
11-24 21:30:35.732 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 48
11-24 21:30:35.750 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 51
11-24 21:30:35.768 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 53
11-24 21:30:35.786 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 56
11-24 21:30:35.804 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 59
11-24 21:30:35.822 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 62
11-24 21:30:35.840 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 65
11-24 21:30:35.858 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 67
11-24 21:30:35.877 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 70
11-24 21:30:35.895 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 72
11-24 21:30:35.913 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 75
11-24 21:30:35.931 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 77
11-24 21:30:35.951 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 80
11-24 21:30:35.969 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 82
11-24 21:30:35.987 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 84
11-24 21:30:36.004 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 86
11-24 21:30:36.022 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 88
11-24 21:30:36.040 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 90
11-24 21:30:36.058 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 91
11-24 21:30:36.076 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 93
11-24 21:30:36.095 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 94
11-24 21:30:36.112 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 95
11-24 21:30:36.130 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 96
11-24 21:30:36.148 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 97
11-24 21:30:36.167 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 98
11-24 21:30:36.185 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 99
11-24 21:30:36.203 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 99
11-24 21:30:36.221 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 99
11-24 21:30:36.239 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 99
11-24 21:30:36.257 12224-12224/com.test E/com.test.TestActivity-->lambda$onCreate$0$TestActivity: 100
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

正常了,哈哈

有错误的地方请指出,谢谢大家!

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Android ListView 实现商品倒计时时,常见的时间错乱问题是由于 ListView 的视图复用机制导致的。当 ListView 滑动时,由于视图的复用,导致原本应该显示的倒计时时间被重置,出现时间错乱问题解决这个问题方法是在 getView 方法中对倒计时时间进行保存和更新。具体实现如下: 1. 在 Bean 类中添加一个 long 类型的字段用于保存商品的倒计时时间戳: ``` public class GoodsBean { ... private long countdownTime; // 倒计时时间戳 ... // getter 和 setter 方法 } ``` 2. 在 Adapter 的 getView 方法中对倒计时时间进行保存和更新。首先获取当前商品的倒计时时间,如果该商品的倒计时时间戳已经保存,则直接使用保存的时间戳进行倒计时更新;否则,根据商品的截止时间计算倒计时时间戳并保存到商品的 countdownTime 字段中: ``` public View getView(int position, View convertView, ViewGroup parent) { ... GoodsBean goods = getItem(position); long countdownTime = goods.getCountdownTime(); if (countdownTime == 0) { countdownTime = calculateCountdownTime(goods.getEndTime()); // 根据商品的截止时间计算倒计时时间戳 goods.setCountdownTime(countdownTime); // 保存倒计时时间戳 } ... TextView tvCountdown = convertView.findViewById(R.id.tv_countdown); updateCountdown(tvCountdown, countdownTime); // 更新倒计时显示 ... } private void updateCountdown(final TextView tvCountdown, long countdownTime) { ... // 倒计时更新逻辑 ... } ``` 通过这样的方式,可以在 ListView 的视图复用过程中正确地保存和更新商品的倒计时时间,解决倒计时错乱问题
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值