Multiple Surfaces¶
In this guide you will learn how to add/remove
multiple fragment surfaces, each of which can contain
an interactive map centered on a different desired location.
The maps are fully 3D, supporting pan, pinch-zoom, rotate and tilt.
Setup¶
First, get an API key token, see the Getting Started guide.
Download the Maps & Navigation SDK for Android archive fileDownload the MultipleSurfacesInFragment project
archive file or clone the project with Git
See the Configure Android Example guide.
Run the example¶
In Android Studio, from the File
menu, select Sync Project with Gradle Files
An android device should be connected via USB cable.
Press SHIFT+F10 to compile, install and run the example on the
android device.
Click the envelope in the lower right corner to continue.
Click NEXT
Click the green + in the lower right corner to add a fragment with a map.
Each map can show a different location. Try to pan and zoom each map.
Click the red - in the lower left corner to remove the last added fragment.
How it works¶
You can open the MainActivity.kt file to see how to render multiple maps.
1// Kotlin code
2
3class FirstFragment : Fragment()
4{
5 override fun onCreateView(
6 inflater: LayoutInflater, container: ViewGroup?,
7 savedInstanceState: Bundle?
8 ): View? {
9 return inflater.inflate(R.layout.fragment_first, container, false)
10 }
11 override fun onViewCreated(view: View, savedInstanceState: Bundle?)
12 {
13 super.onViewCreated(view, savedInstanceState)
14 view.findViewById<Button>(R.id.button_first).setOnClickListener
15 {
16 findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment)
17 }
18 }
19}
The first fragment.
Each of the 2 fragments has a button to navigate to the other fragment,
see
app/res/navigation/nav_graph.xml
in the project.In
onViewCreated()
, a click listener is set for
the button to go to the second fragment, as defined inapp/res/layout/fragment_first.xml
file in this project. 1class SecondFragment : Fragment()
2{
3 private val maps = mutableMapOf<Long, MapView?>()
4 private val maxSurfacesCount = 9
5 override fun onCreateView(
6 inflater: LayoutInflater, container: ViewGroup?,
7 savedInstanceState: Bundle?
8 ): View? {
9 return inflater.inflate(R.layout.fragment_second, container, false)
10 }
11 override fun onViewCreated(view: View, savedInstanceState: Bundle?)
12 {
13 super.onViewCreated(view, savedInstanceState)
14 view.findViewById<Button>(R.id.button_second).setOnClickListener
15 {
16 findNavController().navigate(R.id.action_SecondFragment_to_FirstFragment)
17 }
18 val leftBtn = view.findViewById<FloatingActionButton>(R.id.bottomLeftButton)
19 leftBtn.visibility = View.VISIBLE
20 ButtonsDecorator.buttonAsDelete(requireContext(), leftBtn)
21 {
22 deleteLastSurface()
23 }
24 val rightBtn = view.findViewById<FloatingActionButton>(R.id.bottomRightButton)
25 rightBtn.visibility = View.VISIBLE
26 ButtonsDecorator.buttonAsAdd(requireContext(), rightBtn)
27 {
28 addSurface()
29 }
30 addSurface()
31 }
The second fragment.
Each of the 2 fragments has a button to navigate to the other fragment,
see
app/res/navigation/nav_graph.xml
in the project.In
onViewCreated()
, a click listener is set for
the button to go to the first fragment, as defined inapp/res/layout/fragment_second.xml
file in this project. 1private fun buttonAsAdd(context: Context,
2 button: FloatingActionButton?, action: () -> Unit) {
3 button ?: return
4 val tag = "add"
5 val backgroundTintList =
6 AppCompatResources.getColorStateList(context, R.color.green)
7 val drawable = ContextCompat.getDrawable(context, android.R.drawable.ic_input_add)
8 button.tag = tag
9 button.setOnClickListener { action() }
10 button.setImageDrawable(drawable)
11 button.backgroundTintList = backgroundTintList
12 }
13 private fun buttonAsDelete(
14 context: Context,
15 button: FloatingActionButton?,
16 action: () -> Unit
17 ) {
18 button ?: return
19 val tag = "delete"
20 val backgroundTintList =
21 AppCompatResources.getColorStateList(context, R.color.red)
22 val drawable = ContextCompat.getDrawable(context, android.R.drawable.ic_delete)
23 button.tag = tag
24 button.setOnClickListener { action() }
25 button.setImageDrawable(drawable)
26 button.backgroundTintList = backgroundTintList
27}
The
SecondFragment
also has a green + and a red x button to add or
remove maps, respectively, using the addSurface()
and
deleteLastSurface()
functions. 1 private fun addSurface()
2 {
3 val linearLayout = view?.findViewById<LinearLayout>(R.id.scrolledLinearLayout) ?: return
4 if (linearLayout.childCount >= maxSurfacesCount)
5 {
6 return
7 }
8 val surface = GemSurfaceView(requireContext())
9 surface?.layoutParams = ViewGroup.LayoutParams(
10 ViewGroup.LayoutParams.MATCH_PARENT,
11 ViewGroup.LayoutParams.MATCH_PARENT
12 )
13 surface.onDefaultMapViewCreated = onDefaultMapViewCreated@{
14 val screen = surface.gemScreen ?: return@onDefaultMapViewCreated
15 // Add the map view to the collection of displayed maps.
16 maps[screen.address] = it
17 }
18 val params = FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 400)
19 params.setMargins(50)
20 val frame = FrameLayout(requireContext())
21 frame.layoutParams = params
22 frame.addView(surface)
23 linearLayout.addView(frame)
24 }
25 private fun deleteLastSurface()
26 {
27 val linearLayout = view?.findViewById<LinearLayout>(R.id.scrolledLinearLayout) ?: return
28 if (linearLayout.childCount == 0)
29 return
30 val lastIndex = linearLayout.childCount - 1
31 val frame = (linearLayout[lastIndex] as FrameLayout)
32 val lastSurface = frame[0] as GemSurfaceView
33 SdkCall.execute
34 {
35 val mapsId = lastSurface.getScreen()?.address()
36 // Release the map view.
37 maps[mapsId]?.release()
38 // Remove the map view from the collection of displayed maps.
39 maps.remove(mapsId)
40 }
41 linearLayout.removeView(frame)
42 }
43}
The
addSurface()
function adds a map to the new surface,
and also stores the map in a map container,
like a list, to enable removing it later:val mapView = MapView.produce(screen, mainViewRect)
maps[screen.address()] = mapView
The
deleteLastSurface()
function deletes the current map and removes
it from the map container/list.val mapsId = lastSurface.getScreen()?.address()
maps[mapsId]?.release()
maps.remove(mapsId)
1override fun onCreate(savedInstanceState: Bundle?) {
2 super.onCreate(savedInstanceState)
3 setContentView(R.layout.activity_main)
4}
MainActivity
overrides the onCreate()
which loads the first
fragment, as defined inapp/res/layout/activity_main.xml
file in this project.