问题
My realtime Firebase database
I try to change position for many lat langs at some time like cars in uber, but when I change lat or lang or both this don't change the position of marker but its make anew marker. I want when lat for example is changed in realtime database the markers also changed based on the value of that lat
I try make condition
`if (marker != null)
setPosition
else "marker == null"`
add new marker
but the problem is I have >1 markers so that this solution is display me on the map one marker only because marker != null
public class Map extends FragmentActivity implements OnMapReadyCallback {
FirebaseDatabase database;
DatabaseReference myRef;
GoogleMap mMap;
MarkerOptions options;
Marker mCurrLocationMarker;
LatLng ll;
model save;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
database = FirebaseDatabase.getInstance();
myRef = database.getReference("user");
myRef.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot ds : dataSnapshot.getChildren()) {
Double lang = ds.child("lang").getValue(Double.class);
Double lat = ds.child("lat").getValue(Double.class);
save = new model(lat, lang);
ll = new LatLng(save.getLat(), save.getLang());
mCurrLocationMarker = mMap.addMarker(options
.position(ll));
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
I expected the marker's position was changed when I change real time lat and lang in firebase, but the result is created another marker.
回答1:
Following from my comments on other answers, here is what I've come up with based on your question.
This code will show multiple markers on the map, but only one marker for each ID under /users
in the database. If the location for a particular ID changes, it's associated marker will be moved without affecting the other markers on the map.
Warning: The below code will update your map in real-time. You may which to cache the new marker locations and only update them once every 2-5 seconds or so depending on your how frequently your data changes.
Some quick notes before diving into the code:
- Each marker is linked to it's string ID in the database under a Map called
mNamedMarkers
. - As
model
was not provided and seems irrelevant, I have omitted it from the code below. - I'm not sure if you are coming from a Germanic background, but
lng
is short for Longitude, not "lang" in this context. I would also change your database entries to uselng
rather than lang/long/longitude/etc (it saves space & eliminates confusion). - In the code below, I've added
getMarkerOptions(key)
, this is so that you can add code to get different images, titles, and text for each marker based on it's ID. Currently it will produce the same data for each marker. - I've added Javadoc mark-up for each function to summarize what each does.
- There are a couple of TODOs for further development.
Here is the code:
public class Map extends FragmentActivity implements OnMapReadyCallback {
FirebaseDatabase database;
DatabaseReference userLocationsRef;
GoogleMap mMap;
Map<String, Marker> mNamedMarkers = new HashMap<String,Marker>();
ChildEventListener markerUpdateListener = new ChildEventListener() {
/**
* Adds each existing/new location of a marker.
*
* Will silently update any existing markers as needed.
* @param dataSnapshot The new location data
* @param previousChildName The key of the previous child event
*/
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
String key = dataSnapshot.getKey();
Log.d(TAG, "Adding location for '" + key + "'");
Double lng = ds.child("lang").getValue(Double.class);
Double lat = ds.child("lat").getValue(Double.class);
LatLng location = new LatLng(lat, lng);
Marker marker = mNamedMarkers.get(key);
if (marker == null) {
MarkerOptions options = getMarkerOptions(key);
marker = mMap.addMarker(options.position(location));
mNamedMarkers.put(key, marker);
} else {
// This marker-already-exists section should never be called in this listener's normal use, but is here to handle edge cases quietly.
// TODO: Confirm if marker title/snippet needs updating.
marker.setPosition(location);
}
}
/**
* Updates the location of a previously loaded marker.
*
* Will silently create any missing markers as needed.
* @param dataSnapshot The new location data
* @param previousChildName The key of the previous child event
*/
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
String key = dataSnapshot.getKey();
Log.d(TAG, "Location for '" + key + "' was updated.");
Double lng = ds.child("lang").getValue(Double.class);
Double lat = ds.child("lat").getValue(Double.class);
LatLng location = new LatLng(lat, lng);
Marker marker = mNamedMarkers.get(key);
if (marker == null) {
// This null-handling section should never be called in this listener's normal use, but is here to handle edge cases quietly.
Log.d(TAG, "Expected existing marker for '" + key + "', but one was not found. Added now.");
MarkerOptions options = getMarkerOptions(key); // TODO: Read data from database for this marker (e.g. Name, Driver, Vehicle type)
marker = mMap.addMarker(options.position(location));
mNamedMarkers.put(key, marker);
} else {
// TODO: Confirm if marker title/snippet needs updating.
marker.setPosition(location);
}
}
/**
* Removes the marker from its GoogleMap instance
* @param dataSnapshot The removed data
*/
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
String key = dataSnapshot.getKey();
Log.d(TAG, "Location for '" + key + "' was removed.");
Marker marker = mNamedMarkers.get(key);
if (marker != null)
marker.remove()
}
/**
* Ignored.
* @param dataSnapshot The moved data
* @param previousChildName The key of the previous child event
*/
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
// Unused
Log.d(TAG, "Priority for '" + dataSnapshot.getKey() "' was changed.");
}
/**
* Error handler when listener is canceled.
* @param databaseError The error object
*/
@Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "markerUpdateListener:onCancelled", databaseError.toException());
Toast.makeText(mContext, "Failed to load location markers.", Toast.LENGTH_SHORT).show();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
/**
* Waits for the map to be ready then loads markers from the database.
* @param googleMap The GoogleMap instance
*/
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
database = FirebaseDatabase.getInstance();
userLocationsRef = database.getReference("user");
userLocationsRef.addChildEventListener(markerUpdateListener);
// later when the activity becomes inactive.
// userLocationsRef.removeEventListener(markerUpdateListener)
}
/**
* Retrieves the marker data for the given key.
* @param key The ID of the marker
* @return A MarkerOptions instance containing this marker's infoormation
*/
private MarkerOptions getMarkerOptions(String key) {
// TODO: Read data from database for the given marker (e.g. Name, Driver, Vehicle type)
return new MarkerOptions().title('Location placeholder').snippet('Update this with marker information');
}
}
回答2:
Try this.
And you must implement OnMapReadyCallback
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
}
And mMap make private or public variable.
Before calling for loop method
Insert follow code
mMap.clear();
You're using the wrong way for addValueEventListener.
You can submit addValueEventlistener in onCreate Method, And also need check null before call.
In OnMapReadyCallback method only do bind mMap variable.
回答3:
Please have a look for complete tracking of marker, animate and move like uber, with the below Kotlin code :
Initially add your firebase listener
mFirebaseRef?.child(mDriverId)!!.addChildEventListener(listener);
Define your firebase listener
var listener = object : ChildEventListener {
override fun onCancelled(p0: DatabaseError) {
Log.e("onCancelled", " " + p0.message)
}
override fun onChildMoved(p0: DataSnapshot, p1: String?) {
Log.e("onChildMoved", " " + p0.key)
}
override fun onChildChanged(dataSnapshot: DataSnapshot, p1: String?) {
Log.e("onChildChanged", " " + dataSnapshot.key)
//Write your database reference login for getting Lat Lng
if (dataSnapshot.key.equals("l")) {
val latLatLng = dataSnapshot.value as ArrayList<Any>?
if (latLatLng!!.size == 2) {
displayLocation(latLatLng)
}
}
}
override fun onChildAdded(p0: DataSnapshot, p1: String?) {
Log.e("onChildAdded", " " + p0.key)
}
override fun onChildRemoved(p0: DataSnapshot) {
Log.e("onChildRemoved", " " + p0.key)
}
}
Add and update marker with starting zoom for First time
private fun displayLocation(latLatLng: ArrayList<Any>) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
REQUEST_LOCATION)
} else {
mLastLocation = Location(LocationManager.GPS_PROVIDER)
mLastLocation!!.latitude = latLatLng[0] as Double;
mLastLocation!!.longitude = latLatLng[1] as Double;
val latitude = latLatLng[0]!!
val longitude = latLatLng[1]!!
addMarker(mMap, latitude as Double, longitude as Double)
if (isFirstTime) {
isFirstTime = false
try {
var pickUPLatLng: LatLng = LatLng(pick_lat.toDouble(), pick_long.toDouble())
var deliveryLatLng: LatLng = LatLng(drop_lat.toDouble(), drop_long.toDouble())
var latLngBounds: LatLngBounds = LatLngBounds.Builder()
.include(pickUPLatLng)
.include(deliveryLatLng)
.include(LatLng(latitude as Double, longitude as Double))
.build()
lstLatLngRoute.clear()
try {
mLastLocation = Location(LocationManager.GPS_PROVIDER)
mLastLocation!!.longitude = longitude as Double
mLastLocation!!.latitude = latitude as Double
lstLatLngRoute.add(LatLng(mLastLocation!!.latitude, mLastLocation!!.longitude))
} catch (e: Exception) {
e.printStackTrace()
}
lstLatLngRoute.add(pickUPLatLng)
lstLatLngRoute.add(deliveryLatLng)
zoomRoute(mMap!!, lstLatLngRoute)
//mMap!!.moveCamera(CameraUpdateFactory.newLatLngBounds(latLngBounds, 250))
//mMap!!.animateCamera(CameraUpdateFactory.newLatLngZoom(LatLng(latitude as Double, longitude as Double), 14f))
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
Now add marker if not added on Google Map already or use the previous once
private var markerCount: Int = 0
private var marker: Marker? = null;
private var mLastLocation: Location? = null
fun addMarker(googleMap: GoogleMap?, lat: Double, lon: Double) {
if (markerCount == 1) {
try {
try {
animateMarker(marker!!, LatLng(mLastLocation!!.latitude, mLastLocation!!.longitude))
} catch (e: Exception) {
}
} catch (e: Exception) {
e.printStackTrace()
}
} else if (markerCount == 0) {
var pickUpBmp: Bitmap = BitmapFactory.decodeResource(resources, R.drawable.carbike)
mMap = googleMap
val latlong = LatLng(lat, lon)
marker = mMap!!.addMarker(MarkerOptions().position(LatLng(lat, lon))
.icon(BitmapDescriptorFactory.fromBitmap(pickUpBmp))
.anchor(0.5f, 0.5f)
.flat(false))
mMap!!.moveCamera(CameraUpdateFactory.newLatLngZoom(latlong, MAP_ZOOM_LEVEL))
markerCount = 1
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return
}
}
}
And at the last just animate the marker with the below method :
fun animateMarker( marker: Marker,toPosition: LatLng) {
val handler = Handler()
val start = SystemClock.uptimeMillis()
val proj = mMap!!.getProjection()
val startPoint = proj.toScreenLocation(marker.getPosition())
val startLatLng = proj.fromScreenLocation(startPoint)
val duration: Long = 3000
val interpolator = LinearInterpolator()
handler.post(object : Runnable {
override fun run() {
val elapsed = SystemClock.uptimeMillis() - start
val t = interpolator.getInterpolation(elapsed.toFloat() / duration)
val lng = t * toPosition.longitude + (1 - t) * startLatLng.longitude
val lat = t * toPosition.latitude + (1 - t) * startLatLng.latitude
marker.position = LatLng(lat, lng)
marker.rotation = (getBearingBetweenTwoPoints1(startLatLng, toPosition).toString().toFloat())
runOnUiThread(Runnable {
mMap!!.moveCamera(CameraUpdateFactory
.newCameraPosition(CameraPosition.Builder()
.target(toPosition)
.zoom(MAP_ZOOM_LEVEL)
.build()))
})
if (t < 1.0) {
handler.postDelayed(this, 16)
}
}
})
}
Use below methods for bearing or head calculate of marker
private fun getBearingBetweenTwoPoints1(latLng1: LatLng, latLng2: LatLng): Double {
val lat1 = degreesToRadians(latLng1.latitude)
val long1 = degreesToRadians(latLng1.longitude)
val lat2 = degreesToRadians(latLng2.latitude)
val long2 = degreesToRadians(latLng2.longitude)
val dLon = long2 - long1
val y = Math.sin(dLon) * Math.cos(lat2)
val x = Math.cos(lat1) * Math.sin(lat2) - (Math.sin(lat1)
* Math.cos(lat2) * Math.cos(dLon))
val radiansBearing = Math.atan2(y, x)
return radiansToDegrees(radiansBearing)
}
private fun degreesToRadians(degrees: Double): Double {
return degrees * Math.PI / 180.0
}
private fun radiansToDegrees(radians: Double): Double {
return radians * 180.0 / Math.PI
}
I hope this will help you, If you are familiar with Kotlin :)
@mohamedgaber No need to be confused, just update your listener like this and it will work for number of childrens(Markers) not for single marker(Be sure your markers are different). Just convert this listener in Koltin and use other code same given by me, I promise it will work very smooth like Uber.
myRef.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot ds : dataSnapshot.getChildren()) {
Double lang = ds.child("lang").getValue(Double.class);
Double lat = ds.child("lat").getValue(Double.class);
save = new model(lat, lang);
ll = new LatLng(save.getLat(), save.getLang());
displayLocation(ll)
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
回答4:
mMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
@Override
public boolean onMarkerClick(final Marker marker) {
dbref.addSnapshotListener(Mapsimport1.this, new EventListener<DocumentSnapshot>() {
@Override
public void onEvent(DocumentSnapshot documentSnapshot, FirebaseFirestoreException e) {
if (e != null) {
Toast.makeText(Mapsimport1.this, "Error while loading!", Toast.LENGTH_SHORT).show();
Log.d(TAG, e.toString());
return;
}
if (documentSnapshot.exists()) {
String acmilan = documentSnapshot.getString(marker.getTitle());
Log.d("acmilan**",acmilan);
marker.setSnippet(acmilan); }
}
});
return false;
}
});
来源:https://stackoverflow.com/questions/55567149/change-marker-position-by-realtime-lat-lang-in-firebase-database-without-added