MapView Polyline
This example shows how to add PolyLines in MapView
-
Add Polyline from a set of Latlng
-
Set Polyline stroke
-
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}
-
applyPolyline(latLng: LatLng)
-
This method is used to apply a polyline on the map.
-
It takes a LatLng object as a parameter, representing a point on the polyline.
-
First, the point is added to the points list.
-
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.
-
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.
-
Finally, the updateFloatButtonStatus method is called to update the status of the floating button.
-
-
removeSingleLine
-
This method is used to remove the last added point, thereby deleting the last segment of the polyline.
-
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.
-
If the size of the points list is greater than 2, the last point is removed, and a new polyline is created.
-
Finally, the updateFloatButtonStatus method is called to update the status of the floating button.
-
-
recreatePolyline
-
This method is used to recreate the polyline.
-
First, the previous polyline (if it exists) is removed.
-
A new polyline is created based on the points in the points list and assigned to the polyline variable.
-
The color of the polyline is set to the color specified by the lineColor variable.
-
The width of the polyline is set to the width specified by the lineWidth variable.
-
The animateBound method is called to fit the polyline's position.
-
-
removeAllPolyline
-
This method is used to remove all polylines.
-
The points list is cleared.
-
All markers and polylines on the map are cleared.
-
The polyline variable is set to null.
-
The updateFloatButtonStatus method is called to update the status of the floating button.
-
-
animateBound
-
This method is used to adjust the map's display area based on the position of the polyline.
-
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.
-
A LatLngBounds object is created based on the points in the points list.
-
The animateCameraBox method is called to animate the map's display area.
-
-
animateCameraBox
-
This method is used to animate the map's display area to include the specified boundary range.
-
A CameraPosition object is created based on the boundary range and padding.
-
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.