[android] - added DraggableMarkerActivity
This commit is contained in:
committed by
Łukasz Paczos
parent
3bd24e655f
commit
282f89a9f3
@@ -345,15 +345,15 @@
|
||||
android:value=".activity.FeatureOverviewActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.snapshot.MapSnapshotterLocalStyleActivity"
|
||||
android:description="@string/description_map_snapshotter_local_style"
|
||||
android:label="@string/activity_map_snapshotter_local_style">
|
||||
android:name=".activity.snapshot.MapSnapshotterLocalStyleActivity"
|
||||
android:description="@string/description_map_snapshotter_local_style"
|
||||
android:label="@string/activity_map_snapshotter_local_style">
|
||||
<meta-data
|
||||
android:name="@string/category"
|
||||
android:value="@string/category_imagegenerator" />
|
||||
android:name="@string/category"
|
||||
android:value="@string/category_imagegenerator" />
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".activity.FeatureOverviewActivity" />
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".activity.FeatureOverviewActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.maplayout.DoubleMapActivity"
|
||||
@@ -766,10 +766,22 @@
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".activity.FeatureOverviewActivity" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activity.style.DraggableMarkerActivity"
|
||||
android:description="@string/description_draggable_marker"
|
||||
android:label="@string/activity_draggable_maker">
|
||||
<meta-data
|
||||
android:name="@string/category"
|
||||
android:value="@string/category_style" />
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".activity.FeatureOverviewActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.camera.GestureDetectorActivity"
|
||||
android:label="@string/activity_gesture_detector"
|
||||
android:description="@string/description_gesture_detector">
|
||||
android:description="@string/description_gesture_detector"
|
||||
android:label="@string/activity_gesture_detector">
|
||||
<meta-data
|
||||
android:name="@string/category"
|
||||
android:value="@string/category_camera" />
|
||||
@@ -789,14 +801,14 @@
|
||||
android:name=".activity.espresso.EspressoTestActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name=".activity.espresso.DeviceIndependentTestActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
android:name=".activity.espresso.DeviceIndependentTestActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name=".activity.style.FillExtrusionStyleTestActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name=".activity.render.RenderTestActivity"
|
||||
android:screenOrientation="landscape"/>
|
||||
android:screenOrientation="landscape" />
|
||||
<!-- Configuration Settings -->
|
||||
<meta-data
|
||||
android:name="com.mapbox.TestEventsServer"
|
||||
@@ -811,4 +823,4 @@
|
||||
<!-- android:value="true" /> -->
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
</manifest>
|
||||
@@ -0,0 +1,314 @@
|
||||
package com.mapbox.mapboxsdk.testapp.activity.style
|
||||
|
||||
import android.graphics.PointF
|
||||
import android.os.Bundle
|
||||
import android.support.design.widget.Snackbar
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import com.mapbox.android.gestures.AndroidGesturesManager
|
||||
import com.mapbox.android.gestures.MoveGestureDetector
|
||||
import com.mapbox.geojson.Feature
|
||||
import com.mapbox.geojson.FeatureCollection
|
||||
import com.mapbox.geojson.Point
|
||||
import com.mapbox.mapboxsdk.annotations.IconFactory
|
||||
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng
|
||||
import com.mapbox.mapboxsdk.maps.MapView
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap
|
||||
import com.mapbox.mapboxsdk.style.layers.PropertyFactory.*
|
||||
import com.mapbox.mapboxsdk.style.layers.SymbolLayer
|
||||
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource
|
||||
import com.mapbox.mapboxsdk.testapp.R
|
||||
import kotlinx.android.synthetic.main.activity_draggable_marker.*
|
||||
|
||||
/**
|
||||
* An Activity that showcases how to make symbols draggable.
|
||||
*/
|
||||
class DraggableMarkerActivity : AppCompatActivity() {
|
||||
companion object {
|
||||
private const val sourceId = "source_draggable"
|
||||
private const val layerId = "layer_draggable"
|
||||
private const val markerImageId = "marker_icon_draggable"
|
||||
|
||||
private var latestId: Long = 0
|
||||
fun generateMarkerId(): String {
|
||||
if (latestId == Long.MAX_VALUE) {
|
||||
throw RuntimeException("You've added too many markers.")
|
||||
}
|
||||
return latestId++.toString()
|
||||
}
|
||||
}
|
||||
|
||||
private val actionBarHeight: Int by lazy {
|
||||
supportActionBar?.height ?: 0
|
||||
}
|
||||
|
||||
private lateinit var mapboxMap: MapboxMap
|
||||
private val featureCollection = FeatureCollection.fromFeatures(mutableListOf())
|
||||
private val source = GeoJsonSource(sourceId, featureCollection)
|
||||
private val layer = SymbolLayer(layerId, sourceId)
|
||||
.withProperties(
|
||||
iconImage(markerImageId),
|
||||
iconAllowOverlap(true),
|
||||
iconIgnorePlacement(true))
|
||||
|
||||
private var draggableSymbolsManager: DraggableSymbolsManager? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_draggable_marker)
|
||||
|
||||
mapView.onCreate(savedInstanceState)
|
||||
mapView.getMapAsync { mapboxMap ->
|
||||
this.mapboxMap = mapboxMap
|
||||
|
||||
// Setting up markers icon, source and layer
|
||||
mapboxMap.addImage(markerImageId, IconFactory.getInstance(this).defaultMarker().bitmap)
|
||||
mapboxMap.addSource(source)
|
||||
mapboxMap.addLayer(layer)
|
||||
|
||||
// Add initial markers
|
||||
addMarker(LatLng(52.407210, 16.924324))
|
||||
addMarker(LatLng(41.382679, 2.181555))
|
||||
addMarker(LatLng(51.514886, -0.112589))
|
||||
|
||||
// Initial camera position
|
||||
mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(
|
||||
LatLng(45.0, 8.0), 3.0
|
||||
))
|
||||
|
||||
mapboxMap.addOnMapClickListener {
|
||||
// Adding a marker on map click
|
||||
val features = mapboxMap.queryRenderedSymbols(it, layerId)
|
||||
if (features.isEmpty()) {
|
||||
addMarker(it)
|
||||
} else {
|
||||
// Displaying marker info on marker click
|
||||
Snackbar.make(
|
||||
mapView,
|
||||
"Marker's position: %.4f, %.4f".format(it.latitude, it.longitude),
|
||||
Snackbar.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
draggableSymbolsManager = DraggableSymbolsManager(
|
||||
mapView, mapboxMap, featureCollection, source, layerId, 0, actionBarHeight)
|
||||
|
||||
// Adding symbol drag listeners
|
||||
draggableSymbolsManager?.addOnSymbolDragListener(object : DraggableSymbolsManager.OnSymbolDragListener {
|
||||
override fun onSymbolDragStarted(id: String) {
|
||||
draggedMarkerPositionTv.visibility = View.VISIBLE
|
||||
Snackbar.make(
|
||||
mapView,
|
||||
"Marker drag started (%s)".format(id),
|
||||
Snackbar.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun onSymbolDrag(id: String) {
|
||||
val point = featureCollection.features()?.find {
|
||||
it.id() == id
|
||||
}?.geometry() as Point
|
||||
draggedMarkerPositionTv.text = "Dragged marker's position: %.4f, %.4f".format(point.latitude(), point.longitude())
|
||||
}
|
||||
|
||||
override fun onSymbolDragFinished(id: String) {
|
||||
draggedMarkerPositionTv.visibility = View.GONE
|
||||
Snackbar.make(
|
||||
mapView,
|
||||
"Marker drag finished (%s)".format(id),
|
||||
Snackbar.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun addMarker(latLng: LatLng) {
|
||||
featureCollection.features()?.add(
|
||||
Feature.fromGeometry(Point.fromLngLat(latLng.longitude, latLng.latitude), null, generateMarkerId()))
|
||||
source.setGeoJson(featureCollection)
|
||||
}
|
||||
|
||||
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
|
||||
// Dispatching parent's touch events to the manager
|
||||
draggableSymbolsManager?.onParentTouchEvent(ev)
|
||||
return super.dispatchTouchEvent(ev)
|
||||
}
|
||||
|
||||
/**
|
||||
* A manager, that allows dragging symbols after they are long clicked.
|
||||
* Since this manager lives outside of the Maps SDK, we need to intercept parent's motion events
|
||||
* and pass them with [DraggableSymbolsManager.onParentTouchEvent].
|
||||
* If we were to try and overwrite [AppCompatActivity.onTouchEvent], those events would've been
|
||||
* consumed by the map.
|
||||
*
|
||||
* We also need to setup a [DraggableSymbolsManager.androidGesturesManager],
|
||||
* because after disabling map's gestures and starting the drag process
|
||||
* we still need to listen for move gesture events which map won't be able to provide anymore.
|
||||
*
|
||||
* @param mapView the mapView
|
||||
* @param mapboxMap the mapboxMap
|
||||
* @param symbolsCollection the collection that contains all the symbols that we want to be draggable
|
||||
* @param symbolsSource the source that contains the [symbolsCollection]
|
||||
* @param symbolsLayerId the ID of the layer that the symbols are displayed on
|
||||
* @param touchAreaShiftX X-axis padding that is applied to the parent's window motion event,
|
||||
* as that window can be bigger than the [mapView].
|
||||
* @param touchAreaShiftY Y-axis padding that is applied to the parent's window motion event,
|
||||
* as that window can be bigger than the [mapView].
|
||||
* @param touchAreaMaxX maximum value of X-axis motion event
|
||||
* @param touchAreaMaxY maximum value of Y-axis motion event
|
||||
*/
|
||||
class DraggableSymbolsManager(mapView: MapView, private val mapboxMap: MapboxMap,
|
||||
private val symbolsCollection: FeatureCollection,
|
||||
private val symbolsSource: GeoJsonSource, private val symbolsLayerId: String,
|
||||
private val touchAreaShiftX: Int = 0, private val touchAreaShiftY: Int = 0,
|
||||
private val touchAreaMaxX: Int = mapView.width, private val touchAreaMaxY: Int = mapView.height) {
|
||||
|
||||
private val androidGesturesManager: AndroidGesturesManager = AndroidGesturesManager(mapView.context, false)
|
||||
private var draggedSymbolId: String? = null
|
||||
private val onSymbolDragListeners: MutableList<OnSymbolDragListener> = mutableListOf<OnSymbolDragListener>()
|
||||
|
||||
init {
|
||||
mapboxMap.addOnMapLongClickListener {
|
||||
// Starting the drag process on long click
|
||||
draggedSymbolId = mapboxMap.queryRenderedSymbols(it, symbolsLayerId).firstOrNull()?.id()?.also { id ->
|
||||
mapboxMap.uiSettings.setAllGesturesEnabled(false)
|
||||
mapboxMap.gesturesManager.moveGestureDetector.interrupt()
|
||||
notifyOnSymbolDragListeners {
|
||||
onSymbolDragStarted(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
androidGesturesManager.setMoveGestureListener(MyMoveGestureListener())
|
||||
}
|
||||
|
||||
inner class MyMoveGestureListener : MoveGestureDetector.OnMoveGestureListener {
|
||||
override fun onMoveBegin(detector: MoveGestureDetector): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onMove(detector: MoveGestureDetector, distanceX: Float, distanceY: Float): Boolean {
|
||||
if (detector.pointersCount > 1) {
|
||||
// Stopping the drag when we don't work with a simple, on-pointer move anymore
|
||||
stopDragging()
|
||||
return true
|
||||
}
|
||||
|
||||
// Updating symbol's position
|
||||
draggedSymbolId?.also { draggedSymbolId ->
|
||||
val moveObject = detector.getMoveObject(0)
|
||||
val point = PointF(moveObject.currentX - touchAreaShiftX, moveObject.currentY - touchAreaShiftY)
|
||||
|
||||
if (point.x < 0 || point.y < 0 || point.x > touchAreaMaxX || point.y > touchAreaMaxY) {
|
||||
stopDragging()
|
||||
}
|
||||
|
||||
val latLng = mapboxMap.projection.fromScreenLocation(point)
|
||||
|
||||
symbolsCollection.features()?.indexOfFirst {
|
||||
it.id() == draggedSymbolId
|
||||
}?.also { index ->
|
||||
symbolsCollection.features()?.get(index)?.also { oldFeature ->
|
||||
val properties = oldFeature.properties()
|
||||
val newFeature = Feature.fromGeometry(
|
||||
Point.fromLngLat(latLng.longitude, latLng.latitude),
|
||||
properties,
|
||||
draggedSymbolId
|
||||
)
|
||||
symbolsCollection.features()?.set(index, newFeature)
|
||||
symbolsSource.setGeoJson(symbolsCollection)
|
||||
notifyOnSymbolDragListeners {
|
||||
onSymbolDrag(draggedSymbolId)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onMoveEnd(detector: MoveGestureDetector, velocityX: Float, velocityY: Float) {
|
||||
// Stopping the drag when move ends
|
||||
stopDragging()
|
||||
}
|
||||
}
|
||||
|
||||
private fun stopDragging() {
|
||||
mapboxMap.uiSettings.setAllGesturesEnabled(true)
|
||||
draggedSymbolId?.let {
|
||||
notifyOnSymbolDragListeners {
|
||||
onSymbolDragFinished(it)
|
||||
}
|
||||
}
|
||||
draggedSymbolId = null
|
||||
}
|
||||
|
||||
fun onParentTouchEvent(ev: MotionEvent?) {
|
||||
androidGesturesManager.onTouchEvent(ev)
|
||||
}
|
||||
|
||||
private fun notifyOnSymbolDragListeners(action: OnSymbolDragListener.() -> Unit) {
|
||||
onSymbolDragListeners.forEach(action)
|
||||
}
|
||||
|
||||
fun addOnSymbolDragListener(listener: OnSymbolDragListener) {
|
||||
onSymbolDragListeners.add(listener)
|
||||
}
|
||||
|
||||
fun removeOnSymbolDragListener(listener: OnSymbolDragListener) {
|
||||
onSymbolDragListeners.remove(listener)
|
||||
}
|
||||
|
||||
interface OnSymbolDragListener {
|
||||
fun onSymbolDragStarted(id: String)
|
||||
fun onSymbolDrag(id: String)
|
||||
fun onSymbolDragFinished(id: String)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
mapView.onStart()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
mapView.onResume()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
mapView.onPause()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
mapView.onStop()
|
||||
}
|
||||
|
||||
override fun onLowMemory() {
|
||||
super.onLowMemory()
|
||||
mapView.onLowMemory()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
mapView.onDestroy()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle?) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState?.let {
|
||||
mapView.onSaveInstanceState(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun MapboxMap.queryRenderedSymbols(latLng: LatLng, layerId: String): List<Feature> {
|
||||
return this.queryRenderedFeatures(this.projection.toScreenLocation(latLng), layerId)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".activity.style.DraggableMarkerActivity">
|
||||
|
||||
<com.mapbox.mapboxsdk.maps.MapView
|
||||
android:id="@+id/mapView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/draggedMarkerPositionTv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone" />
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -69,4 +69,5 @@
|
||||
<string name="description_hillshade">Example raster-dem source and hillshade layer</string>
|
||||
<string name="description_heatmaplayer">Use HeatmapLayer to visualise earthquakes</string>
|
||||
<string name="description_gesture_detector">Manipulate gestures detector\'s settings</string>
|
||||
<string name="description_draggable_marker">Click to add a marker, long-click to drag</string>
|
||||
</resources>
|
||||
|
||||
@@ -69,4 +69,5 @@
|
||||
<string name="activity_hillshade">Hillshade</string>
|
||||
<string name="activity_heatmaplayer">Heatmap layer</string>
|
||||
<string name="activity_gesture_detector">Gestures detector</string>
|
||||
<string name="activity_draggable_maker">Draggable marker</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user