Skip to content

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 file

Download 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

Multiple surfaces example Android screenshot

Multiple surfaces example Android screenshot

Multiple surfaces example Android screenshot

Multiple surfaces example Android screenshot

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

Android example screenshot

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 in
app/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 in
app/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 in
app/res/layout/activity_main.xml file in this project.

Android Examples

Maps SDK for Android Examples can be downloaded or cloned with Git