Отслеживание местоположения пользователя в режиме реального времени с помощью полилиний в Картах Google (Android)

У меня есть некоторый опыт работы с Android, но я новичок в использовании API Карт Google и Служб определения местоположения. Я пытаюсь показать движение пользователя на MapFragment, перемещая трекер и рисуя ломаную линию над тем местом, где был пользователь, вроде MapMyRun.

Из того, что я видел в своем отладчике, я получаю местоположение после возобновления фрагмента, и оно добавляется к списку LatLng. Мне нужно, чтобы карта обновлялась каждые несколько секунд или каждый раз, когда местоположение меняется на десятую часть мили или около того.

Вот что у меня есть для действия, содержащего MapFragment. Мой инстинкт подсказывает мне, что мне нужно сделать что-то внутри метода handleNewLocation, чтобы обновить карту... либо это, либо, возможно, сделать что-то в LocationProvider, чтобы чаще обновлять местоположение.

Будем признательны за любую помощь, советы или ссылки! Моя цель - научиться, чтобы понять, как я могу делать это лучше в будущем :)!

package com.seniorproject.trafton.trackrecordrace;

//Import statements not shown for readability
import...

public class MapsActivity extends AppCompatActivity implements  LocationProvider.LocationCallback {

public static final String TAG = "cloudsTraf";
        //MapsActivity.class.getSimpleName();

private GoogleMap mMap; // Might be null if Google Play services APK is not available.

private LocationProvider mLocationProvider;

//variable for toggle buttons
private boolean isRunning = false;

//ArrayList to store geopoints for current run
private ArrayList<LatLng> geoPoints;

//Array of distances
private ArrayList<Float> distances;

//total distance
private float totalDistance;

//polyline that represented the route
Polyline tracker;

/*Load up widgets for tracking */
private ImageButton mPlayButton;
private ImageButton mPauseButton;

private TextView mRunTimeText;
private TextView mRunSpeedText;
private TextView mRunDistText;
private TextView mRunCalsText;

private Boolean mIsPlayButtonClicked;

//Handler to control timer tracking
//Thank you to Nikos Maravitsas for the tutorial on timers

private Handler timeHandler = new Handler();

private long startTime = 0L;
long timeInMillis = 0L;
long timeSwapBuffer = 0L;
long updatedTime = 0L;





@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_maps);
    setUpMapIfNeeded();

    mLocationProvider = new LocationProvider(this, this);
    geoPoints = new ArrayList<LatLng>(); //added

    //Add in code to inflate the tracking modules
    mRunTimeText = (TextView) findViewById(R.id.run_time_text);
    mRunDistText = (TextView) findViewById(R.id.run_dist_text);
    mRunSpeedText = (TextView) findViewById(R.id.run_speed_text);

    Toolbar runToolbar= (Toolbar) findViewById(R.id.toolbar_run);
    runToolbar.setTitle("Run on " + getDate());
    runToolbar.setTitleTextColor(Color.WHITE);
    setSupportActionBar(runToolbar);

}

//Inflate the menu for the toolbar
public boolean onCreateOptionsMenu(Menu menu) {
    Log.e("XXX", "Menu created");
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.menu_maps_run, menu);
    return super.onCreateOptionsMenu(menu);
}

//Handles possibilities of menu items being selected.
@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle item selection
    switch (item.getItemId()) {
        case R.id.action_begin_run:
            if (!isRunning) {
                startTime = SystemClock.uptimeMillis();
                timeHandler.postDelayed(updateTimerThread,0);
                item.setIcon(R.drawable.ic_pause_white_24dp);
                isRunning = true;
            }
            else {
                timeSwapBuffer += timeInMillis;
                timeHandler.removeCallbacks(updateTimerThread);
                item.setIcon(R.drawable.ic_play_arrow_white_24dp);
                isRunning = false;

            }
            return true;

        case R.id.action_stop_run:
            //stop run, save run, and transport user to the stats for that run

            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

/*Code to update the timer, begins a new timer thread.*/
private Runnable updateTimerThread = new Runnable() {
    public void run(){
        timeInMillis = SystemClock.uptimeMillis() - startTime;
        updatedTime = timeSwapBuffer + timeInMillis;

        //Get integer value from time update and put into textView
        int seconds = (int) (updatedTime/1000);
        //need two seconds variables for formatting purposes.
        int secs = seconds % 60;
        int mins = (seconds / 60);
        int hours = (mins / 60);

        mRunTimeText.setText("" + hours + ":" +
                String.format("%02d", mins) + ":" +
                String.format("%02d", secs));
        timeHandler.postDelayed(this, 0);
    }

};

/* */

@Override
protected void onResume() {
    super.onResume();
    setUpMapIfNeeded();
    mLocationProvider.connect();
}

@Override
protected void onPause() {
    super.onPause();
    mLocationProvider.disconnect();
}

/**
 * Sets up the map if it is possible to do so (i.e., the Google Play services APK is correctly
 * installed) and the map has not already been instantiated.. This will ensure that we only ever
 * call {@link #setUpMap()} once when {@link #mMap} is not null.
 * <p/>
 * If it isn't installed {@link SupportMapFragment} (and
 * {@link com.google.android.gms.maps.MapView MapView}) will show a prompt for the user to
 * install/update the Google Play services APK on their device.
 * <p/>
 * A user can return to this FragmentActivity after following the prompt and correctly
 * installing/updating/enabling the Google Play services. Since the FragmentActivity may not
 * have been completely destroyed during this process (it is likely that it would only be
 * stopped or paused), {@link #onCreate(Bundle)} may not be called again so we should call this
 * method in {@link #onResume()} to guarantee that it will be called.
 */
private void setUpMapIfNeeded() {
    // Do a null check to confirm that we have not already instantiated the map.
    if (mMap == null) {
        // Try to obtain the map from the SupportMapFragment.
        mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
                .getMap();
        // Check if we were successful in obtaining the map.
        if (mMap != null) {
            setUpMap();
        }
    }
}

/**
 * This should only be called once and when we are sure that {@link #mMap} is not null.
 */
private void setUpMap() {
    //mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker"));
}

//handle new location
public void handleNewLocation(Location location) {
    Log.d(TAG, location.toString());

    double currentLatitude = location.getLatitude();
    double currentLongitude = location.getLongitude();
    LatLng latLng = new LatLng(currentLatitude, currentLongitude);

    //Get the new geopoints to redraw the line on each iteration
    geoPoints.add(latLng);
    //get the latest distance update
    if (geoPoints.size() > 2) {
        calculateDistance();
    }
    //set the distance test
    mRunDistText.setText(Float.toString(totalDistance) + " Meters");
    mRunSpeedText.setText((location.getSpeed() + " m/s"));

    //draw the polyline
    drawRoute();
    MarkerOptions options = new MarkerOptions()
            .position(latLng)
            .title("I am here!");
    mMap.addMarker(options);
    mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng,15));
}

/*
*Methods to calculate metrics. All measurements returned are approximations.
 */
//returns the latest distance between geoPoints. Append to total number
public void calculateDistance(){
    Location newLoc = new Location("Latest Location");
    Location oldLoc = new Location("Last known Location");
    LatLng newPt = geoPoints.get(geoPoints.size()- 1);
    LatLng oldPt = geoPoints.get(geoPoints.size()-2);
    distances.add(oldLoc.distanceTo(newLoc));
    //add to the distance variable
    totalDistance = totalDistance + oldLoc.distanceTo(newLoc);
    Log.d(TAG, "distance between points is: " + oldLoc.distanceTo(newLoc));
}

//calculates the current KCals being burned
public void calculateKcals(){

}

//get today's date in a simple format
public String getDate(){
    Date today = Calendar.getInstance().getTime();
    SimpleDateFormat formatter = new SimpleDateFormat("EEE MMM dd");
    String todaysDate = formatter.format(today);
    return todaysDate;
}

//method to draw polyline. Uses the recorded geopoints.
public void drawRoute(){
    mMap.clear();
    PolylineOptions options = new PolylineOptions().width(5).color(android.R.color.holo_blue_dark).geodesic(true).visible(true);
    for(int i = 0; i < geoPoints.size(); i++){
        LatLng pt = geoPoints.get(i);
        options.add(pt);
    }
    Log.d(TAG,"GeoPoints recorded: " + geoPoints);
    mMap.addPolyline(options);
}

}

Я беру местоположение в другом файле (потому что модульность — это хорошо). Если есть какие-либо дополнительные данные, которые я могу предоставить, я был бы более чем счастлив включить их!

package com.seniorproject.trafton.trackrecordrace;

import android.app.Activity;
import android.content.Context;
import android.content.IntentSender;
import android.location.Location;
import android.os.Bundle;
import android.util.Log;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;


public class LocationProvider implements
    GoogleApiClient.ConnectionCallbacks,
    GoogleApiClient.OnConnectionFailedListener,
    LocationListener {

public abstract interface LocationCallback {
    public void handleNewLocation(Location location);
}

public static final String TAG = LocationProvider.class.getSimpleName();

/*
 * Define a request code to send to Google Play services
 * This code is returned in Activity.onActivityResult
 */
private final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;

private LocationCallback mLocationCallback;
private Context mContext;
private GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;


public LocationProvider(Context context, LocationCallback callback) {
    mGoogleApiClient = new GoogleApiClient.Builder(context)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .addApi(LocationServices.API)
            .build();

    mLocationCallback = callback;

    // Create the LocationRequest object
    mLocationRequest = LocationRequest.create()
            .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
            .setInterval(3 * 1000)        // 10 seconds, in milliseconds
            .setFastestInterval(1 * 1000); // 1 second, in milliseconds

    mContext = context;
}

public void connect() {
    mGoogleApiClient.connect();
}

public void disconnect() {
    if (mGoogleApiClient.isConnected()) {
        LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
        mGoogleApiClient.disconnect();
    }
}

@Override
public void onConnected(Bundle bundle) {
    Log.i(TAG, "Location services connected.");

    Location location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
    if (location == null) {
        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
    }
    else {
        mLocationCallback.handleNewLocation(location);
    }
}

@Override
public void onConnectionSuspended(int i) {

}

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
    /*
     * Google Play services can resolve some errors it detects.
     * If the error has a resolution, try sending an Intent to
     * start a Google Play services activity that can resolve
     * error.
     */
    if (connectionResult.hasResolution() && mContext instanceof Activity) {
        try {
            Activity activity = (Activity)mContext;
            // Start an Activity that tries to resolve the error
            connectionResult.startResolutionForResult(activity, CONNECTION_FAILURE_RESOLUTION_REQUEST);
        /*
         * Thrown if Google Play services canceled the original
         * PendingIntent
         */
        } catch (IntentSender.SendIntentException e) {
            // Log the error
            e.printStackTrace();
        }
    } else {
        /*
         * If no resolution is available, display a dialog to the
         * user with the error.
         */
        Log.i(TAG, "Location services connection failed with code " + connectionResult.getErrorCode());
    }
}

@Override
public void onLocationChanged(Location location) {
    mLocationCallback.handleNewLocation(location);
    Log.i(TAG, "New location received ");
}

}


person cloudyGirrl    schedule 09.02.2016    source источник


Ответы (1)


Чтобы нарисовать полилинию на карте, сделайте это в onLocationChanged.

Polyline route = mMap.addPolyline(new PolylineOptions());
route.setPoints(mGeoPoints);`
person Aleksandar Vasilevski    schedule 25.09.2017