[안드로이드] 초간단 구글 맵 캡쳐 버튼 만들기

jibmin jung·2022년 4월 7일
0

구글 지도가 포함된 어플리케이션을 만들 때, 지도 fragment를 캡쳐해서 이미지로 저장하고 싶은 경우가 있습니다.

그런데 구글링을 통해 뷰에서 비트맵을 가져오는 방식으로 했더니, 검은 화면만 나오게 되더라구요.
알고보니 구글 맵 API에 이미 좋은 기능이 탑재되어 있었고, 간단하게 사용할 수 있었습니다.

1. 지도 객체 얻어오기

class MainActivity : AppCompatActivity(), OnMapReadyCallback {
	//캡쳐 버튼
	private val screenShotButton: AppCompatButton by lazy {
        findViewById<AppCompatButton>(R.id.screenShotButton)
    }
	...
    
    override fun onCreate(savedInstanceState: Bundle?) {
        
		// 프래그먼트를 xml에서 정적으로 추가한 경우
        val mapFragment = supportFragmentManager
            .findFragmentById(R.id.map) as SupportMapFragment

        mapFragment.getMapAsync(this)
        

먼저 지도 객체를 얻어와야 합니다.
지도 프래그먼트의 getMapAsync를 이용해서 GoogleMap 객체를 요청합니다.

MainActivity가 OnMapReadyCallBack을 구현하도록 해주고,
getMapAsync의 파라미터로 this를 써서 MainActivity의 OnMapReadyCallback가 콜백으로 실행되도록 해줍니다.

이제 지도 객체가 준비되면, MainActivity에 구현할 OnMapReadyCallback.onMapReady가 실행되겠죠?
다음 단계에서 구현을 해줄게요!

2. onMapReady에서 버튼에 리스너 달아주기

override fun onMapReady(googleMap: GoogleMap) {

        screenShotButton.setOnClickListener {
        	// 버튼 클릭하면 실행되는 부분
            googleMap.snapshot {
            	//SnapshotReadyCallback
                it?.let{
                    saveMediaToStorage(it)
                }
            }
        }
}

onMapReady의 파라미터로 넘어오는 googleMap을 이용해서 지도를 다룰 수 있게 해줍니다.
이 googleMap은 지도를 다루는데 계속 활용되기 때문에 잘 저장해두는 것이 좋아요.

지도 캡쳐는 googleMap 객체의 snapshot 메소드를 이용하면 됩니다.
파라미터로 SnapshotReadyCallback.onSnapShotReady을 주는데, 캡쳐가 준비되었을 때 실행될 동작을 여기에 작성해주면 돼요.

SnapshotReadyCallback.onSnapshotReady(Bitmap snapshot)의 인자는 Nullable이니 ?. 세이프콜을 이용해 Null이 아닌 경우에만 저장을 하도록 해주었어요.
saveMediaToStorage()는 비트맵을 받아서 저장하는 메소드입니다.

스크린샷 버튼을 눌렀을 때, 캡쳐하기 위해서 버튼에 OnClickListener를 달아주고,
중괄호 안에 캡쳐 동작을 감싸서 넣어줍니다!

3. saveMediaToStorage 메소드

private fun saveMediaToStorage(bitmap: Bitmap) {
        // Generating a file name
        val filename = "${System.currentTimeMillis()}.jpg"

        // Output stream
        var fos: OutputStream? = null

        // For devices running android >= Q
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            // getting the contentResolver
            this.contentResolver?.also { resolver ->

                // Content resolver will process the contentvalues
                val contentValues = ContentValues().apply {

                    // putting file information in content values
                    put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
                    put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg")
                    put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
                }

                // Inserting the contentValues to
                // contentResolver and getting the Uri
                val imageUri: Uri? = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)

                // Opening an outputstream with the Uri that we got
                fos = imageUri?.let { resolver.openOutputStream(it) }
            }
        } else {
            // These for devices running on android < Q
            val imagesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
            val image = File(imagesDir, filename)
            fos = FileOutputStream(image)
        }

        fos?.use {
            // Finally writing the bitmap to the output stream that we opened
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it)
            Toast.makeText(this , "Captured View and saved to Gallery" , Toast.LENGTH_SHORT).show()
        }
    }

전달된 비트맵을 이미지 파일로 저장해주는 함수입니다.

4. 완성

이런 화면에서 보라색 스크린샷을 누르면 깔끔하게 캡쳐됩니다!
profile
이것저것 안드로이드

0개의 댓글