Android: Lottie Animation을 임의의 위치와 크기에 오버레이(ViewOverlay) 표시

2021/12/06 : 편집 참고 : Lottie 3.4.0에서는 다음과 같은 방법으로 잘 작동하지만 Lottie 3.6.0에서는 제대로 작동하지 않을 수 있습니다. Lottie 3.6.0 이상에서는 이 기사에 기재한 정책과 ViewGroupOverlay + View (foreground = LottieDrawable)의 조합으로 잘 움직였습니다. 향후 다시 검증할 시간이 걸리면 정보를 정리하고 ViewGroupOverlay에서의 대응을 기재할 예정입니다.

TL;DR


  • Lottie Animation을 ViewOverlay에 표시하여 View Tree에 영향을주지 않고 애니메이션을 표시합니다.
  • ViewOverlay는 Android 4.3 이상에서 사용할 수 있습니다.

    하고 싶은 일과 문제점



    Button 탭을 시작으로 Lottie에서 풍부한 애니메이션을 표시할 때 레이아웃의 Button 크기보다 넓은 범위에 애니메이션을 표시하고 싶을 수 있습니다.

    예를 들어 Twitter Android 앱의 즐겨찾기 Button UI를 탭하면 하트마크보다 넓은 범위에 애니메이션이 표시됩니다.





    이것을 XML 레이아웃상으로 표현하면 이하와 같은 레이아웃이 됩니다만, 이 방법에서는 Button 주위의 View 와 LottieAnimationView 가 입을 수 있거나, 외형상의 margin 와 XML 에서의 margin 지정이 어긋나 있어 레이아웃 배치에 어려움. 레이아웃이 겹치는 순서에도 문제가 있으므로 LottieAnimationView를 맨 앞으로 이동시키는 궁리도 필요합니다.

    이 레이아웃에서 A와 C 모두 애니메이션을 추가하면 XML이 어려울 것입니다 ...



    애니메이션을 XML로 표현할 때의 구성:
    <LinearLayout...>
        <Button.../>
        <FrameLayout...
            android:layout_marginTop="-14dp"
            android:layout_marginStart="-10dp"
            android:layout_marginEnd="-10dp">
            <Button.../>
            <com.airbnb.lottie.LottieAnimationView.../>
        </FrameLayout>
        <Button..../>
    </LinearLayout>
    

    솔루션: Lottie Animation을 ViewOverlay에 표시



    Android 4.3 이상에서는 ViewOverlay를 사용할 수 있습니다.

    ViewOverlay는 View의 맨 앞에 있는 레이어로 ViewOverlay에 임의의 View 또는 Drawable을 표시할 수 있습니다.
    ViewOverlay는 각 View에 존재합니다. ViewOverlay 는 그리기에만 사용되므로 탭 이벤트 등에는 반응하지 않습니다.

    Activity 및 Fragment 화면 전환 중 Shared Element Transition도 ViewOverlay에 의해 실현됩니다.

    ViewOverlay 사용법


  • 모든 View에 대해 view.overlay 로 액세스할 수 있습니다
  • 오버레이 표시할 때 view.overlay.add(Drawable) 또는 view.overlay.add(View) 에 따라 그리려는 요소 추가
  • Drawable 의 draw 위치는 drawable.bounds 로 지정한다

  • 오버레이 표시가 더 이상 필요하지 않으면 view.overlay.remove()에서 요소 삭제
  • Any view added to the overlay should be removed when it is no longer needed or no longer visible. htps : //에서 ゔぇぺぺr. 안 d로이 d. 코 m / 레후 렌세 / 안 d 로이 d / ぃ え w / ぃ ぃ え w )
  • 더 이상 필요하지 않으면 삭제해야한다고합니다


  • ViewOverlay + LottieDrawable 구현



    후술하는 PositionedLottieDrawable 를 이용해, 다음과 같이 구현합니다.
    // アニメーションの起点となる Button
    private val button: ToggleButton = ...
    // オーバーレイ表示したい View。 Button の Parent である場合が多い
    private val targetView: ViewGroup = (button.parent as! ViewGroup)
    private val drawable = PositionedLottieDrawable()
    private val compositionTask: LottieTask<LottieComposition>
    init {
        drawable.addAnimatorListener(object: Animator.AnimatorListener {
            override fun onAnimationStart(animation: Animator?) = Unit
            override fun onAnimationEnd(animation: Animator?) {
                // アニメーション終了で Drawable を Overlay から削除
                targetView.overlay.remove(drawable)
                // アニメーション終了後の状態 (Toggle ON/OFF) で表示
                button.alpha = 1f
            }
            override fun onAnimationCancel(animation: Animator?) = Unit
            override fun onAnimationRepeat(animation: Animator?) = Unit
        })
        compositionTask = LottieCompositionFactory
            .fromRawRes(context, R.raw.lottie_animation)
            .addListener {
                // 非同期で Lottie JSON を読み込み、drawable へ設定する
                drawable.composition = it
            }
    }
    fun clickButton(checked: Boolean) {
        if (checked) {
            // checked = true へ移行するアニメーション処理
    
            // より安全な実装とするには、ここで drawable.composition が読み込み済みであることを確認してください
            // 読み込みが完了していなければ compositionTask の終了を待ってからアニメーションを実行する必要があります
    
            // アニメーション中は Button を非表示
            // 表示したままでよければ alpha を変更する必要はない
            // アニメーション中にも Button タップ判定を拾いたいため、visibility ではなく alpha 変更
            button.alpha = 0f
            // アニメーション Drawable の座標を計算する
            // このサンプルでは Button とアニメーションが中央合わせとなるように計算している
            drawable.x = (button.x - (drawable.composition.bounds.width() - button.width) / 2)
            drawable.y = (button.y - (drawable.composition.bounds.height() - button.height) / 2)
            targetView.overlay.add(drawable)
            drawable.playAnimation()
        } else {
            // checked = false へ移行する実装は省略
            // button.alpha やアニメーションの処理を実装する
            // こちらにもアニメーションが必要なら、checked = true のアニメーションを停止してから
            // あたらしくアニメーションを開始したりする
        }
    }
    
    

    PositionedLottieDrawable 작업 라운드



    Lottie 3.6.1 시점에서, LottieDrawable 는 bounds 로 지정한 좌표에 렌더링 해 주지 않는 문제가 있습니다.
  • Issue #1079 htps : // 기주 b. 코 m / 아이 rb b / ぉ 치에 - 안 d 로이 d / 이스에 s / 1079

  • 이하의 PositionedLottieDrawable 에 의해, 묘화 좌표를 수정합니다.
    class PositionedLottieDrawable : LottieDrawable() {
        var x: Float = 0f
        var y: Float = 0f
        override fun draw(canvas: Canvas) {
            canvas.save()
            canvas.translate(x, y)
            super.draw(canvas)
            canvas.restore()
        }
    }
    

    ViewOverlay + LottieAnimationView에서 잘 작동하지 않음



    Lottie 3.6.1 시점에서 LottieAnimationView는 ViewOverlay에 추가해도 제대로 애니메이션되지 않습니다.

  • htps : // 기주 b. 코 m / 아이 rb b / ぉ 치 - 안 d 로이 d / b ぉ b / v3. 치에 / ぉ 치에 아니 마치 온 ゔ ぃ에 w. 그럼 # L613
  • LottieAnimationView.playAnimation ()은 View.isShown ()에서만 애니메이션을 시작하도록 구현되었습니다.
  • OverlayView에 추가 된 View는 View.isShown ()이 true가되지 않기 때문에 애니메이션이 시작되지 않습니다

  • 좋은 웹페이지 즐겨찾기