Android

[Android] 프래그먼트 뷰 캡처해서 이미지로 저장하기

76 2023. 8. 17. 00:45
728x90

안드로이드 어플리케이션 화면의 특정 부분을 이미지로 저장하는 방법에 대해 알아보자.

 

앱을 개발하다 보면 사용자 인터페이스의 특정 영역을 이미지로 저장하는 기능이 필요할 때가 있다.

예를 들어, 그림 그리기 앱에서 사용자가 그린 그림을 저장하거나, 특정 정보가 표시된 프래그먼트의 내용을 이미지 파일로 저장하는 경우이다. 해당 게시글에서는 바로 이러한 안드로이드 영역 캡처에 대해 서술할 것이다.

 

예시를 위해 아래와 같은 프래그먼트를 만들어 보았다.

 

 

우리는 여기서 First 버튼을 누르면 Dashboard 프래그먼트의 모든 뷰를 이미지로 저장할 것이고, Second버튼을 누르면 하늘색 영역만 저장할 것이다.

 

Dashboard 프래그먼트의 xml코드는 아래와 같다.

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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=".ui.dashboard.DashboardFragment"
    android:orientation="vertical"
    android:background="@color/purple_200">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="500dp"
        android:background="@color/teal_200"
        android:orientation="vertical"
        android:id="@+id/layout2">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Second"
            android:textSize="50dp"
            android:layout_gravity="center"/>

    </LinearLayout>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="First"
        android:textSize="40dp"
        android:layout_gravity="center"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:orientation="vertical">

        <Button
            android:id="@+id/btn_first"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="First" />

        <Button
            android:id="@+id/btn_second"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="Second" />

    </LinearLayout>


</LinearLayout>

 

화면을 이미지로 만드는 과정은 아래 2단계를 거쳐 진행된다.

 

1. 뷰를 비트맵으로 변환한다.

private fun viewToBitmap(view: View): Bitmap {
    val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(bitmap)
    view.draw(canvas)
    return bitmap
}

 

2. 비트맵을 이미지로 저장한다. (MediaStore 활용)

private fun saveNoteImageToMediaStore(bitmap: Bitmap) {
    val displayName = "note_image_" + SimpleDateFormat(
        "yyyyMMdd_HHmmss",
        Locale.getDefault()
    ).format(Date()) + ".png"

    val imageCollection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
    } else {
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI
    }

    val contentValues = ContentValues().apply {
        put(MediaStore.Images.Media.DISPLAY_NAME, displayName)
        put(MediaStore.Images.Media.MIME_TYPE, "image/png")
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            put(
                MediaStore.Images.Media.RELATIVE_PATH,
                Environment.DIRECTORY_PICTURES + File.separator + "YourAppDirectoryName"
            )
        }
    }

    val contentResolver = requireContext().contentResolver
    var imageUri: Uri? = null

    try {
        imageUri = contentResolver.insert(imageCollection, contentValues)
        imageUri?.let {
            val outputStream: OutputStream? = contentResolver.openOutputStream(it)
            outputStream?.use { stream ->
                bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream)
                Toast.makeText(requireContext(), "이미지가 저장되었습니다.", Toast.LENGTH_SHORT).show()
            }
        }
    } catch (e: IOException) {
        e.printStackTrace()
    }
}

 

이 두 코드를 사용하여 각 버튼에 대한 이벤트를 정의한다.

이때 layout2는 Dashboard 프래그먼트의 하늘색 영역을 의미하며 layout2 라는 아이디를 xml에서 부여했다.

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    val layout2 = binding.layout2 // layout2 LinearLayout 가져오기
    
	//First버튼이 눌릴 경우
    binding.btnFirst.apply {
        setOnClickListener{
            val bitmap = viewToBitmap(binding.root) // 프래그먼트의 뷰 전체를 Bitmap으로 변환
            saveNoteImageToMediaStore(bitmap) // Bitmap을 저장
        }
    }

	//Second버튼이 눌릴 경우
    binding.btnSecond.apply {
        setOnClickListener{
            val bitmap = viewToBitmap(layout2) // 프래그먼트의 뷰의 layout2 부분만 Bitmap으로 변환
            saveNoteImageToMediaStore(bitmap) // Bitmap을 저장
        }
    }

}

 

이제 버튼을 눌러 제대로 저장이 되는지 확인해보자.

버튼을 누르면 아래와 같이 토스트 메시지와 함께 이미지가 저장된다.

 

 

갤러리로 이동하여 확인하면 아래와 같이 2장의 이미지가 생성된 것을 확인 할 수 있다.