[코틀린 안드로이드] 구글 맵 캡쳐 버튼 만들기

구글 지도가 포함된 어플리케이션을 만들 때, 지도 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 객체를 요청한다.

파라미터로 this를 써서 현재 객체의 OnMapReadyCallback이 콜백으로 등록했다.
지도 객체가 준비되면, OnMapReadyCallback의 구현된 onMapReady가 실행된다.

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

override fun onMapReady(googleMap: GoogleMap) {

        screenShotButton.setOnClickListener {
            googleMap.snapshot {
                it?.let{
                    saveMediaToStorage(it)
                }
            }
        }
}

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

스크린샷 버튼을 눌렀을 때, 캡쳐하기 위해서 버튼에 OnClickListener를 달아줄 것이다.
중괄호 안에 액션을 써주면 되므로, 지도를 캡쳐하는 코드를 써주면 된다.

지도 캡쳐는 googleMap 객체의 snapshot 메소드를 이용하면 된다.
파라미터로 SnapshotReadyCallback을 주는데, 람다형식으로 함수를 작성해주면 된다.
SnapshotReadyCallback의 onSnapshotReady는 Nullable Bitmap이 파라미터로 넘어온다.
it이라고 되어있는 것이 넘어온 Bitmap이다.

Nullable이니 ?. 세이프콜을 이용해 Null이 아닌 경우에만 저장을 하도록 한다.
saveMediaToStorage()는 비트맵을 받아서 저장하는 메소드이다. 구글에서 찾은 것을 아래에 쓴다.

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. 완성

이런 화면에서 보라색 스크린샷을 누르면 깔끔하게 캡쳐된다!

좋은 웹페이지 즐겨찾기