16

我切换到 Android 版地图 v2,并尝试移植以下功能:

使用 MyLocationOverlay,我可以显示设备的当前位置(蓝点)。当用户的位置发生变化并且点到达可见区域的边缘时,地图会实时显示动画,因此点成为视图的中心。

在 v2 中,我将 SupportMapFragment 与 getMap().setMyLocationEnabled(true) 一起使用。当前位置显示为蓝点(箭头),但当设备更改位置时,地图视图不会移动,并且该点最终会离开视图。

有任何想法吗?

4

3 回答 3

24

您需要将 LocationSource 添加到您的 GoogleMap 并响应 onLocationChanged 事件。这是一个简单的类,它询问用户的位置,然后等到用户的位置可用,然后将地图设置为以他们的位置为中心。

public class MyLocationMapFragmentActivity extends FragmentActivity implements LocationListener, LocationSource
{
/**
 * Note that this may be null if the Google Play services APK is not available.
 */
private GoogleMap mMap;

private OnLocationChangedListener mListener;
private LocationManager locationManager;

@Override
protected void onCreate(Bundle savedInstanceState) 
{
    super.onCreate(savedInstanceState);

    setContentView(R.layout.basic_map);

    locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);

    if(locationManager != null)
    {
        boolean gpsIsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
        boolean networkIsEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

        if(gpsIsEnabled)
        {
            locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000L, 10F, this);
        }
        else if(networkIsEnabled)
        {
            locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 5000L, 10F, this);
        }
        else
        {
            //Show an error dialog that GPS is disabled.
        }
    }
    else
    {
        //Show a generic error dialog since LocationManager is null for some reason
    }

    setUpMapIfNeeded();
}

@Override
public void onPause()
{
    if(locationManager != null)
    {
        locationManager.removeUpdates(this);
    }

    super.onPause();
}

@Override
public void onResume()
{
    super.onResume();

    setUpMapIfNeeded();

    if(locationManager != null)
    {
        mMap.setMyLocationEnabled(true);
    }
}


/**
 * 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 Activity after following the prompt and correctly
 * installing/updating/enabling the Google Play services. Since the Activity 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.basicMap)).getMap();
        // Check if we were successful in obtaining the map.

        if (mMap != null) 
        {
            setUpMap();
        }

        //This is how you register the LocationSource
        mMap.setLocationSource(this);
    }
}

/**
 * This is where we can add markers or lines, add listeners or move the camera. In this case, we
 * just add a marker near Africa.
 * <p>
 * This should only be called once and when we are sure that {@link #mMap} is not null.
 */
private void setUpMap() 
{
    mMap.setMyLocationEnabled(true);
}

@Override
public void activate(OnLocationChangedListener listener) 
{
    mListener = listener;
}

@Override
public void deactivate() 
{
    mListener = null;
}

@Override
public void onLocationChanged(Location location) 
{
    if( mListener != null )
    {
        mListener.onLocationChanged( location );

        //Move the camera to the user's location once it's available!
        mMap.animateCamera(CameraUpdateFactory.newLatLng(new LatLng(location.getLatitude(), location.getLongitude())));
    }
}

@Override
public void onProviderDisabled(String provider) 
{
    // TODO Auto-generated method stub
    Toast.makeText(this, "provider disabled", Toast.LENGTH_SHORT).show();
}

@Override
public void onProviderEnabled(String provider) 
{
    // TODO Auto-generated method stub
    Toast.makeText(this, "provider enabled", Toast.LENGTH_SHORT).show();
}

@Override
public void onStatusChanged(String provider, int status, Bundle extras) 
{
    // TODO Auto-generated method stub
    Toast.makeText(this, "status changed", Toast.LENGTH_SHORT).show();
}
}

这应该跟随用户并在地图发生变化时保持地图居中在他们的位置上 - 如果您只想在用户“离开屏幕”时将地图居中,那么您可以检查用户的位置是否在地图的可见边界

@Override
public void onLocationChanged(Location location) 
{
    if( mListener != null )
    {
        mListener.onLocationChanged( location );

        LatLngBounds bounds = this.mMap.getProjection().getVisibleRegion().latLngBounds;

        if(!bounds.contains(new LatLng(location.getLatitude(), location.getLongitude())))
        {
             //Move the camera to the user's location if they are off-screen!
             mMap.animateCamera(CameraUpdateFactory.newLatLng(new LatLng(location.getLatitude(), location.getLongitude())));
        }
    }
}

以下是关于该主题的简短文章: Google Maps Android API V2 MyLocation LocationSource and event handling

更新

我编辑了获取用户位置的方式(参见 onCreate 中的 locationManager 代码)。我发现使用“getBestProvider”方法并不可靠,而且不能在多个设备上工作。我有几个用户抱怨他们的设备永远找不到他们的位置,无论我制定的标准多么宽松。似乎手动选择 GPS 或网络普遍适用。

于 2012-12-06T22:08:52.780 回答
14

更新:谷歌介绍了新的LocationClient和相关LocationListener的(OnMyLocationChangeListener界面现在已弃用)。所以现在相机的自动居中是一项微不足道的任务。


尽管 spotdog 已经回答了我想分享我自己的示例的问题,但我希望这将有助于 Android 初学者更好地了解LocationSource为 my-location 层进行自定义需要什么。希望您会发现代码有很好的文档记录/注释。

public class PlaceMapFragment extends SupportMapFragment {

    // Note that this may be null if the Google Play services APK is not available.
    private GoogleMap mMap;

    protected PlaceActivity activity;
    private FollowMeLocationSource followMeLocationSource;
    private Context mContext;

    /* We need the Context in order to get a reference to the Location Manager
     * (when instantiating this fragment from your activity use:
     *  PlaceMapFragment mapFragment = new PlaceMapFragment(this); ) */
    public PlaceMapFragment(Context context) {
        this.mContext = context;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        activity = (PlaceActivity)getActivity();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // creates our custom LocationSource and initializes some of its members
        followMeLocationSource = new FollowMeLocationSource();

        /* We can't be guaranteed that the map is available because Google Play services might not be available.
         * (un-comment the following line when using this code in a FragmentActivity / Activity 
         *  to try get a reference to the map here !) */
        //setUpMapIfNeeded();
    }

    @Override
    public void onResume() {
        super.onResume();

        /* We query for the best Location Provider everytime this fragment is displayed
         * just in case a better provider might have become available since we last displayed it */
        followMeLocationSource.getBestAvailableProvider();

        // Get a reference to the map/GoogleMap object
        setUpMapIfNeeded();

        /* Enable the my-location layer (this causes our LocationSource to be automatically activated.)
         * While enabled, the my-location layer continuously draws an indication of a user's
         * current location and bearing, and displays UI controls that allow a user to interact
         * with their location (for example, to enable or disable camera tracking of their location and bearing).*/
        mMap.setMyLocationEnabled(true);
    }

    @Override
    public void onPause() {
        /* Disable the my-location layer (this causes our LocationSource to be automatically deactivated.) */
        mMap.setMyLocationEnabled(false);

        super.onPause();
    }

    /**
     * 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
     * manipulate the map once when it {@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.
     */
    private void setUpMapIfNeeded() {
        // Do a null check to confirm that we have not already instantiated the map.
        if (mMap == null) {
            mMap = getMap();
            // Check if we were successful in obtaining the map.
            if (mMap != null) {
                // The Map is verified. It is now safe to manipulate the map:

                // Replace the (default) location source of the my-location layer with our custom LocationSource
                mMap.setLocationSource(followMeLocationSource);

                // Set default zoom
                mMap.moveCamera(CameraUpdateFactory.zoomTo(15f));
            }
        }
    }

    /* Our custom LocationSource. 
     * We register this class to receive location updates from the Location Manager
     * and for that reason we need to also implement the LocationListener interface. */
    private class FollowMeLocationSource implements LocationSource, LocationListener {

        private OnLocationChangedListener mListener;
        private LocationManager locationManager;
        private final Criteria criteria = new Criteria();
        private String bestAvailableProvider;
        /* Updates are restricted to one every 10 seconds, and only when
         * movement of more than 10 meters has been detected.*/
        private final int minTime = 10000;     // minimum time interval between location updates, in milliseconds
        private final int minDistance = 10;    // minimum distance between location updates, in meters

        private FollowMeLocationSource() {
            // Get reference to Location Manager
            locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);

            // Specify Location Provider criteria
            criteria.setAccuracy(Criteria.ACCURACY_FINE);
            criteria.setPowerRequirement(Criteria.POWER_LOW);
            criteria.setAltitudeRequired(true);
            criteria.setBearingRequired(true);
            criteria.setSpeedRequired(true);
            criteria.setCostAllowed(true);
        }

        private void getBestAvailableProvider() {
            /* The preffered way of specifying the location provider (e.g. GPS, NETWORK) to use 
             * is to ask the Location Manager for the one that best satisfies our criteria.
             * By passing the 'true' boolean we ask for the best available (enabled) provider. */
            bestAvailableProvider = locationManager.getBestProvider(criteria, true);
        }

        /* Activates this provider. This provider will notify the supplied listener
         * periodically, until you call deactivate().
         * This method is automatically invoked by enabling my-location layer. */
        @Override
        public void activate(OnLocationChangedListener listener) {
            // We need to keep a reference to my-location layer's listener so we can push forward
            // location updates to it when we receive them from Location Manager.
            mListener = listener;

            // Request location updates from Location Manager
            if (bestAvailableProvider != null) {
                locationManager.requestLocationUpdates(bestAvailableProvider, minTime, minDistance, this);
            } else {
                // (Display a message/dialog) No Location Providers currently available.
            }
        }

        /* Deactivates this provider.
         * This method is automatically invoked by disabling my-location layer. */
        @Override
        public void deactivate() {
            // Remove location updates from Location Manager
            locationManager.removeUpdates(this);

            mListener = null;
        }

        @Override
        public void onLocationChanged(Location location) {
            /* Push location updates to the registered listener..
             * (this ensures that my-location layer will set the blue dot at the new/received location) */
            if (mListener != null) {
                mListener.onLocationChanged(location);
            }

            /* ..and Animate camera to center on that location !
             * (the reason for we created this custom Location Source !) */
            mMap.animateCamera(CameraUpdateFactory.newLatLng(new LatLng(location.getLatitude(), location.getLongitude())));
        }

        @Override
        public void onStatusChanged(String s, int i, Bundle bundle) {

        }

        @Override
        public void onProviderEnabled(String s) {

        }

        @Override
        public void onProviderDisabled(String s) {

        }
    }

}
于 2013-01-13T16:25:49.203 回答
0

只需使用OnMyLocationChangeListener,覆盖该方法public void onMyLocationChange(Location location)。用于GoogleMap.setOnMyLocationChangeListener(your listener)注册您的听众。

public void onMyLocationChange(Location location) {
        changeMapLocation(location);
    }

    private void changeMapLocation(Location location) {
        LatLng latlong = new LatLng(location.getLatitude(),
                location.getLongitude());
        map.moveCamera(CameraUpdateFactory.newLatLngZoom(latlong, 15));

        // Zoom in, animating the camera.
        map.animateCamera(CameraUpdateFactory.zoomTo(10), 2000, null);
    }
于 2013-04-04T14:15:53.090 回答