Skip to content

순차적으로 테두리 채우는 애니메이션

HyeonSeongKang edited this page Aug 29, 2023 · 1 revision

안녕하세요. 안드로이드 팀의 강현성 입니다.

이번 프로젝트에서 순차적으로 테두리를 채우는 애니메이션을 구현했는데 어떻게 구현했는지에 대해서 공유하려 합니다.

순차적으로 테두리 채워지는 애니메이션(가이드 모드)

layout 작성

먼저 4개의 뷰를 사용하여 각각의 경계선을 나타낸뒤 조건에 따라 보이거나 숨김 상태로 초기를 세팅합니다. (상, 하, 좌, 우)

        <!-- Bottom line -->
        <View
            android:id="@+id/v_bottom_line"
            android:layout_width="match_parent"
            android:layout_height="2dp"
            android:background="@drawable/shape_bottom_border_line"
            android:visibility='@{is_visible == 0 || current_type == "SelfMode" ? View.INVISIBLE : View.VISIBLE}'
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent" />

        <!-- Right line -->
        <View
            android:id="@+id/v_right_line"
            android:layout_width="2dp"
            android:layout_height="match_parent"
            android:background="@drawable/shape_right_border_line"
            android:visibility='@{is_visible == 0 || current_type == "SelfMode" ? View.INVISIBLE : View.VISIBLE}'
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <!-- Top line -->
        <View
            android:id="@+id/v_top_line"
            android:layout_width="match_parent"
            android:layout_height="2dp"
            android:background="@drawable/shape_top_border_line"
            android:visibility='@{is_visible == 0 || current_type == "SelfMode" ? View.INVISIBLE : View.VISIBLE}'
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <!-- Left line -->
        <View
            android:id="@+id/v_left_line"
            android:layout_width="2dp"
            android:layout_height="match_parent"
            android:background="@drawable/shape_left_border_line"
            android:visibility='@{is_visible == 0 || current_type == "SelfMode" ? View.INVISIBLE : View.VISIBLE}'
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

애니메이션 구현

  1. 애니메이션 초기화: 현재 실행 중인 모든 애니메이션을 취소하여 충돌을 방지합니다.
        binding.vBottomLine.animate().cancel()
        binding.vTopLine.animate().cancel()
        binding.vLeftLine.animate().cancel()
        binding.vRightLine.animate().cancel()
  1. 경계선 초기 상태 설정: 각 경계선의 스케일 값을 초기화하여 애니메이션을 시작하기 전의 상태로 설정합니다.
        binding.vBottomLine.scaleX = 0f
        binding.vTopLine.scaleX = 0f
        binding.vLeftLine.scaleY = 0f
        binding.vRightLine.scaleY = 0f
  1. 애니메이션 순서 및 동작:

📍 Pivot: View에서의 회전 또는 확대/축소의 중심점을 나타냅니다. 예를 들어, pivotX가 0f일 때 view는 X축에서 왼쪽 끝을 중심으로 확대/축소됩니다.

🔍 scaleX / scaleY: View의 가로 및 세로 크기를 확대/축소하는 데 사용되는 값입니다. 1f는 원래 크기를 나타내며, 0.5f는 원래 크기의 절반을 나타냅니다.

  • 상단 경계선 애니메이션: pivotX를 0f로 설정하고, 가로 스케일을 확장합니다.
binding.vTopLine.pivotX = 0f
binding.vTopLine.animate().setDuration(500).scaleX(1f)
  • 우측 경계선 애니메이션: 이전 애니메이션 완료 후, pivotY를 0f로 설정하고, 세로 스케일을 확장합니다.
binding.vRightLine.pivotY = 0f
binding.vRightLine.animate().setDuration(500).scaleY(1f)
  • 하단 경계선 애니메이션: 이전 애니메이션 완료 후, pivotX를 경계선의 너비로 설정하고, 가로 스케일을 확장합니다.
binding.vBottomLine.pivotX = binding.vBottomLine.width.toFloat()
binding.vBottomLine.animate().setDuration(500).scaleX(1f)
  • **좌측 경계선 애니메이션:**이전 애니메이션 완료 후, pivotY를 경계선의 높이로 설정하고, 세로 스케일을 확장합니다.
binding.vLeftLine.pivotY = binding.vLeftLine.height.toFloat()
binding.vLeftLine.animate().setDuration(500).scaleY(1f)
  1. 반환 값: 함수는 마지막 애니메이션(좌측 경계선 애니메이션)의 ViewPropertyAnimator를 반환합니다.(withEndAction 사용하기 위해)
return binding.vLeftLine.animate()

전체코드

    fun animateBorder(): ViewPropertyAnimator {
        binding.vBottomLine.animate().cancel()
        binding.vTopLine.animate().cancel()
        binding.vLeftLine.animate().cancel()
        binding.vRightLine.animate().cancel()

        binding.vBottomLine.scaleX = 0f
        binding.vTopLine.scaleX = 0f
        binding.vLeftLine.scaleY = 0f
        binding.vRightLine.scaleY = 0f

        binding.vBottomLine.visibility = visibility
        binding.vTopLine.visibility = visibility
        binding.vLeftLine.visibility = visibility
        binding.vRightLine.visibility = visibility

        binding.vTopLine.pivotX = 0f
        binding.vTopLine.animate()
            .setDuration(500)
            .scaleX(1f)
            .withEndAction {
                binding.vRightLine.pivotY = 0f
                binding.vRightLine.animate()
                    .setDuration(500)
                    .scaleY(1f)
                    .withEndAction {
                        binding.vBottomLine.pivotX = binding.vBottomLine.width.toFloat()
                        binding.vBottomLine.animate()
                            .setDuration(500)
                            .scaleX(1f)
                            .withEndAction {
                                binding.vLeftLine.pivotY = binding.vLeftLine.height.toFloat()
                                binding.vLeftLine.animate()
                                    .setDuration(500)
                                    .scaleY(1f)
                            }
                    }
            }

        return binding.vLeftLine.animate()  // 마지막 애니메이션의 애니메이터 반환
    }
Clone this wiki locally