简体   繁体   English

谷歌地图:恢复地图活动时如何恢复标记

[英]Google Maps: How to restore markers when resuming map activity

I am making an app that collects data from a sensor.我正在制作一个从传感器收集数据的应用程序。 Every time the sensor sends data, a new marker is placed on a map.每次传感器发送数据时,都会在地图上放置一个新标记。 When I press back and then resume the map activity all the markers are gone.当我按下然后恢复地图活动时,所有标记都消失了。 Is there a way to save and restore each unique marker with its unique MarkerOptions (title,snippet,LatLng) when resuming the activity?有没有办法在恢复活动时使用其独特的 MarkerOptions(标题、片段、LatLng)保存和恢复每个独特的标记?

Here is my maps activity这是我的地图活动

 public class MapsActivity extends FragmentActivity implements OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener
{

private GoogleMap mMap;
GoogleApiClient mGoogleApiClient;
Location mLastLocation;
Marker mCurrLocationMarker;
private int co_mV;
private int no2_mV;
LocationRequest mLocationRequest;
private boolean initiateApp;
String currentTime;

TcpClient mTcpClient;
ArrayList<Marker> markerArrayList;

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

    if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        checkLocationPermission();
    }
    // 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);
    initiateApp = true;
    markerArrayList = new ArrayList<Marker>();

}

/**
 * Manipulates the map once available.
 * This callback is triggered when the map is ready to be used.
 * This is where we can add markers or lines, add listeners or move the camera. In this case,
 * we just add a marker near Sydney, Australia.
 * If Google Play services is not installed on the device, the user will be prompted to install
 * it inside the SupportMapFragment. This method will only be triggered once the user has
 * installed Google Play services and returned to the app.
 */
@Override
public void onMapReady(GoogleMap googleMap) {
    mMap = googleMap;
    mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
    mMap.setMyLocationEnabled(true);


    if(mGoogleApiClient == null) {
    //Initialize Google Play Services
    if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            buildGoogleApiClient();

        }
    }
    else {
            buildGoogleApiClient();
        }
    }
}

/* Here we create the infoWindow **/
protected synchronized void buildGoogleApiClient() {
    mGoogleApiClient = new GoogleApiClient.Builder(this)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .addApi(LocationServices.API)
            .build();
    mGoogleApiClient.connect();

}

@Override
public void onConnected(Bundle bundle) {

    getNewLocation();
    new ConnectTask().execute("");

}


public void newData(JSONObject d) {
    try {
        co_mV = d.getInt("co_mV");
        no2_mV = d.getInt("no2_mV");
    } catch (JSONException e) {
        e.printStackTrace();
    }

    getNewLocation();
}

public void getTime() {

    Calendar cal = Calendar.getInstance();
    currentTime = new SimpleDateFormat("HH:mm:ss").format(cal.getTime());

}

public void getNewLocation() {
    mLocationRequest = new LocationRequest();
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    if (ContextCompat.checkSelfPermission(this,
            Manifest.permission.ACCESS_FINE_LOCATION)
            == PackageManager.PERMISSION_GRANTED) {
        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
    }

}

@Override
public void onConnectionSuspended(int i) {

}

@Override
public void onLocationChanged(Location location) {

    if (markerArrayList.size()>1) {
        if(location.distanceTo(mLastLocation) < 30) {
            markerArrayList.get(markerArrayList.size()-1).remove();
            markerArrayList.remove(markerArrayList.size()-1);
            Toast.makeText(
                    getApplicationContext(),
                    "Reading to close to last reading, replaces last reading", Toast.LENGTH_SHORT).show();
        }
    }


    if (markerArrayList.size() == 8) {
        markerArrayList.get(0).remove();
        markerArrayList.remove(0);
    }

    //Place current location marker
    LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());

    if (!initiateApp) {
        MarkerOptions markerOptions = new MarkerOptions();
        markerOptions.position(latLng);
        markerOptions.title("Time of reading: " + currentTime);
        markerOptions.snippet("co: " + String.valueOf(co_mV) + " mV, " + "no2: " + String.valueOf(no2_mV) + " mV");
        markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA));
        mCurrLocationMarker = mMap.addMarker(markerOptions);
        markerArrayList.add(mCurrLocationMarker);

    }

    mLastLocation = location;


    Log.d("ADebugTag", "Value: " + Double.toString(location.getLatitude()));
    Log.d("ADebugTag", "Value: " + Double.toString(location.getLongitude()));


    //move map camera

    if(initiateApp){
        mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 15));
    }

    boolean contains = mMap.getProjection()
            .getVisibleRegion()
            .latLngBounds
            .contains(latLng);

    if(!contains){
        mMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));
    }

    initiateApp = false;
}

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {

}

public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;
public boolean checkLocationPermission(){
    if (ContextCompat.checkSelfPermission(this,
            Manifest.permission.ACCESS_FINE_LOCATION)
            != PackageManager.PERMISSION_GRANTED) {

        // Asking user if explanation is needed
        if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                Manifest.permission.ACCESS_FINE_LOCATION)) {

            // Show an explanation to the user *asynchronously* -- don't block
            // this thread waiting for the user's response! After the user
            // sees the explanation, try again to request the permission.

            //Prompt the user once explanation has been shown
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                    MY_PERMISSIONS_REQUEST_LOCATION);


        } else {
            // No explanation needed, we can request the permission.
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                    MY_PERMISSIONS_REQUEST_LOCATION);
        }
        return false;
    } else {
        return true;
    }
}

@Override
public void onRequestPermissionsResult(int requestCode,
                                       String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_LOCATION: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // permission was granted. Do the
                // contacts-related task you need to do.
                if (ContextCompat.checkSelfPermission(this,
                        Manifest.permission.ACCESS_FINE_LOCATION)
                        == PackageManager.PERMISSION_GRANTED) {

                    if (mGoogleApiClient == null) {
                        buildGoogleApiClient();
                    }
                    mMap.setMyLocationEnabled(true);
                }

            } else {

                // Permission denied, Disable the functionality that depends on this permission.
                Toast.makeText(this, "permission denied", Toast.LENGTH_LONG).show();
            }
            return;
        }

        // other 'case' lines to check for other permissions this app might request.
        // You can add here other case statements according to your requirement.
    }
}

ublic JSONObject getNewJSON(JSONObject json) {
    try {

        int humidity = json.getInt("humidity_ppm");
        int pressure = json.getInt("pressure_Pa");
        int noise = json.getInt("noise_dB");
        double lat = mLastLocation.getLatitude();
        double lng = mLastLocation.getLongitude();
        long time = System.currentTimeMillis() / 1000L;

       JSONObject c = new JSONObject();
        c.put("time",time);
        c.put("lat",lat);
        c.put("long",lng);
        c.put("humidity",humidity);
        c.put("pressure",pressure);
        c.put("noise_dB",noise);
        return c;

    } catch (JSONException e) {
        e.printStackTrace();
    }

return null;
}


public class ConnectTask extends AsyncTask<String, String, TcpClient> {

    @Override
    protected TcpClient doInBackground(String... message) {

        //we create a TCPClient object
        mTcpClient = new TcpClient(new TcpClient.OnMessageReceived() {
            @Override
            //here the messageReceived method is implemented
            public void messageReceived(String message) {
                //this method calls the onProgressUpdate
                publishProgress(message);
            }
        });
        mTcpClient.run();

        return null;
    }

    @Override
    protected void onProgressUpdate(String... values) {
        super.onProgressUpdate(values);


        try {
            JSONObject parser = new JSONObject(values[0]);
            JSONObject d = parser.getJSONObject("d");
            JSONObject cloudData = getNewJSON(d);

            if (mLastLocation != null) {
                newData(d);
                getTime();
            }

            System.out.println(cloudData);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        //process server response here....
    }

}

You said你说

When I press back and then resume the map当我按下返回然后恢复地图时

right?对? Not closing your app.不关闭您的应用程序。

Simplest way is to override the onBackPressed method, like this:最简单的方法是覆盖onBackPressed方法,如下所示:

@Override
public void onBackPressed() {
    moveTaskToBack(true);
}

This won't trigger onDestroy() and onCreate() again, so your would leave your Activity instance unmodified.这不会再次触发onDestroy()onCreate() ,因此您不会修改您的 Activity 实例。 Simple enough, huh?够简单了吧?

You can make a function that populates with fresh data from your server.您可以创建一个使用来自您的服务器的新数据填充的函数。 Then you can call that function as soon as the server sends the JSON data.然后,您可以在服务器发送 JSON 数据后立即调用该函数。 To answer your question, you simply call the same function in onResume.要回答您的问题,您只需在 onResume 中调用相同的函数即可。 For more information about activity state read this .有关活动状态的更多信息,请阅读此内容

The other approach is to save it in a savedInstanceState bundle and do check in onSaveInstanceState just like this SO accepted answer if that bundle is not null, if so populate the map with the List(last known locations of the markers, if that works with your project).另一种方法是将它保存在一个 savedInstanceState 包中,并像这个 SO 接受的答案一样检查 onSaveInstanceState 如果该包不为空,如果是这样,则用列表(标记的最后已知位置,如果这适用于您的项目)。 Otherwise just load the map.否则只需加载地图。

I found this answer which provides an elegant one-line solution.我发现这个答案提供了一个优雅的单行解决方案。 This will restore markers when the user rotates the phone, but not when he presses the Back button (I know this is not what OP asked but Google will lead people here)这将在用户旋转手机时恢复标记,但在他按下后退按钮时不会恢复标记(我知道这不是 OP 要求的,但谷歌会引导人们到这里)

Add the following in onCreate :onCreate添加以下内容:

// mapFragment is a SupportMapFragment 
mapFragment.setRetainInstance(true);

In manifest file, you can add this line:在清单文件中,您可以添加以下行:

    <activity android:name=".MapsActivity"
              android:configChanges="orientation|screenSize">

It was one of the simplest solution that worked for me in tandem with这是对我有用的最简单的解决方案之一

@Override
public void onBackPressed() {
moveTaskToBack(true);
}

, and works for simple enough apps, though onSaveInstanceState() method is recommended too. ,并且适用于足够简单的应用程序,但也推荐使用 onSaveInstanceState() 方法。 What I found from my research is it's tough to serialize and deserialize marker lists, and onSaveInstanceState() only works with simple objects like strings and such.我从我的研究中发现,序列化和反序列化标记列表很困难,而且 onSaveInstanceState() 仅适用于字符串等简单对象。 If anyone found a better solution, please let me know too :)如果有人找到更好的解决方案,也请告诉我:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM