限制滚动和缩放Google Maps Android API v2

时间:2013-02-20 10:05:47

标签: android google-maps-android-api-2

我已将GroundOverlay添加到地图中,并希望限制此区域内的滚动和缩放。

如何在Android谷歌地图的某些范围内限制滚动?

是否可以立即从MapFragment获取运动点?

请帮助我。

6 个答案:

答案 0 :(得分:14)

约束相机(最终!)作为 Google Play服务9.4 发布的一部分添加为功能 - 您可以调用setLatLngBoundsForCameraTarget(LatLngBounds bounds)来设置允许的平移区域。

// Create a LatLngBounds that includes the city of Adelaide in Australia.
final LatLngBounds ADELAIDE = new LatLngBounds(
    new LatLng(-35.0, 138.58), new LatLng(-34.9, 138.61));

// Constrain the camera target to the Adelaide bounds.
mMap.setLatLngBoundsForCameraTarget(ADELAIDE);

您可以在文档中找到详尽的说明:Restricting the user's panning to a given areasample activity in GitHub

答案 1 :(得分:13)

可能为时已晚,但这是我的解决方案:

  1. 禁用内置GoogleMap的手势。

  2. 添加了自定义手势(用于滚动,投掷和缩放)。

  3. 处理事件时检查允许的区域。

  4. 使用标准Map的功能手动设置边界/缩放。

  5. 以下是我的例子:

    <强> [增订]

    出现问题 - 在初始化地图之前收到触摸事件。

    检查onInterceptTouchEvent

    中的空值

    此外,我发现我的解决方案比内置函数稍慢。

    import android.content.Context;
    import android.graphics.Point;
    import android.os.Handler;
    import android.util.AttributeSet;
    import android.view.GestureDetector;
    import android.view.MotionEvent;
    import android.view.ScaleGestureDetector;
    import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
    import com.google.android.gms.maps.CameraUpdate;
    import com.google.android.gms.maps.CameraUpdateFactory;
    import com.google.android.gms.maps.GoogleMap;
    import com.google.android.gms.maps.GoogleMapOptions;
    import com.google.android.gms.maps.MapView;
    import com.google.android.gms.maps.MapsInitializer;
    import com.google.android.gms.maps.model.LatLng;
    import com.google.android.gms.maps.model.VisibleRegion;
    
    public class RestrictedMapView extends MapView {
    
        public static float MAX_ZOOM = 20;
        public static float MIN_ZOOM = 5;
        public static float MIN_ZOOM_FOR_FLING = 7;
    
        public static double MAX_LONGITUDE = 183.61;
        public static double MIN_LONGITUDE = 159.31;
        public static double MAX_LATITUDE = -32.98;
        public static double MIN_LATITUDE = -53.82;
    
        public static double DEF_LATITUDE = -41.78;
        public static double DEF_LONGITUDE = 173.02;
        public static float DEF_ZOOM = 7;
    
        private Handler mHandler = new Handler();
        private Context mContext;
        private VisibleRegion mLastCorrectRegion = null;
        private boolean mIsInAnimation = false;
    
        public RestrictedMapView(Context c, AttributeSet a, int o) {
            super(c, a, o);
            init(c);
        }
        public RestrictedMapView(Context c, AttributeSet a) {
            super(c, a);
            init(c);
        }
        public RestrictedMapView(Context c) {
            super(c);
            init(c);
        }
    
        public RestrictedMapView(Context c, GoogleMapOptions o) {
            super(c, o);
            init(c);
        }
    
        private GestureDetector mGestureDetector = null;
        private GestureDetector.SimpleOnGestureListener mGestudeListener =
                new GestureDetector.SimpleOnGestureListener() {
    
            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                if (mIsInAnimation) return false;
                GoogleMap map = getMap();
                LatLng target = map.getCameraPosition().target;
                Point screenPoint = map.getProjection().toScreenLocation(target);
                Point newPoint = new Point(screenPoint.x + (int)distanceX, screenPoint.y + (int)distanceY);
                LatLng mapNewTarget = map.getProjection().fromScreenLocation(newPoint);
                CameraUpdate update = CameraUpdateFactory.newLatLngZoom(
                        mapNewTarget,map.getCameraPosition().zoom);         
                tryUpdateCamera(update, 0); 
                return true;
            }
    
            @Override
            public boolean onFling (MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                if (mIsInAnimation) return false;
                GoogleMap map = getMap();
                double zoom = map.getCameraPosition().zoom;
                if (zoom < MIN_ZOOM_FOR_FLING) 
                    return false;
                int velocity = (int) Math.sqrt(velocityX * velocityX + velocityY * velocityY);
                if (velocity < 500) return false;
                double k1 = 0.002d; /*exipemental*/
                double k2 = 0.002d;/*exipemental*/
    
                LatLng target = map.getCameraPosition().target;
                Point screenPoint = map.getProjection().toScreenLocation(target);
                Point newPoint = new Point(screenPoint.x - (int)(velocityX * k1 * zoom * zoom/*exipemental*/),
                        screenPoint.y - (int)(velocityY * k1 * zoom * zoom/*exipemental*/));
                LatLng mapNewTarget = map.getProjection().fromScreenLocation(newPoint);
                CameraUpdate update = CameraUpdateFactory.newLatLngZoom(
                        mapNewTarget,map.getCameraPosition().zoom); 
                tryUpdateCamera(update, (int)(velocity * k2 * zoom * zoom) /*exipemental*/);    
                return true;
            }
        };  
        private ScaleGestureDetector mScaleGestureDetector = null;
        private ScaleGestureDetector.SimpleOnScaleGestureListener mScaleGestudeListener =
                new ScaleGestureDetector.SimpleOnScaleGestureListener() {
    
            @Override
            public boolean onScale (ScaleGestureDetector detector) {
                if (mIsInAnimation) return false;
    
                GoogleMap map = getMap();
                double zoom = map.getCameraPosition().zoom;
    
                double k = 1d / detector.getScaleFactor();
                int x = (int) detector.getFocusX();
                int y = (int) detector.getFocusY();
                LatLng mapFocus = map.getProjection().
                        fromScreenLocation(new Point(x, y));
                LatLng target = map.getCameraPosition().target;
    
                zoom = zoom + Math.log(detector.getScaleFactor()) / Math.log(2d);
                if (zoom < MIN_ZOOM)  
                    if (zoom == MIN_ZOOM) return false;
                    else zoom = MIN_ZOOM;
                if (zoom > MAX_ZOOM) 
                    if (zoom == MAX_ZOOM) return false;
                    else zoom = MAX_ZOOM;
    
                double dx = norm(mapFocus.longitude) - norm(target.longitude);
                double dy = mapFocus.latitude - target.latitude;
                double dk = 1d - 1d / k;
                LatLng newTarget = new LatLng(target.latitude - dy * dk, 
                        norm(target.longitude) - dx * dk);
    
                CameraUpdate update = CameraUpdateFactory.newLatLngZoom(newTarget, (float) zoom);           
                tryUpdateCamera(update, 0);         
                return true;
            }
        };
    
    
        private void tryUpdateCamera(CameraUpdate update, int animateTime) {
            GoogleMap map = getMap();
            final VisibleRegion reg = map.getProjection().getVisibleRegion();
            if (animateTime <= 0) {
                map.moveCamera(update);
                checkCurrentRegion(reg);
            } else {
                mIsInAnimation = true;
                map.animateCamera(update, animateTime, new GoogleMap.CancelableCallback() {
                    @Override
                    public void onFinish() {
                        mIsInAnimation = false;
                        checkCurrentRegion(reg);
                    }
                    @Override
                    public void onCancel() {
                        mIsInAnimation = false;
                        checkCurrentRegion(reg);
                    }
                });
            }
        }
    
        private void checkCurrentRegion(VisibleRegion oldReg) {
            GoogleMap map = getMap();
            VisibleRegion regNew = map.getProjection().getVisibleRegion();
            if (checkBounds(regNew)) {
                mLastCorrectRegion = regNew;
            } else {
                if (mLastCorrectRegion != null)
                    oldReg = mLastCorrectRegion;
                mIsInAnimation = true;
                map.animateCamera(CameraUpdateFactory.newLatLngBounds(
                        oldReg.latLngBounds, 0),
                        200, new GoogleMap.CancelableCallback() {
                            @Override
                            public void onFinish() {
                                mIsInAnimation = false;
                            }                       
                            @Override
                            public void onCancel() {
                                mIsInAnimation = false;
                            }
                        });
    
            }
        }
    
        /**
         * 
         * 
         * @param lonVal
         * @return
         */
        private double norm(double lonVal) {
            while (lonVal > 360d) lonVal -= 360d;
            while (lonVal < -360d) lonVal += 360d;
            if (lonVal < 0) lonVal = 360d + lonVal;
            return lonVal;
        }
    
        private double denorm(double lonVal) {
            if (lonVal > 180d) lonVal = -360d + lonVal; 
            return lonVal;
        }
    
        private boolean checkBounds(VisibleRegion reg) {
            double left = Math.min(
                    Math.min(norm(reg.farLeft.longitude), norm(reg.nearLeft.longitude)),
                    Math.min(norm(reg.farRight.longitude), norm(reg.nearRight.longitude)));
            double right = Math.max(
                    Math.max(norm(reg.farLeft.longitude), norm(reg.nearLeft.longitude)),
                    Math.max(norm(reg.farRight.longitude), norm(reg.nearRight.longitude)));
            double top = Math.max( 
                    Math.max(reg.farLeft.latitude, reg.nearLeft.latitude),
                    Math.max(reg.farRight.latitude, reg.nearRight.latitude));
            double bottom = Math.min( 
                    Math.min(reg.farLeft.latitude, reg.nearLeft.latitude),
                    Math.min(reg.farRight.latitude, reg.nearRight.latitude));
    
            boolean limitBounds = left < MIN_LONGITUDE || right > MAX_LONGITUDE ||
                    bottom < MIN_LATITUDE || top > MAX_LATITUDE;        
            return !limitBounds;
        }
    
        private void init(Context c) {
            try {
                 MapsInitializer.initialize(c);
             } catch (GooglePlayServicesNotAvailableException e) {
                 e.printStackTrace();
             }
            mContext = c;
            mHandler.post(new Runnable() {          
                @Override
                public void run() {
                    GoogleMap map = getMap();
                    if (map != null) {
                        getMap().getUiSettings().setZoomControlsEnabled(false);
                        map.getUiSettings().setAllGesturesEnabled(false);
                        map.moveCamera(CameraUpdateFactory.newLatLngZoom(
                                new LatLng(DEF_LATITUDE, DEF_LONGITUDE), DEF_ZOOM));
                        mLastCorrectRegion = map.getProjection().getVisibleRegion();
                        mGestureDetector = new GestureDetector(mContext, mGestudeListener);
                        mScaleGestureDetector = new ScaleGestureDetector(mContext, mScaleGestudeListener);
                    } else mHandler.post(this);
                }
            });
        }
    
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent event) {
            if (mGestureDetector != null) mGestureDetector.onTouchEvent(event);
            if (mScaleGestureDetector != null) mScaleGestureDetector.onTouchEvent(event);
            return super.onInterceptTouchEvent(event);
        }
    }
    

    我的片段xml-layout中的定义:

    <com.package....RestrictedMapView
        android:id="@+id/mapview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" /> 
    

    在xml文件中,可以定义自定义缩放/位置按钮并设置单击侦听器以手动操作摄像机(在这种情况下,您必须检查MAX_ZOOM和MIN_ZOOM,并检查当前位置是否在允许的范围内)。 / p>

答案 2 :(得分:5)

太糟糕了,谷歌没有让我们拦截并阻止用户,我发现MaciejGórski的答案是最适合我需要的答案。

我想与你分享我的解决方案(根据他的回答)。

首先我定义了界限和最大/最小缩放:

private final LatLngBounds BOUNDS = new LatLngBounds(new LatLng(41.8138,12.3891), new LatLng(41.9667, 12.5938));
private final int MAX_ZOOM = 18;
private final int MIN_ZOOM = 14;

然后我创建了这个小函数来测试当前摄像机边界是否超出最大边界并返回纬度和经度的差异。

/**
 * Returns the correction for Lat and Lng if camera is trying to get outside of visible map
 * @param cameraBounds Current camera bounds
 * @return Latitude and Longitude corrections to get back into bounds.
 */
private LatLng getLatLngCorrection(LatLngBounds cameraBounds) {
    double latitude=0, longitude=0;
    if(cameraBounds.southwest.latitude < BOUNDS.southwest.latitude) {
        latitude = BOUNDS.southwest.latitude - cameraBounds.southwest.latitude;
    }
    if(cameraBounds.southwest.longitude < BOUNDS.southwest.longitude) {
        longitude = BOUNDS.southwest.longitude - cameraBounds.southwest.longitude;
    }
    if(cameraBounds.northeast.latitude > BOUNDS.northeast.latitude) {
        latitude = BOUNDS.northeast.latitude - cameraBounds.northeast.latitude;
    }
    if(cameraBounds.northeast.longitude > BOUNDS.northeast.longitude) {
        longitude = BOUNDS.northeast.longitude - cameraBounds.northeast.longitude;
    }
    return new LatLng(latitude, longitude);
}

然后控制过度滚动(和overzoom)的处理程序每​​隔100ms限制它。

/**
 * Bounds the user to the overlay.
 */
private class OverscrollHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        CameraPosition position = mMap.getCameraPosition();
        VisibleRegion region = mMap.getProjection().getVisibleRegion();
        float zoom = 0;
        if(position.zoom < MIN_ZOOM) zoom = MIN_ZOOM;
        if(position.zoom > MAX_ZOOM) zoom = MAX_ZOOM;
        LatLng correction = getLatLngCorrection(region.latLngBounds);
        if(zoom != 0 || correction.latitude != 0 || correction.longitude != 0) {
            zoom = (zoom==0)?position.zoom:zoom;
            double lat = position.target.latitude + correction.latitude;
            double lon = position.target.longitude + correction.longitude;
            CameraPosition newPosition = new CameraPosition(new LatLng(lat,lon), zoom, position.tilt, position.bearing);
            CameraUpdate update = CameraUpdateFactory.newCameraPosition(newPosition);
            mMap.moveCamera(update);
        }
        /* Recursively call handler every 100ms */
        sendEmptyMessageDelayed(0,100);
    }
}

此处理程序必须定义为当前类中的字段(我在扩展SupportMapFragment的类中执行此操作)

private OverscrollHandler mOverscrollHandler = new OverscrollHandler();

最后必须第一次调用它,我在onActivityCreated结束时调用它以确保地图存在。

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    mContext = getActivity();
    mMap = getMap();
    mMap.setMapType(GoogleMap.MAP_TYPE_NONE);
    mMap.addTileOverlay(new TileOverlayOptions().tileProvider(new VexLocalTileProvider(getResources().getAssets())));
    CameraUpdate upd = CameraUpdateFactory.newLatLngZoom(new LatLng(41.87145, 12.52849), 14);
    mMap.moveCamera(upd);
    mOverscrollHandler.sendEmptyMessageDelayed(0,100);
}

希望你会发现它很有用!

答案 3 :(得分:3)

您可以尝试使用旧的民意调查技术:onCameraChangemap.getCameraPosition(),而不是使用map.getProjection().getVisibleRegion()新的闪亮推送技术。然后,您可以检查返回的值是否是您喜欢的,如果不是,map.moveCamera(...)

基本上你需要一个Handler,它将获得handleMessage中摄像机位置的值,并且每隔10ms左右就会向此处理程序发送一条消息。在handleMessagesendEmptyMessageDelayed

您也可以使用Runnable代替Message(但这只是品味问题)。

答案 4 :(得分:2)

在Maps API v2中,GoogleMap类上有Min / MaxZoomLevel,但我不知道你是否可以以任何方式设置它。

另一种方法是添加

GoogleMap.OnCameraChangeListener

到您的地图并实施

public void onCameraChange(CameraPosition cameraPosition);

使用GoogleMap.moveCamera(cameraPosition)

限制可见区域

即使您希望用户能够滚动或缩放“某些”。

您还可以通过GoogleMapOptions

完全停用滚动/缩放事件

答案 5 :(得分:2)

限制缩放您可以使用此代码

private GoogleMap mMap;
// Set a preference for minimum and maximum zoom.
mMap.setMinZoomPreference(6.0f);
mMap.setMaxZoomPreference(14.0f);