이 화면의 받기/거절 버튼의 애니메이션을 만들어 보았다.
ViewBinding과 constraintlayout 을 넣었다.
buildFeatures { viewBinding true } dependencies { implementation 'androidx.constraintlayout:constraintlayout:2.0.4' }
화면의 레이아웃은 이렇게 잡았다. 애니메이션 시작, 종료 테스트를 위한 버튼과, 받기/거절의 위치를 조절하기 위한 빨간줄로 표시한 가이드라인용 뷰도 추가했다. 빨간줄은 실제 화면에서는 표시되지 않는다.

<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#00f" tools:context=".MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:orientation="horizontal" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <Button android:id="@+id/startButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="start" /> <Button android:id="@+id/endButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="5dp" android:text="stop" /> </LinearLayout> <androidx.constraintlayout.motion.widget.MotionLayout android:id="@+id/motionLayout" android:layout_width="match_parent" android:layout_height="wrap_content" app:layoutDescription="@xml/main_activity_scene" app:layout_constraintBottom_toBottomOf="parent"> <View android:id="@+id/guide1" android:layout_width="0dp" android:layout_height="1dp" app:layout_constraintEnd_toStartOf="@id/acceptButton" app:layout_constraintHorizontal_chainStyle="spread" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@id/acceptButton" app:layout_constraintWidth_percent="0.1" tools:background="#f00" /> <View android:id="@+id/guide2" android:layout_width="0dp" android:layout_height="1dp" app:layout_constraintEnd_toStartOf="@id/rejectButton" app:layout_constraintStart_toEndOf="@id/acceptButton" app:layout_constraintTop_toTopOf="@id/guide1" app:layout_constraintWidth_percent="0.3" tools:background="#f00" /> <View android:id="@+id/guide3" android:layout_width="0dp" android:layout_height="1dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/rejectButton" app:layout_constraintTop_toTopOf="@id/guide1" app:layout_constraintWidth_percent="0.1" tools:background="#f00" /> <ImageView android:id="@+id/bgAcceptButton" android:layout_width="60dp" android:layout_height="60dp" android:alpha="1" android:background="@drawable/bg_btn_call_normal" app:layout_constraintBottom_toBottomOf="@id/acceptButton" app:layout_constraintEnd_toEndOf="@id/acceptButton" app:layout_constraintStart_toStartOf="@id/acceptButton" app:layout_constraintTop_toTopOf="@id/acceptButton" /> <ImageView android:id="@+id/bgRejectButton" android:layout_width="60dp" android:layout_height="60dp" android:background="@drawable/bg_btn_call_normal" app:layout_constraintBottom_toBottomOf="@id/rejectButton" app:layout_constraintEnd_toEndOf="@id/rejectButton" app:layout_constraintStart_toStartOf="@id/rejectButton" app:layout_constraintTop_toTopOf="@id/rejectButton" /> <ImageView android:id="@+id/acceptButton" android:layout_width="60dp" android:layout_height="60dp" android:layout_marginBottom="100dp" android:background="@drawable/bg_btn_call" android:padding="15dp" android:src="@drawable/ic_call_accept" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/guide2" app:layout_constraintStart_toEndOf="@id/guide1" /> <ImageView android:id="@+id/rejectButton" android:layout_width="60dp" android:layout_height="60dp" android:background="@drawable/bg_btn_call" android:padding="15dp" android:src="@drawable/ic_call_reject" app:layout_constraintBottom_toBottomOf="@id/acceptButton" app:layout_constraintEnd_toStartOf="@id/guide3" app:layout_constraintStart_toEndOf="@id/guide2" app:layout_constraintTop_toTopOf="@id/acceptButton" /> </androidx.constraintlayout.motion.widget.MotionLayout> </androidx.constraintlayout.widget.ConstraintLayout>
그런 다음 애니메이션 scene을 넣어야하는데, res/xml 폴더에 scene 하나를 만든다.
Transition의 KeyAttribute는 속성값을 바꿀 수 있고, ConstraintSet은 크기를 바꿀 수 있다. 그래서 받기/거절과 같은 크기로 있던 반투명한 흰색 원이 점점 커지면서 투명해진다. 2초동안 커지고, 다시 반복한다.
<?xml version="1.0" encoding="utf-8"?> <MotionScene xmlns:android="http://schemas.android.com/apk/res/android" xmlns:motion="http://schemas.android.com/apk/res-auto"> <Transition motion:autoTransition="animateToEnd" motion:constraintSetEnd="@+id/end" motion:constraintSetStart="@id/start" motion:duration="2000"> <KeyFrameSet> <KeyAttribute android:alpha="0.4" motion:framePosition="60" motion:motionTarget="@+id/bgAcceptButton" /> <KeyAttribute android:alpha="0" motion:framePosition="100" motion:motionTarget="@+id/bgAcceptButton" /> <KeyAttribute android:alpha="0.4" motion:framePosition="70" motion:motionTarget="@+id/bgRejectButton" /> <KeyAttribute android:alpha="0" motion:framePosition="100" motion:motionTarget="@+id/bgRejectButton" /> </KeyFrameSet> </Transition> <Transition motion:autoTransition="animateToStart" motion:constraintSetEnd="@id/end" motion:constraintSetStart="@id/start" /> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/bgAcceptButton" android:layout_width="60dp" android:layout_height="60dp" motion:layout_constraintBottom_toBottomOf="@id/acceptButton" motion:layout_constraintEnd_toEndOf="@id/acceptButton" motion:layout_constraintStart_toStartOf="@id/acceptButton" motion:layout_constraintTop_toTopOf="@id/acceptButton" /> <Constraint android:id="@+id/bgRejectButton" android:layout_width="60dp" android:layout_height="60dp" motion:layout_constraintBottom_toBottomOf="@id/rejectButton" motion:layout_constraintEnd_toEndOf="@id/rejectButton" motion:layout_constraintStart_toStartOf="@id/rejectButton" motion:layout_constraintTop_toTopOf="@id/rejectButton" /> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@+id/bgAcceptButton" android:layout_width="100dp" android:layout_height="100dp" motion:layout_constraintBottom_toBottomOf="@id/acceptButton" motion:layout_constraintEnd_toEndOf="@id/acceptButton" motion:layout_constraintStart_toStartOf="@id/acceptButton" motion:layout_constraintTop_toTopOf="@id/acceptButton" /> <Constraint android:id="@+id/bgRejectButton" android:layout_width="100dp" android:layout_height="100dp" motion:layout_constraintBottom_toBottomOf="@id/rejectButton" motion:layout_constraintEnd_toEndOf="@id/rejectButton" motion:layout_constraintStart_toStartOf="@id/rejectButton" motion:layout_constraintTop_toTopOf="@id/rejectButton" /> </ConstraintSet> </MotionScene>
코드에서 할 일은 거의 없다.
class MainActivity : AppCompatActivity() { private lateinit var binding: MainActivityBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = MainActivityBinding.inflate(layoutInflater) setContentView(binding.root) binding.startButton.setOnClickListener { startAnimation() } binding.endButton.setOnClickListener { stopAnimation() } } private fun startAnimation() { binding.motionLayout.transitionToEnd() } private fun stopAnimation() { binding.motionLayout.progress = 0f } }
만든 예제는 https://github.com/susemi99/SamsungCallScreenSample 에 올려놨다.