Вопрос

I'm displaying a Google Maps API v2 MapView in an Android app, but it's curiously not updating properly in a consistent fashion. I'm using the GPS to update the map position (tried both LocationManager and LocationClient) and although the map moves to the position, about fifty percent of the time either the street name layer fails to update or there's a fuzzy/blurry section that fails to update--until I do a manual drag (scroll) of the map. Then the entire map updates instantly. I've stripped out a lot of the processing in the app to see if I was somehow preventing a refresh, but it didn't make a difference.

I inserted a mapView.invalidate() call in onCameraChange but that oddly seemed to make the problem occur more readily (although still not 100% of the time).

I am implementing all of the Activity callbacks as required by MapView.

Has anyone encountered a problem like this with Google Map API v2 on Android? If so, did you identify the cause and how did you solve it?

Это было полезно?

Решение

You have to let the map breathe so to speak.

Use animateCamera with a CancelableCallback then when the animation is completed you'll get a call back to onFinish() start the next animation.

public class KmlReader extends ActionBarActivity implements
    CancelableCallback {


@Override
public void onFinish() {
    startAnimation(); // start next map movement
}


@Override
public void onCancel() {
    //Called when user interacts with the map while it is moving.
}


public void startAnimation(){

cameraPosition = mMap.getCameraPosition();    
LatLng ll = new LatLng(expectedLocation.getLatitude(),
                    expectedLocation.getLongitude());
            cb.zoom(cameraPosition.zoom)
            // previous camera tilt
                    .tilt(cameraPosition.tilt)
                    // new expected destination
                    .target(ll)
                    // north up or heading view
                    .bearing((isHeading) ? bearing : 0f);
            cameraPosition = cb.build();
            CameraUpdate update = CameraUpdateFactory
                    .newCameraPosition(cameraPosition);
            mMap.animateCamera(update, working_interval, this);
}

* Edit this is the code I'm working on now.* it uses an asynctask for the calcuations. I've given it a walking test but haven't tested it in a vehicle.

private static CameraPosition currentCameraPosition;
private static com.google.android.gms.maps.model.CameraPosition.Builder cameraPositionBuilder;
private volatile CameraUpdate nextCameraUpdate;
// updates 
private static final long UPDATE_INTERVAL = 2500;
// fastest 
private static final int FASTEST_INTERVAL = 2500;
private static int working_interval = 5000; 
private volatile boolean isAnimating;


// Define the callback method that receives location updates
@SuppressLint("NewApi")
@Override
public void onLocationChanged(Location location) {
    Log.d("test", Boolean.toString(isAnimating)  +" onlocation");
    currentCameraPosition = mMap.getCameraPosition();

    NewCameraUpdateTask newCameraUpdateTask = new NewCameraUpdateTask();

// This task must run async
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
    newCameraUpdateTask.executeOnExecutor(
            AsyncTask.THREAD_POOL_EXECUTOR, location);
} else {
    newCameraUpdateTask.execute(location);
}
    // speed display
    setMetersPerSecond(location.getSpeed());
}

// create a newCameraUpdate to move the map with
private class NewCameraUpdateTask extends
        AsyncTask<Location, Void, CameraUpdate> {

    @Override
    protected CameraUpdate doInBackground(Location... params) {
        Location workingLocation = null;
        CameraUpdate newCameraUpdate = null;

        float bearing = 0f;
        float speed = 0f;

        for (Location mlocation : params) {
            speed = mlocation.getSpeed();

            // camera position is saved before the start of each animation.
            LatLng ll;

        if (!mlocation.hasBearing() || speed == 0) {
            workingLocation = mlocation;
            // previous bearing
        } else {
            // current bearing
            bearing = mlocation.getBearing();
            // calculate the age of the location
            // atempt for animation to end a little bit past when
            // the
            // next
            // location arrives.
            // (location.getSpeed()m/s)(1/1000 interval seconds)(
            // 1/1000
            // km/m)
            // (1/6371 radians/km) = radians/6371000000.0
            double expectedDistance = working_interval / 6371000000.0
                    * speed;

            // latitude in Radians
            double currentLatitude = Math.toRadians(mlocation
                    .getLatitude());
            // longitude in Radians
            double currentlongitude = Math.toRadians(mlocation
                    .getLongitude());

            double calcBearing = Math.toRadians(bearing);

            // the camera position is needed so I can put in the
            // previous camera bearing when the location has no
            // bearing. This should prevent the map from
            // zooming to north when the device stops moving.

            // calculate the expected latitude and longitude based
            // on
            // staring
            // location
            // , bearing, and distance
            double sincurrentLatitude = Math.sin(currentLatitude);
            double coscurrentLatitude = Math.cos(currentLatitude);
            double cosexpectedDistance = Math.cos(expectedDistance);
            double sinexpectedDistance = Math.sin(expectedDistance);

            double expectedLatitude = Math.asin(sincurrentLatitude
                    * cosexpectedDistance + coscurrentLatitude
                    * sinexpectedDistance * Math.cos(calcBearing));
            double a = Math.atan2(
                    Math.sin(calcBearing) * sinexpectedDistance
                            * coscurrentLatitude,
                    cosexpectedDistance - sincurrentLatitude
                            * Math.sin(expectedLatitude));
            double expectedLongitude = currentlongitude + a;
            expectedLongitude = (expectedLongitude + PI3) % PI2 - PI;

            // convert to degrees for the expected destination
            double expectedLongitudeDestination = Math
                    .toDegrees(expectedLongitude);
            double expectedLatitudeDestination = Math
                    .toDegrees(expectedLatitude);

            mlocation.setLatitude(expectedLatitudeDestination);
            mlocation.setLongitude(expectedLongitudeDestination);
            workingLocation = mlocation;

        }
        break;
    }

    if (workingLocation != null) {
        if (workingLocation.hasBearing()) {
            bearing = workingLocation.getBearing();
        } else {
            bearing = currentCameraPosition.bearing;
        }
        LatLng ll = new LatLng(workingLocation.getLatitude(),
                workingLocation.getLongitude());
        cameraPositionBuilder.zoom(currentCameraPosition.zoom)
        // previous camera tilt
                .tilt(currentCameraPosition.tilt)
                // new expected destination
                .target(ll)
                // north up or heading view
                .bearing((isHeading) ? bearing : 0f);
        newCameraUpdate = CameraUpdateFactory
                .newCameraPosition(cameraPositionBuilder.build());
    }

    return newCameraUpdate;
}

@Override
protected void onPostExecute(CameraUpdate result) {
    Log.d("test", Boolean.toString(isAnimating) + " onPostExecute");
    if (result != null) {
        nextCameraUpdate = result;
        // stop the currently playing animation
        // there is a new one ready to start
        if (isAnimating) {
            if (mMap != null) {
                mMap.stopAnimation();
            }
        }
        // start the next animation
        startAnimation();
        Log.d("test", Boolean.toString(isAnimating)  +" onPostExecuteComplete");
    }


}
}


// called when map animation has been canceled
@Override
public void onCancel() {
    Log.d("test", Boolean.toString(isAnimating)  +" oncancel");
    isAnimating = false;
}

@Override
public void onFinish() {
    Log.d("test", Boolean.toString(isAnimating)  +" onfinish");
    isAnimating = false;
    startAnimation();

    // call to start saved animation.
}

private void startAnimation() {
    Log.d("test", Boolean.toString(isAnimating)  +" startAnimation");
    if (action_track) {
        if (isAnimating) {
            return;
        }
        if (nextCameraUpdate == null) {
            return;
        }
        // abort if animating
        isAnimating = true;
        CameraUpdate animateCameraUpdate = nextCameraUpdate;
        nextCameraUpdate = null;
        mMap.animateCamera(animateCameraUpdate, working_interval, this);
        Log.d("test", Boolean.toString(isAnimating)  +" startanimateCamera");
    }
}

Другие советы

In case someone is having this issue, the key is to keep the animateCamera call's animation duration less than the frequency with which you are calling the animateCamera method.

For example if you call animateCamera every 1000ms set tha animate camera duration to be less than that.

map.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, ZOOM_LEVEL), 900, null)

If your animateCamera call is not being called at a constant time, then danny117's answer to use the callback to trigger the next camera update will work perfectly.

Inspired from danny117's solution, I found a simpler solution. I set the location request update for each 5 millisecond and I set the animate camera duration to be 2.5 ms. Problem solved

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top