Change marker position by Realtime lat & lang in Firebase database without added new one marker

霸气de小男生 提交于 2019-12-24 10:56:23

问题


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 use lng 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!