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

use*_*636 16 android google-maps-android-api-2

我添加了GroundOverlay来映射,并希望限制此区域内的滚动和缩放.

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

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

请帮我.

mat*_*ash 14

作为Google Play Services 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);
Run Code Online (Sandbox Code Playgroud)

您可以在文档中找到详细说明:限制用户对指定区域的平移GitHub中示例活动.


mat*_*kin 13

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

  1. 禁用内置GoogleMap的手势.

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

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

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

这是我的例子:

[更新]

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

检查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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

<com.package....RestrictedMapView
    android:id="@+id/mapview"
    android:layout_width="match_parent"
    android:layout_height="match_parent" /> 
Run Code Online (Sandbox Code Playgroud)

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


Luc*_*cci 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;
Run Code Online (Sandbox Code Playgroud)

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

/**
 * 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);
}
Run Code Online (Sandbox Code Playgroud)

然后控制过度滚动(和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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

private OverscrollHandler mOverscrollHandler = new OverscrollHandler();
Run Code Online (Sandbox Code Playgroud)

最后它必须第一次被调用,我在结束时做了它以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);
}
Run Code Online (Sandbox Code Playgroud)

希望你会发现它很有用!


Thi*_* D. 2

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

另一种方法是添加一个

GoogleMap.OnCameraChangeListener
Run Code Online (Sandbox Code Playgroud)

到您的地图并通过实施

public void onCameraChange(CameraPosition cameraPosition);
Run Code Online (Sandbox Code Playgroud)

要限制可见区域,请使用 GoogleMap.moveCamera(cameraPosition)

也就是说,如果您希望用户能够滚动或缩放“某些”。

您还可以通过GoogleMapOptions完全停用滚动/缩放事件

  • 问题是 onCameraChange 方法仅在相机运动停止时调用,因此我可以过度滚动该区域,然后该方法才会调用 (5认同)