Android - Google Map API 설치부터 심화 활용까지 (커스텀 클러스터링) (4) (Kotlin)
업데이트:
- 연구주제 : Android - Google Map 활용 (Kotlin)
- 연구목적 : 안드로이드에서의 코틀린 활용
- 연구일시 : 2020년 04월 09일 09:00~17:00
- 연구자 : 이재환 ljh951103@naver.com
- 연구장비 : HP EliteDesk 800 G4 TWR, Kotlin, Android studio, IntelliJ
- 관련연구 : Java, Android, Kotlin, Google, API
서론
이번 포스팅에서는 지난 포스팅에서 구현했던 커스텀 마커와 클러스터링을 동시에 사용하여 다음과 같은 화면을 구현해보도록 하겠다.
본론
boundmap
다음의 함수를 사용하면 클러스터링에 삽입된 마커들을 모아준다.
즉, 마커들의 좌표들을 모으고 그 마커들을 다 보일 수 있도록 카메라 줌을 초기화한다.
fun boundmap() {
val bounds: LatLngBounds = builder.build()
mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, ZoomLevel))
val zoom: Float = mMap.getCameraPosition().zoom - 0.5f
mMap.animateCamera(CameraUpdateFactory.zoomTo(zoom))
}
addLatLNgData
addLatLNgData
는 클러스터링에 정의한 데이터들을 추가해준다.
fun addLatLNgData(id : Long, latlng : LatLng) {
val data = LatLngData(index++, id, latlng)
mClusterManager.addItem(data)
builder.include(data.latlng)
}
MarkerClusterRenderer
MarkerClusterRenderer
클래스는 클러스터링의 여러가지 옵션을 정의한다.
onBeforeClusterItemRendered
함수의 경우, markerOption
을 통하여 커스텀 마커가 가능하다.
createDrawableFromView
의 경우 저번 포스팅에서 커스텀 마커를 구현했을 때의 함수와 같이 뷰를 비트맵으로 변환시켜주는 함수이다.
onClustersChanged
는 마커를 클릭한 후, 마커가 클러스터로 변환될 경우, 적용된 렌더링이 풀리기 때문에 정의해준다.
class MarkerClusterRenderer(context: Context?, map: GoogleMap?, clusterManager: ClusterManager<LatLngData>?,
marker_view: View) : DefaultClusterRenderer<LatLngData>(context, map, clusterManager) {
private val context = context
private var marker_view = marker_view
private lateinit var tag_marker: TextView
override fun onBeforeClusterItemRendered(item: LatLngData, markerOptions: MarkerOptions) { // 5
tag_marker = marker_view.findViewById(R.id.tag_marker) as TextView
tag_marker.text = MediaStore_Dao.getNameById(context!!, item.id)
markerOptions.icon( BitmapDescriptorFactory.fromBitmap(createDrawableFromView(context, marker_view)) )
}
override fun onClustersChanged(clusters: MutableSet<out Cluster<LatLngData>>?) {
super.onClustersChanged(clusters)
if(selectedMarker != null) {
selectedMarker!!.setIcon(BitmapDescriptorFactory.fromBitmap(createDrawableFromView(context, marker_view)))
selectedMarker = null
}
}
companion object {
fun createDrawableFromView(context: Context?, view: View): Bitmap {
val displayMetrics = DisplayMetrics()
(context as Activity).windowManager.defaultDisplay.getMetrics(displayMetrics)
view.layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
view.measure(displayMetrics.widthPixels, displayMetrics.heightPixels)
view.layout(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels)
view.buildDrawingCache()
val bitmap: Bitmap = Bitmap.createBitmap(
view.measuredWidth,
view.measuredHeight,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
view.draw(canvas)
return bitmap
}
}
}
changeRenderer
여기서는 마커를 클릭했을 때, 렌더링을 서로 달리하기위함이다.
커스텀 마커를 구현했을 때와 같다.
private fun changeRenderer(item: LatLngData) {
if(selectedMarker != clusterRenderer.getMarker(item)) {
if (selectedMarker != null) {
tag_marker.setTextColor(Color.BLACK)
tag_marker.setBackgroundResource(R.drawable.ic_marker_phone)
selectedMarker!!.setIcon(
BitmapDescriptorFactory.fromBitmap(
createDrawableFromView(this, marker_view)
)
)
}
if (clusterRenderer.getMarker(item) != null) {
tag_marker.setTextColor(Color.WHITE)
tag_marker.setBackgroundResource(R.drawable.ic_marker_phone_blue)
tag_marker.text = MediaStore_Dao.getNameById(this, item.id)
clusterRenderer.getMarker(item).setIcon(
BitmapDescriptorFactory.fromBitmap(
createDrawableFromView(
this,
marker_view
)
)
)
selectedMarker = clusterRenderer.getMarker(item)
tag_marker.setTextColor(Color.BLACK)
tag_marker.setBackgroundResource(R.drawable.ic_marker_phone)
}
}
}
clusterClick
클러스터를 클릭했을 때의 리스너를 정해준다.
이것을 bound()
와 마찬가지로 카메라 줌을 맞춰준다.
private fun clusterClick(mMap: GoogleMap) {
mClusterManager.setOnClusterClickListener { cluster ->
// 클러스터 클릭 리스너
val builder_c: LatLngBounds.Builder = LatLngBounds.builder()
for (item in cluster.items) {
if (item != null) {
builder_c.include(item.position)
}
}
val bounds_c: LatLngBounds = builder_c.build()
mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds_c, ZoomLevel))
val zoom: Float = mMap.cameraPosition.zoom - 0.5f
mMap.animateCamera(CameraUpdateFactory.zoomTo(zoom))
true
}
}
clusterItemClick
그리고 마커를 클릭했을 때의 리스너도 달아준다.
private fun clusterItemClick(mMap: GoogleMap) {
mClusterManager.setOnClusterItemClickListener { p0 ->
// // 클러스터 아이템 클릭 리스너
val center: CameraUpdate = CameraUpdateFactory.newLatLng(p0?.position)
mMap!!.animateCamera(center)
Log.d("qweqwe","wqe")
changeRenderer(p0)
true
}
}
onMapReady
마지막으로 onMapReady
를 재정의 해준다.
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
mMap.uiSettings.isMyLocationButtonEnabled = true;
mMap.uiSettings.isZoomControlsEnabled = true;
boundmap()
mClusterManager = ClusterManager<LatLngData>(this, mMap)
clusterRenderer = MarkerClusterRenderer(this,mMap, mClusterManager, marker_view)
mClusterManager.setRenderer(clusterRenderer)
mMap.setOnCameraChangeListener (mClusterManager)
mMap.setOnMarkerClickListener(mClusterManager)
mMap.setOnInfoWindowClickListener { }
mMap.setOnMapClickListener {
card_view.visibility = View.GONE
if(selectedMarker != null) {
selectedMarker!!.setIcon(BitmapDescriptorFactory.fromBitmap(createDrawableFromView(this, marker_view)))
selectedMarker = null
}
}
clusterItemClick(mMap)
clusterClick(mMap)
}
이제 맵을 각자의 프로젝트에 맞게 설정해서 실행시키면 이런식으로 클러스터링에 커스텀 마커를 적용시켜서 사용할 수 있다.
결론
이제 클러스터링에 커스팀마커를 적용해보았다.
이제 추가적으로 구글맵에서 검색하여 마커를 클릭할 때, 나오는 하단의 카드뷰를 만들어보고 최종적으로 지도를 완성해보겠다.
향후과제
지도 하단 카드뷰 추가
참고자료
https://milkissboy.tistory.com/36
Writer: Jae-Hwan Lee
댓글남기기