IndeterminateProgressbar解析-Part 4

IndeterminateProgressbar解析-Part 4

IndeterminateProgressBar 能在用戶進行某項不定時長的耗時操作時提供絕佳的用戶體驗,之前我有教過大家怎么創建水平的 IndeterminateProgressBar,今天我就來教大家實現圓形的 IndeterminateProgressBar,這個控件將支持 API 11(Honeycomb)以上的設備。

在上一篇博文中,我們創建了一個自定義 Drawable - 它能繪制一段圓弧,并且能設置圓弧的起點和終點,以及完成圓弧的旋轉。接下來我們要為該 Drawable 添加 ObjectAnimator,使得這些值能夠隨時間改變,以完成我們期望的動畫。

在看代碼之前,我需要指出的是:我并不喜歡創建一個工具類,然后在里面放一大堆靜態方法 - 因為它們不代表具體的對象,那么在面向對象編程中也就不能擁有清晰的職責。但在這里我還是創建了一個工具類,將 Animator 的構建過程從 Drawable 中剝離,使代碼更可讀。

final class IndeterminateAnimatorFactory {
    private static final String START_ANGLE = "startAngle";
    private static final String END_ANGLE = "endAngle";
    private static final String ROTATION = "rotation";

    private static final int SWEEP_DURATION = 1333;
    private static final int ROTATION_DURATION = 6665;
    private static final float END_ANGLE_MAX = 360;
    private static final float START_ANGLE_MAX = END_ANGLE_MAX - 1;
    private static final int ROTATION_END_ANGLE = 719;

    private IndeterminateAnimatorFactory() {
        // NO-OP
    }

    public static Animator createIndeterminateDrawableAnimator(IndeterminateDrawable drawable) {
        AnimatorSet animatorSet = new AnimatorSet();
        Animator startAngleAnimator = createStartAngleAnimator(drawable);
        Animator sweepAngleAnimator = createSweepAngleAnimator(drawable);
        Animator rotationAnimator = createAnimationAnimator(drawable);
        animatorSet.playTogether(startAngleAnimator, sweepAngleAnimator, rotationAnimator);
        return animatorSet;
    }
    .
    .
    .
}

類的實現非常簡單,就是創建三個 Animator,并把它們添加到 AnimatorSet 中,然后同時播放。

final class IndeterminateAnimatorFactory {
    .
    .
    .
    private static Animator createStartAngleAnimator(IndeterminateDrawable drawable) {
        ObjectAnimator animator = ObjectAnimator.ofFloat(drawable, START_ANGLE, 0f, START_ANGLE_MAX);
        animator.setDuration(SWEEP_DURATION);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setRepeatMode(ValueAnimator.RESTART);
        animator.setInterpolator(createStartInterpolator());
        return animator;
    }

    private static Interpolator createStartInterpolator() {
        return new LinearInterpolator();
    }

    private static Animator createSweepAngleAnimator(IndeterminateDrawable drawable) {
        ObjectAnimator animator = ObjectAnimator.ofFloat(drawable, END_ANGLE, 0f, END_ANGLE_MAX);
        animator.setDuration(SWEEP_DURATION);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setRepeatMode(ValueAnimator.RESTART);
        animator.setInterpolator(createEndInterpolator());
        return animator;
    }

    private static Interpolator createEndInterpolator() {
        return new LinearInterpolator();
    }
    .
    .
    .
}

這兩個 Animator 會控制圓弧的起點和終點,SWEEP_DURATION 是從 AOSP 實現的代碼中直接拷過來的。創建插值器的方法會在后面實現一些有趣的效果。

然后是展示旋轉動畫的 Animator:

final class IndeterminateAnimatorFactory {
    .
    .
    .
    private static Animator createAnimationAnimator(IndeterminateDrawable drawable) {
        ObjectAnimator rotateAnimator = ObjectAnimator.ofFloat(drawable, ROTATION, 0, ROTATION_END_ANGLE);
        rotateAnimator.setDuration(ROTATION_DURATION);
        rotateAnimator.setRepeatMode(ValueAnimator.RESTART);
        rotateAnimator.setRepeatCount(ValueAnimator.INFINITE);
        rotateAnimator.setInterpolator(new LinearInterpolator());
        return rotateAnimator;
    }
    .
    .
    .
}

ROTATION_DURATION 也是從 AOSP 實現中拷貝的,在這里我們使用了線性插值器,因為我們需要規則的旋轉。

現在需要更新 IndeterminateDrawable 中的 createAnimator() 方法,用上這些 Animator:

public class IndeterminateDrawable extends Drawable implements Animatable {
    .
    .
    .
    private void createAnimator() {
        animator = IndeterminateAnimatorFactory.createIndeterminateDrawableAnimator(this);
    }
    .
    .
    .
}

運行的話就會看到一條旋轉的線:

video

因為起點和終點使用相同的線性插值器,所以他們移動的步調一致。又由于方形覆蓋塊的起點和終點使用相同的值,所以終點會稍微繪制地比圓弧的長度要早一些。

這一點可以通過使用不同的插值器優化:

final class IndeterminateAnimatorFactory {
    .
    .
    .
    private static Interpolator createStartInterpolator() {
        return new DecelerateInterpolator();
    }
    .
    .
    .
    private static Interpolator createEndInterpolator() {
        return new AccelerateInterpolator();
    }
    .
    .
    .
}

現在起點會很快達到終點的位置,然后慢慢恢復,而終點則會慢慢開始,然后逐漸加速,直到到達終點,具體效果看視頻:

video

看起來很像了,但我們還能優化!

如果我們把旋轉動畫去掉的話,能更好地感受剛剛的代碼實現的效果,以及了解旋轉動畫的效果:

video

在這系列的最后一篇博文中我會使用更多的插值器,讓大家了解插值器的魔力。

源碼可以在這里下載


所屬標簽

無標簽

25选5玩法中奖