lottie android min sdk,跳一个Lottie无法回调开始播放动画事件的坑
一、背景介紹
項目背景是在界面中彈出一個浮層動畫,同時播放一個音效。
二、當前實現
實現思路比較簡單:繼承一個DialogFragment,在相關的生命周期方法onViewCreated中調用startLottieAnim進行動畫播放,同時監聽lottie動畫播放的回調事件,在動畫開始播放時播放音效文件;動畫播放結束時關閉DialogFragment。
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
29private void startLottieAnim(String assetFolder, final Uri voiceUri){
lottieAnimationView.setImageAssetsFolder(assetFolder + "/images");
lottieAnimationView.setAnimation(assetFolder + "/anim.json");
lottieAnimationView.setRepeatCount(0);
lottieAnimationView.addAnimatorListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation){
playAnimVoice(voiceUri);
}
@Override
public void onAnimationEnd(Animator animation){
stopAnimVoice();
dismiss();
}
@Override
public void onAnimationCancel(Animator animation){
stopAnimVoice();
dismiss();
}
@Override
public void onAnimationRepeat(Animator animation){
playAnimVoice(voiceUri);
}
});
lottieAnimationView.playAnimation();
}
三、發現問題
對于以上的代碼,實際運行起來會發現動畫播放的同時并不能播放音頻,而且播放結束也不會自動消失。也就是說onAnimationStart和onAnimationEnd方法并沒有被回調。這是為什么呢?看lottie實現源碼 (BaseLottieAnimator),會發現
1
2
3
4
5
6
7
8
9void notifyStart(boolean isReverse){
for (Animator.AnimatorListener listener : listeners) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
listener.onAnimationStart(this, isReverse);
} else {
listener.onAnimationStart(this);
}
}
}
1
2
3
4
5
6
7
8void notifyEnd(boolean isReverse){
for (Animator.AnimatorListener listener : listeners) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
listener.onAnimationEnd(this, isReverse);
} else {
listener.onAnimationEnd(this);
}
}
在通知動畫開始和結束的時候,會根據系統版本決定走哪個方法,這里需要注意方法簽名。由于之前代碼只寫了一個參數的方法回調,因此在Android O及以上版本中會走另一個方法回調。因此,首先想到需要在注冊的AnimatorListener中添加之前遺漏的兩個方法回調。
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{
lottieAnimationView.setImageAssetsFolder(assetFolder + "/images");
lottieAnimationView.setAnimation(assetFolder + "/anim.json");
lottieAnimationView.setRepeatCount(0);
lottieAnimationView.addAnimatorListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation){
playAnimVoice(voiceUri);
}
@Override
public void onAnimationStart(Animator animation, boolean isReverse){
playAnimVoice(voiceUri);
}
@Override
public void onAnimationEnd(Animator animation){
stopAnimVoice();
dismiss();
}
@Override
public void onAnimationEnd(Animator animation, boolean isReverse){
stopAnimVoice();
dismiss();
}
@Override
public void onAnimationCancel(Animator animation){
stopAnimVoice();
dismiss();
}
@Override
public void onAnimationRepeat(Animator animation){
playAnimVoice(voiceUri);
}
});
lottieAnimationView.playAnimation();
}
修改之后運行會發現,動畫播放結束的回調事件成功接收了,但是音效依然無法播放。也就是onAnimationStart方法依然未被回調。繼續看下源碼:在LottieAnimationView.java中
1
2
3
4
5
6
7
8public void playAnimation(){
if (isShown()) {
lottieDrawable.playAnimation();
enableOrDisableHardwareLayer();
} else {
wasAnimatingWhenNotShown = true;
}
}
如果isShown判斷成功,會繼續走LottieDrawable.java中的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public void playAnimation(){
if (compositionLayer == null) {
lazyCompositionTasks.add(new LazyCompositionTask() {
@Override
public void run(LottieComposition composition){
playAnimation();
}
});
return;
}
if (systemAnimationsEnabled || getRepeatCount() == 0) {
animator.playAnimation();
}
if (!systemAnimationsEnabled) {
setFrame((int) (getSpeed() < 0 ? getMinFrame() : getMaxFrame()));
}
}
進而走到LottieValueAnimator.java中的playAnimation
1
2
3
4
5
6
7
8
9@MainThread
public void playAnimation(){
running = true;
notifyStart(isReversed());
setFrame((int) (isReversed() ? getMaxFrame() : getMinFrame()));
lastFrameTimeNs = 0;
repeatCount = 0;
postFrameCallback();
}
在這里看到了熟悉的notifyStart方法。可是我們并沒有收到對應的回調,可以想到LottieAnimationView.java中并沒有走isShown判斷分支,也就是說走了
1
2
3
4
5
6
7
8public void playAnimation(){
if (isShown()) {
lottieDrawable.playAnimation();
enableOrDisableHardwareLayer();
} else {
wasAnimatingWhenNotShown = true;
}
}
只是標記了一個狀態,標明lottie動畫還未顯示出來。那它為什么后來又能播放動畫呢?在LottieAnimationView.java中有監聽可見性回調的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19protected void onVisibilityChanged(@NonNull View changedView, int visibility){
// This can happen on older versions of Android because onVisibilityChanged gets called from the
// constructor of View so this will get called before lottieDrawable gets initialized.
// https://github.com/airbnb/lottie-android/issues/1143
if (lottieDrawable == null) {
return;
}
if (isShown()) {
if (wasAnimatingWhenNotShown) {
resumeAnimation();
wasAnimatingWhenNotShown = false;
}
} else {
if (isAnimating()) {
pauseAnimation();
wasAnimatingWhenNotShown = true;
}
}
}
當lottieview從不可見到可見時,會根據wasAnimatingWhenNotShown之前記錄的這個狀態去resumeAnimation。在resumeAnimation方法一路跟下去最后會走到LottieValueAnimator.java的
1
2
3
4
5
6
7
8
9
10public void resumeAnimation(){
running = true;
postFrameCallback();
lastFrameTimeNs = 0;
if (isReversed() && getFrame() == getMinFrame()) {
frame = getMaxFrame();
} else if (!isReversed() && getFrame() == getMaxFrame()) {
frame = getMinFrame();
}
}
這里可以發現并沒有像startAnimation那樣的notifyXXX的回調方法了。因此我們收不到onAnimationStart的方法回調了。
至此弄清楚了異常的原因,如何修改就很簡單了。現在已經能猜到是布局加載之后,lottieview還沒有渲染出來我們就去startAnimation導致回調無法走到,因此我們可以通過對lottieView進行post或者通過監聽viewTreeObserver的事件再進行startPlayAnimation操作就行了。
四、總結
1、回調方法要寫完整;
2、如果彈框一開始就要顯示lottie動畫,需要等ui控件可見之后再播放動畫
總結
以上是生活随笔為你收集整理的lottie android min sdk,跳一个Lottie无法回调开始播放动画事件的坑的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 撩爆男朋友的土味情话165个
- 下一篇: android5.0 v4a,【图】多图