[Android] 안드로이드 뷰 전환

안녕하세요. 이번 포스팅에서는 액티비티 전환 시 뷰의 모션 애니메이션을 보여주는 기능에 대해 알아보도록 하겠습니다.

AnimationOptions의 하위 메서드인 MakeSceneTransitionAnimation은 전환이 발생할 때 전환할 활동으로 토글됩니다. 두 개의 동일한 뷰를 하나의 액티비티로 연결하여 애니메이션을 호출하는 기능입니다.

주로 어댑터의 항목을 클릭하여 해당 항목의 상세 정보를 볼 때 사용합니다.

먼저 실행 화면을 보겠습니다.


코드의 순서는 다음과 같습니다.

1. StartActivity 및 EndActivity 생성 및 레이아웃 구성

2. 전환 이름즉시 입력

삼. ActivityOptions.makeSceneTransitionAnimation 방법을 구현

4. 인텐트를 통해 전환할 활동으로 데이터 전달

5. 변환된 활동에서 전달된 데이터로 보기 만들기


코드 작성

activity_main.xml

<?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"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/imageViewMain"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:transitionName="imageTran"
        android:src="http://tekken5953.m/@android:mipmap/sym_def_app_icon"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/titleTextMain"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:gravity="center"
        android:text="타이틀"
        android:textSize="18sp"
        android:transitionName="titleTran"
        app:layout_constraintTop_toBottomOf="@id/imageViewMain" />

    <TextView
        android:id="@+id/contentTextMain"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:gravity="center"
        android:text="세부내용입니다. DevJYL Blog 화이팅"
        android:transitionName="contentTran"
        app:layout_constraintTop_toBottomOf="@+id/titleTextMain" />

    <Button
        android:id="@+id/btnMain"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:text="이동"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/contentTextMain" />


</androidx.constraintlayout.widget.ConstraintLayout>

activity_detail.xml

<?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"
    tools:context=".DetailActivity">

    <ImageView
        android:id="@+id/imageViewDetail"
        android:layout_width="match_parent"
        android:layout_height="250dp"
        android:transitionName="imageTran"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/titleTextDetail"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textSize="20sp"
        android:transitionName="titleTran"
        app:layout_constraintTop_toBottomOf="@+id/imageViewDetail" />

    <TextView
        android:id="@+id/contentTextDetail"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:transitionName="contentTran"
        android:gravity="center"
        app:layout_constraintTop_toBottomOf="@+id/titleTextDetail" />

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.kt

import android.app.ActivityOptions
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Bundle
import android.util.Pair
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import java.io.ByteArrayOutputStream


class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val title: TextView = findViewById(R.id.titleTextMain)
        val img: ImageView = findViewById(R.id.imageViewMain)
        val content: TextView = findViewById(R.id.contentTextMain)

        val btn: Button = findViewById(R.id.btnMain)
        btn.setOnClickListener {
            val options = ActivityOptions.makeSceneTransitionAnimation(
                this,
                Pair.create(title, "titleTran"),
                Pair.create(img, "imageTran"),
                Pair.create(content, "contentTran")
            )

            val intent = Intent(this, DetailActivity::class.java)
            intent.putExtra("title", title.text.toString())
            intent.putExtra("image", sendImage(android.R.mipmap.sym_def_app_icon))
            intent.putExtra("content", content.text.toString())
            startActivity(intent, options.toBundle())
        }
    }

    private fun sendImage(image: Int) : ByteArray {
        val sendBitmap = BitmapFactory.decodeResource(resources, image)
        val stream = ByteArrayOutputStream()
        sendBitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream)
        return stream.toByteArray()
    }
}

DetailActivity.kt

import android.graphics.BitmapFactory
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.graphics.drawable.toDrawable


class DetailActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_detail)

        val title: TextView = findViewById(R.id.titleTextDetail)
        val content: TextView = findViewById(R.id.contentTextDetail)
        val img: ImageView = findViewById(R.id.imageViewDetail)

        title.text = intent.extras?.getString("title")
        content.text = intent.extras?.getString("content")
        img.setImageDrawable(getImage())
    }

    private fun getImage() : Drawable {
        val arr = intent.getByteArrayExtra("image")
        return BitmapFactory.decodeByteArray(arr, 0, arr!!.size).toDrawable(resources)
    }
}

그럼 코드에 대한 간략한 소개를 드리겠습니다.

트랜지션 애니메이션을 등록하기 위해 뷰에 등록되는 고유 키 형태의 문자열 값입니다.

android:transitionName="imageTran"

액티비티 전환 옵션을 설정하는 부분입니다.

val options = ActivityOptions.makeSceneTransitionAnimation(
    this,
    Pair.create(title, "titleTran"),
    Pair.create(img, "imageTran"),
    Pair.create(content, "contentTran")
)

다수의 뷰를 등록해야 하는 경우 위와 같이 등록하고, 보유하고 있는 경우 다음과 같이 등록합니다.

val options = ActivityOptions.makeSceneTransitionAnimation(
    this,
    img,
    "ImageTran"
)

전환할 액티비티에 현재 데이터를 전달하는 부분입니다.

val intent = Intent(this, DetailActivity::class.java)
intent.putExtra("title", title.text.toString())
intent.putExtra("image", sendImage(android.R.mipmap.sym_def_app_icon))
intent.putExtra("content", content.text.toString())
startActivity(intent, options.toBundle())

sendImage(image: Int) 함수는 이미지에서 비트맵을 추출하여 ByteArray 유형으로 변환하는 로직입니다.

마지막으로 전환된 활동에서 데이터를 받는 부분입니다.

title.text = intent.extras?.getString("title")
content.text = intent.extras?.getString("content")
img.setImageDrawable(getImage())

getImage() 함수는 이미지의 ByteArray를 비트맵으로 변환하고 비트맵의 Drawable을 반환하는 로직입니다.

이것으로 나는 활동을 전환할 때 보기의 움직이는 애니메이션을 구현했습니다.

예제와 같이 간단한 애니메이션이 필요할 때 유용할 것 같습니다. 감사해요

샘플 코드

https://github.com/tekken5953/ViewTransitionAnimExam

GitHub – tekken5953/ViewTransitionAnimExam

GitHub에서 계정을 만들어 tekken5953/ViewTransitionAnimExam 개발에 기여하세요.

github.com