In this page

MapView Polyline

This example shows how to add PolyLines in MapView

  1. Add Polyline from a set of Latlng

  2. Set Polyline stroke

  3. Set polyline colour

For all code examples, refer to Android Maps SDK Code Examples

activity_polyline.xml view source

1<?xml version="1.0" encoding="utf-8"?>
2<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
3    xmlns:app="http://schemas.android.com/apk/res-auto"
4    xmlns:tools="http://schemas.android.com/tools"
5    android:layout_width="match_parent"
6    android:layout_height="match_parent"
7    tools:context=".PolygonActivity">
8
9    <ai.nextbillion.maps.core.MapView
10        android:id="@+id/map_view"
11        android:layout_width="match_parent"
12        android:layout_height="match_parent"
13        app:nbmap_uiAttribution="false"
14        app:nbmap_cameraTargetLat="53.5511"
15        app:nbmap_cameraTargetLng="9.9937"
16        app:nbmap_cameraZoom="12.5" />
17
18    <ImageView
19        android:id="@+id/iv_back"
20        android:layout_width="40dp"
21        android:layout_height="40dp"
22        android:layout_marginLeft="16dp"
23        android:layout_marginTop="16dp"
24        android:background="@drawable/circle_white_bg"
25        android:src="@drawable/icon_back"
26        app:tint="@color/color_back_icon"/>
27
28    <androidx.core.widget.NestedScrollView
29        android:id="@+id/bottomSheet"
30        android:layout_width="match_parent"
31        android:layout_height="wrap_content"
32        android:background="@color/white"
33        app:behavior_hideable="true"
34        app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
35
36        <LinearLayout
37            android:layout_width="match_parent"
38            android:layout_height="wrap_content"
39            android:orientation="vertical">
40
41            <TextView
42                android:layout_width="68dp"
43                android:layout_height="4dp"
44                android:layout_gravity="center_horizontal"
45                android:layout_marginTop="13dp"
46                android:background="@drawable/nbmap_radius_10_grey_bg"
47                />
48
49            <TextView
50                android:layout_width="wrap_content"
51                android:layout_height="wrap_content"
52                android:textColor="@color/black"
53                android:textSize="24sp"
54                android:paddingHorizontal="16dp"
55                android:layout_marginTop="13dp"
56                android:text="@string/polyLine"/>
57
58            <ai.nextbillion.view.SettingSwitchView
59                android:id="@+id/lineEnable"
60                android:visibility="gone"
61                android:layout_width="match_parent"
62                android:layout_height="wrap_content"/>
63
64            <ai.nextbillion.view.ColorSelectorView
65                android:id="@+id/lineColor"
66                android:layout_marginTop="10dp"
67                android:layout_width="match_parent"
68                android:layout_height="wrap_content"/>
69
70            <ai.nextbillion.view.SliderBarView
71                android:id="@+id/lineWidth"
72                android:layout_marginBottom="10dp"
73                android:layout_width="match_parent"
74                android:layout_height="wrap_content"/>
75
76        </LinearLayout>
77
78
79    </androidx.core.widget.NestedScrollView>
80
81    <LinearLayout
82        android:layout_width="match_parent"
83        android:layout_height="wrap_content"
84        app:layout_anchor="@id/bottomSheet"
85        android:paddingHorizontal="10dp"
86        android:paddingBottom="60dp"
87        android:orientation="horizontal" >
88
89        <Button
90            android:id="@+id/removeSingleLine"
91            android:layout_width="wrap_content"
92            android:layout_height="wrap_content"
93            android:textAllCaps="false"
94            android:textColor="@color/white"
95            android:text="@string/remove_single_line"
96            android:visibility="gone"
97            />
98
99        <Space
100            android:layout_width="0dp"
101            android:layout_height="wrap_content"
102            android:layout_weight="1"/>
103
104        <Button
105            android:id="@+id/removeAllLine"
106            android:layout_width="wrap_content"
107            android:layout_height="wrap_content"
108            android:textColor="@color/white"
109            android:textAllCaps="false"
110            android:text="@string/remove_all_line"
111            android:visibility="gone"
112            />
113
114    </LinearLayout>
115
116</androidx.coordinatorlayout.widget.CoordinatorLayout>

PolylineActivity view source

1class PolylineActivity : AppCompatActivity() {
2   private lateinit var mMap: NextbillionMap;
3   private lateinit var binding: ActivityPolylineBinding;
4   private lateinit var bottomSheetBehavior: BottomSheetBehavior<*>;
5   private var points: MutableList<LatLng> = mutableListOf();
6   private var polyline: Polyline? = null;
7   private var originMarker: Marker? = null;
8   private var lineColor = "#1E58A5";
9   private var lineWidth = 5f;
10
11   override fun onCreate(savedInstanceState: Bundle?) {
12       super.onCreate(savedInstanceState)
13       binding = ActivityPolylineBinding.inflate(LayoutInflater.from(this))
14       setContentView(binding.root)
15       binding.mapView.getMapAsync { map: NextbillionMap -> onMapSync(map) }
16       initBottomSheet()
17       binding.ivBack.setOnClickListener {
18           finish()
19       }
20   }
21
22   private fun onMapSync(map: NextbillionMap) {
23       showSnackBar()
24       mMap = map
25       mMap.addOnMapLongClickListener {
26           applyPolyline(it)
27           false
28       }
29       map.setStyle( Style.Builder().fromUri(StyleConstants.LIGHT))
30   }
31
32   private fun showSnackBar() {
33       val snackBar = Snackbar.make(binding.root,getString(R.string.snack_long_press),Snackbar.LENGTH_LONG)
34       snackBar.setTextColor(Color.WHITE)
35       snackBar.view.setBackgroundColor(Color.BLACK)
36       snackBar.show()
37   }
38
39   private fun applyPolyline(latLng: LatLng) {
40       points.add(latLng)
41
42       if (points.size < 2) {
43           originMarker = mMap.addMarker(latLng)
44       } else {
45           originMarker?.let {
46               mMap.removeMarker(it)
47               originMarker = null
48           }
49           recreatePoleLine()
50       }
51
52       updateFloatButtonStatus()
53   }
54
55   private fun removeSingleLine() {
56       if (points.size <= 2) {
57           removeAllPolyline()
58           return
59       } else {
60           points.removeLast()
61           recreatePoleLine()
62       }
63   }
64
65   private fun recreatePoleLine() {
66       polyline?.let {
67           mMap.removePolyline(it)
68       }
69       polyline = mMap.addPolyline(points, lineColor)
70       polyline?.width = lineWidth
71       animateBound(1f)
72   }
73
74   private fun removeAllPolyline() {
75       points.clear()
76       mMap.clear()
77       polyline = null
78       updateFloatButtonStatus()
79   }
80
81   private fun updateFloatButtonStatus() {
82       if (points.size < 2) {
83           bottomSheetBehavior.isHideable = true
84           bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
85
86           binding.removeSingleLine.visibility = View.GONE
87           binding.removeAllLine.visibility = View.GONE
88       } else {
89           bottomSheetBehavior.isHideable = false
90           bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
91           bottomSheetBehavior.peekHeight = dpToPx(this, 60.0)
92
93           binding.removeSingleLine.visibility = View.VISIBLE
94           binding.removeAllLine.visibility = View.VISIBLE
95       }
96
97   }
98
99
100   private fun initBottomSheet() {
101       binding.removeSingleLine.setOnClickListener {
102           removeSingleLine()
103       }
104
105       binding.removeAllLine.setOnClickListener {
106           removeAllPolyline()
107       }
108
109       binding.lineColor.setTitle(getString(R.string.lineColor))
110       binding.lineColor.initColor(lineColor)
111       binding.lineColor.setOnColorChangedListener(object : ColorSelectorView.OnColorChangedListener {
112           override fun onColorChanged(color: String) {
113               lineColor = color
114               polyline?.color = Color.parseColor(color)
115           }
116       })
117
118       binding.lineWidth.setTitle(getString(R.string.lineWidth))
119       binding.lineWidth.initSeekBar(5f, 1f, 15f, "", 1f)
120       binding.lineWidth.setOnSliderChangedListener(object : SliderBarView.OnSliderChangedListener {
121           override fun onSliderChanged(value: Float) {
122               lineWidth = value
123               polyline?.width = value
124           }
125       })
126
127       bottomSheetBehavior = BottomSheetBehavior.from(binding.bottomSheet)
128       bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
129       bottomSheetBehavior.addBottomSheetCallback(object :
130           BottomSheetBehavior.BottomSheetCallback() {
131           override fun onStateChanged(bottomSheet: View, newState: Int) {}
132           override fun onSlide(bottomSheet: View, slideOffset: Float) {
133               animateBound(slideOffset)
134           }
135       })
136
137   }
138
139
140   private fun animateBound(slideOffset: Float) {
141       if (points.size < 2) {
142           return
143       }
144       val bounds = LatLngBounds.Builder().includes(points).build()
145       animateCameraBox(bounds, createPadding(this, slideOffset))
146   }
147
148   private fun animateCameraBox(
149       bounds: LatLngBounds,
150       padding: IntArray
151   ) {
152       val position = mMap.getCameraForLatLngBounds(bounds, padding)
153       position?.let {
154           val cameraUpdate = CameraUpdateFactory.newCameraPosition(position)
155           mMap.animateCamera(cameraUpdate)
156       }
157
158   }
159
160   private fun createPadding(context: Context, cameraBoundFactor: Float): IntArray {
161       val horPadding = dpToPx(context, 50.0)
162       val topPadding = dpToPx(context, 50.0)
163       val bottomPadding = (dpToPx(context, 260.0) * cameraBoundFactor).toInt()
164       return intArrayOf(horPadding, topPadding, horPadding, bottomPadding)
165   }
166
167   ///////////////////////////////////////////////////////////////////////////
168   // Lifecycle
169   ///////////////////////////////////////////////////////////////////////////
170   override fun onStart() {
171       super.onStart()
172       binding.mapView.onStart()
173   }
174
175   override fun onResume() {
176       super.onResume()
177       binding.mapView.onResume()
178   }
179
180   override fun onPause() {
181       super.onPause()
182       binding.mapView.onPause()
183   }
184
185   override fun onStop() {
186       super.onStop()
187       binding.mapView.onStop()
188   }
189
190   override fun onSaveInstanceState(outState: Bundle) {
191       super.onSaveInstanceState(outState)
192       binding.mapView.onSaveInstanceState(outState)
193   }
194
195   override fun onDestroy() {
196       super.onDestroy()
197       binding.mapView.onDestroy()
198   }
199
200   override fun onLowMemory() {
201       super.onLowMemory()
202       binding.mapView.onLowMemory()
203   }
204
205   fun dpToPx(context: Context, dp: Double): Int {
206       val scale = context.resources.displayMetrics.density
207       return (dp * scale + 0.5f).toInt()
208   }
209}
  1. applyPolyline(latLng: LatLng)

    1. This method is used to apply a polyline on the map.

    2. It takes a LatLng object as a parameter, representing a point on the polyline.

    3. First, the point is added to the points list.

    4. If the size of the points list is less than 2, it means there are not enough points to draw a polyline, so a marker is added to the map.

    5. If the size of the points list is greater than or equal to 2, it means there are enough points to draw a polyline, so the previous polyline is removed and a new one is created.

    6. Finally, the updateFloatButtonStatus method is called to update the status of the floating button.

  2. removeSingleLine

    1. This method is used to remove the last added point, thereby deleting the last segment of the polyline.

    2. If the size of the points list is less than or equal to 2, it means there are not enough points to draw a polyline, so the removeAllPolyline method is called to remove all polylines and return.

    3. If the size of the points list is greater than 2, the last point is removed, and a new polyline is created.

    4. Finally, the updateFloatButtonStatus method is called to update the status of the floating button.

  3. recreatePolyline

    1. This method is used to recreate the polyline.

    2. First, the previous polyline (if it exists) is removed.

    3. A new polyline is created based on the points in the points list and assigned to the polyline variable.

    4. The color of the polyline is set to the color specified by the lineColor variable.

    5. The width of the polyline is set to the width specified by the lineWidth variable.

    6. The animateBound method is called to fit the polyline's position.

  4. removeAllPolyline

    1. This method is used to remove all polylines.

    2. The points list is cleared.

    3. All markers and polylines on the map are cleared.

    4. The polyline variable is set to null.

    5. The updateFloatButtonStatus method is called to update the status of the floating button.

  5. animateBound

    1. This method is used to adjust the map's display area based on the position of the polyline.

    2. If the size of the points list is less than 2, it means there are not enough points to draw a polyline, so it returns directly.

    3. A LatLngBounds object is created based on the points in the points list.

    4. The animateCameraBox method is called to animate the map's display area.

  6. animateCameraBox

    1. This method is used to animate the map's display area to include the specified boundary range.

    2. A CameraPosition object is created based on the boundary range and padding.

    3. If the CameraPosition object is not null, a CameraUpdate object is created, and the animateCamera method is used to animate the map's display area.

The code also includes various lifecycle methods (onStart, onResume, onPause, onStop, onSaveInstanceState, onDestroy, onLowMemory) that should be implemented when using the MapView to properly manage its lifecycle and handle configuration changes.

Note: The code uses the Nextbillion Maps SDK, which provides map-related functionalities. It also utilizes the LatLng class to represent geography.

DIDN'T FIND WHAT YOU LOOKING FOR?