在 Firebase 数据库中通过实时经纬度更改标记位置,而无需添加新的标记
Change marker position by Realtime lat & lang in Firebase database without added new one marker
我的实时 Firebase 数据库
我尝试在某些时候改变许多 lat langs 的位置,就像 uber 中的汽车一样,
但是当我改变 lat 或 lang 或两者时,这不会改变标记的位置,但它会产生一个新的标记。我想当 lat 例如在实时数据库中更改时,标记也会根据该 lat
的值更改
我试试make condition
`if (marker != null)
setPosition
else "marker == null"`
add new marker
但问题是我有 >1 个标记,因此此解决方案仅在地图上显示一个标记,因为标记 != 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) {
}
});
}
当我在 firebase 中更改实时经纬度时,我预计标记的位置会发生变化,但结果创建了另一个标记。
试试这个。
并且您必须实施 OnMapReadyCallback
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
}
并且 mMap 设为私有或 public 变量。
调用for循环方法之前
插入跟随代码
mMap.clear();
您对 addValueEventListener 的使用方式有误。
可以在onCreate方法中提交addValueEventlistener,调用前也需要检查null。
在 OnMapReadyCallback 方法中只绑定 mMap 变量。
请使用以下 Kotlin 代码查看标记、动画和移动的完整跟踪:
首先添加您的 firebase 侦听器
mFirebaseRef?.child(mDriverId)!!.addChildEventListener(listener);
定义您的 firebase 侦听器
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)
}
}
首次使用开始缩放添加和更新标记
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()
}
}
}
}
现在添加标记(如果未在 Google 地图上添加或使用以前的一次
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
}
}
}
最后使用以下方法为标记设置动画:
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)
}
}
})
}
使用以下方法计算标记的方位或水头
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
}
如果您熟悉 Kotlin,希望这对您有所帮助 :)
@mohamedgaber 无需混淆,只需像这样更新您的侦听器,它将适用于多个孩子(标记)而不适用于单个标记(确保您的标记不同)。只需在 Koltin 中转换此侦听器并使用我提供的其他相同代码,我保证它会像 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) {
}
});
根据我对其他答案的评论,以下是我根据您的问题得出的结论。
此代码将在地图上显示多个标记,但数据库中 /users
下的每个 ID 仅显示一个标记。如果特定 ID 的位置发生变化,它的关联标记将被移动,而不会影响地图上的其他标记。
警告:以下代码将在 real-time 中更新您的地图。您可以缓存新的标记位置,并且每 2-5 秒左右只更新一次,具体取决于您的数据更改频率。
深入研究代码之前的一些快速说明:
- 每个标记都链接到它在数据库中名为
mNamedMarkers
的地图下的字符串 ID。
- 由于未提供
model
并且似乎无关紧要,因此我在下面的代码中将其省略。
- 我不确定您是否来自日耳曼背景,但
lng
是经度的缩写,在此上下文中不是 "lang"。我还将更改您的数据库条目以使用 lng
而不是 lang/long/longitude/etc(它可以节省 space 并消除混淆)。
- 在下面的代码中,我添加了
getMarkerOptions(key)
,这样您就可以添加代码以根据每个标记的 ID 为每个标记获取不同的图像、标题和文本。目前它将为每个标记生成相同的数据。
- 我为每个函数添加了 Javadoc mark-up 以总结每个函数的作用。
- 有几个待办事项需要进一步开发。
代码如下:
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');
}
}
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;
}
});
我的实时 Firebase 数据库
我尝试在某些时候改变许多 lat langs 的位置,就像 uber 中的汽车一样, 但是当我改变 lat 或 lang 或两者时,这不会改变标记的位置,但它会产生一个新的标记。我想当 lat 例如在实时数据库中更改时,标记也会根据该 lat
的值更改我试试make condition
`if (marker != null)
setPosition
else "marker == null"`
add new marker
但问题是我有 >1 个标记,因此此解决方案仅在地图上显示一个标记,因为标记 != 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) {
}
});
}
当我在 firebase 中更改实时经纬度时,我预计标记的位置会发生变化,但结果创建了另一个标记。
试试这个。
并且您必须实施 OnMapReadyCallback
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
}
并且 mMap 设为私有或 public 变量。
调用for循环方法之前
插入跟随代码
mMap.clear();
您对 addValueEventListener 的使用方式有误。
可以在onCreate方法中提交addValueEventlistener,调用前也需要检查null。
在 OnMapReadyCallback 方法中只绑定 mMap 变量。
请使用以下 Kotlin 代码查看标记、动画和移动的完整跟踪:
首先添加您的 firebase 侦听器
mFirebaseRef?.child(mDriverId)!!.addChildEventListener(listener);
定义您的 firebase 侦听器
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)
}
}
首次使用开始缩放添加和更新标记
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()
}
}
}
}
现在添加标记(如果未在 Google 地图上添加或使用以前的一次
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
}
}
}
最后使用以下方法为标记设置动画:
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)
}
}
})
}
使用以下方法计算标记的方位或水头
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
}
如果您熟悉 Kotlin,希望这对您有所帮助 :)
@mohamedgaber 无需混淆,只需像这样更新您的侦听器,它将适用于多个孩子(标记)而不适用于单个标记(确保您的标记不同)。只需在 Koltin 中转换此侦听器并使用我提供的其他相同代码,我保证它会像 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) {
}
});
根据我对其他答案的评论,以下是我根据您的问题得出的结论。
此代码将在地图上显示多个标记,但数据库中 /users
下的每个 ID 仅显示一个标记。如果特定 ID 的位置发生变化,它的关联标记将被移动,而不会影响地图上的其他标记。
警告:以下代码将在 real-time 中更新您的地图。您可以缓存新的标记位置,并且每 2-5 秒左右只更新一次,具体取决于您的数据更改频率。
深入研究代码之前的一些快速说明:
- 每个标记都链接到它在数据库中名为
mNamedMarkers
的地图下的字符串 ID。 - 由于未提供
model
并且似乎无关紧要,因此我在下面的代码中将其省略。 - 我不确定您是否来自日耳曼背景,但
lng
是经度的缩写,在此上下文中不是 "lang"。我还将更改您的数据库条目以使用lng
而不是 lang/long/longitude/etc(它可以节省 space 并消除混淆)。 - 在下面的代码中,我添加了
getMarkerOptions(key)
,这样您就可以添加代码以根据每个标记的 ID 为每个标记获取不同的图像、标题和文本。目前它将为每个标记生成相同的数据。 - 我为每个函数添加了 Javadoc mark-up 以总结每个函数的作用。
- 有几个待办事项需要进一步开发。
代码如下:
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');
}
}
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;
}
});