1+ // Copyright 2020 Google LLC
2+ //
3+ // Licensed under the Apache License, Version 2.0 (the "License");
4+ // you may not use this file except in compliance with the License.
5+ // You may obtain a copy of the License at
6+ //
7+ // http://www.apache.org/licenses/LICENSE-2.0
8+ //
9+ // Unless required by applicable law or agreed to in writing, software
10+ // distributed under the License is distributed on an "AS IS" BASIS,
11+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ // See the License for the specific language governing permissions and
13+ // limitations under the License.
14+ package com.example.kotlindemos
15+
16+ import android.os.Bundle
17+ import android.util.Log
18+ import android.view.View
19+ import android.widget.Button
20+ import android.widget.TextView
21+ import android.widget.Toast
22+ import androidx.appcompat.app.AppCompatActivity
23+ import androidx.lifecycle.lifecycleScope
24+ import com.google.android.gms.maps.CameraUpdateFactory
25+ import com.google.android.gms.maps.GoogleMap
26+ import com.google.android.gms.maps.GoogleMap.OnCameraIdleListener
27+ import com.google.android.gms.maps.OnMapReadyCallback
28+ import com.google.android.gms.maps.SupportMapFragment
29+ import com.google.android.gms.maps.model.CameraPosition
30+ import com.google.android.gms.maps.model.LatLng
31+ import com.google.android.gms.maps.model.LatLngBounds
32+ import com.google.maps.android.ktx.CameraIdleEvent
33+ import com.google.maps.android.ktx.awaitMap
34+ import com.google.maps.android.ktx.cameraEvents
35+ import kotlinx.coroutines.ExperimentalCoroutinesApi
36+ import kotlinx.coroutines.coroutineScope
37+ import kotlinx.coroutines.flow.collect
38+ import kotlinx.coroutines.launch
39+
40+ /* *
41+ * This shows how to constrain the camera to specific boundaries and zoom levels.
42+ */
43+ class CameraClampingDemoActivity : AppCompatActivity () {
44+
45+ private lateinit var map: GoogleMap
46+ private lateinit var cameraTextView: TextView
47+ private val buttonIdToLatLngBoundsCameraMap = mapOf (
48+ Pair (R .id.clamp_latlng_adelaide, Pair (ADELAIDE , ADELAIDE_CAMERA )),
49+ Pair (R .id.clamp_latlng_pacific, Pair (PACIFIC , PACIFIC_CAMERA )),
50+ )
51+
52+ /* *
53+ * Internal min zoom level that can be toggled via the demo.
54+ */
55+ private var minZoom = DEFAULT_MIN_ZOOM
56+
57+ /* *
58+ * Internal max zoom level that can be toggled via the demo.
59+ */
60+ private var maxZoom = DEFAULT_MAX_ZOOM
61+
62+ @ExperimentalCoroutinesApi
63+ override fun onCreate (savedInstanceState : Bundle ? ) {
64+ super .onCreate(savedInstanceState)
65+ setContentView(R .layout.camera_clamping_demo)
66+ cameraTextView = findViewById(R .id.camera_text)
67+ val mapFragment = supportFragmentManager.findFragmentById(R .id.map) as SupportMapFragment
68+ lifecycleScope.launchWhenCreated {
69+ map = mapFragment.awaitMap()
70+ launch {
71+ map.cameraEvents().collect { event ->
72+ when (event) {
73+ is CameraIdleEvent -> onCameraIdle()
74+ else -> Log .d(TAG , " Got event: $event " )
75+ }
76+ }
77+ }
78+ setButtonClickListeners()
79+ }
80+ }
81+
82+ private fun setButtonClickListeners () {
83+ // Min/max zooms
84+ findViewById<Button >(R .id.clamp_min_zoom).setOnClickListener {
85+ minZoom + = ZOOM_DELTA
86+ // Constrains the minimum zoom level.
87+ map.setMinZoomPreference(minZoom)
88+ toast(" Min zoom preference set to: $minZoom " )
89+ }
90+ findViewById<Button >(R .id.clamp_max_zoom).setOnClickListener {
91+ maxZoom - = ZOOM_DELTA
92+ // Constrains the maximum zoom level.
93+ map.setMaxZoomPreference(maxZoom)
94+ toast(" Max zoom preference set to: $maxZoom " )
95+ }
96+ findViewById<Button >(R .id.clamp_zoom_reset).setOnClickListener {
97+ resetMinMaxZoom()
98+ map.resetMinMaxZoomPreference()
99+ toast(" Min/Max zoom preferences reset." )
100+ }
101+
102+ // Clamp
103+ val clampListener: (View ) -> Unit = { view ->
104+ buttonIdToLatLngBoundsCameraMap[view.id]?.let { (latLngBounds, camera) ->
105+ map.setLatLngBoundsForCameraTarget(latLngBounds)
106+ map.animateCamera(CameraUpdateFactory .newCameraPosition(camera))
107+ }
108+ }
109+ findViewById<Button >(R .id.clamp_latlng_adelaide).setOnClickListener(clampListener)
110+ findViewById<Button >(R .id.clamp_latlng_pacific).setOnClickListener(clampListener)
111+ findViewById<Button >(R .id.clamp_latlng_reset).setOnClickListener {
112+ map.setLatLngBoundsForCameraTarget(null )
113+ toast(" LatLngBounds clamp reset." )
114+ }
115+ }
116+
117+ private fun onCameraIdle () {
118+ cameraTextView.text = map.cameraPosition.toString()
119+ }
120+
121+
122+ private fun toast (msg : String ) {
123+ Toast .makeText(baseContext, msg, Toast .LENGTH_SHORT ).show()
124+ }
125+
126+ private fun resetMinMaxZoom () {
127+ minZoom = DEFAULT_MIN_ZOOM
128+ maxZoom = DEFAULT_MAX_ZOOM
129+ }
130+
131+ companion object {
132+ private val TAG = CameraClampingDemoActivity ::class .java.name
133+ private const val ZOOM_DELTA = 2.0f
134+ private const val DEFAULT_MIN_ZOOM = 2.0f
135+ private const val DEFAULT_MAX_ZOOM = 22.0f
136+ private val ADELAIDE = LatLngBounds (
137+ LatLng (- 35.0 , 138.58 ), LatLng (- 34.9 , 138.61 ))
138+ private val ADELAIDE_CAMERA = CameraPosition .Builder ()
139+ .target(LatLng (- 34.92873 , 138.59995 )).zoom(20.0f ).bearing(0f ).tilt(0f ).build()
140+ private val PACIFIC = LatLngBounds (
141+ LatLng (- 15.0 , 165.0 ), LatLng (15.0 , - 165.0 ))
142+ private val PACIFIC_CAMERA = CameraPosition .Builder ()
143+ .target(LatLng (0.0 , (- 180 ).toDouble())).zoom(4.0f ).bearing(0f ).tilt(0f ).build()
144+ }
145+ }
0 commit comments