구글 지도가 포함된 어플리케이션을 만들 때, 지도 fragment를 캡쳐해서 이미지로 저장하고 싶은 경우가 있습니다.
그런데 구글링을 통해 뷰에서 비트맵을 가져오는 방식으로 했더니, 검은 화면만 나오게 되더라구요.
알고보니 구글 맵 API에 이미 좋은 기능이 탑재되어 있었고, 간단하게 사용할 수 있었습니다.
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가 실행되겠죠?
다음 단계에서 구현을 해줄게요!
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를 달아주고,
중괄호 안에 캡쳐 동작을 감싸서 넣어줍니다!
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()
}
}
전달된 비트맵을 이미지 파일로 저장해주는 함수입니다.