ограничить прокрутку и масштабирование Google Maps Android API v2

Я добавил GroundOverlay на карту и хотите ограничить прокрутку и масштабирование в этой области.

Как ограничить прокрутку в некоторых пределах на картах Android Google?

Можно ли мгновенно получить точки движения из MapFragment?

Пожалуйста помогите.


person user2090636    schedule 20.02.2013    source источник


Ответы (6)


Ограничение камеры (наконец-то!) было добавлено как функция в рамках выпуска Сервисов 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);

Вы можете найти подробное объяснение в документации: Ограничение панорамирования пользователем до заданная область и пример действия в GitHub.

person matiash    schedule 26.10.2016

Может быть, уже слишком поздно, но вот мое решение:

  1. Отключены встроенные жесты GoogleMap.

  2. Добавлены пользовательские жесты (для прокрутки, перелистывания и масштабирования).

  3. Проверка разрешенной области при обработке событий.

  4. Установите границы/масштаб вручную с помощью стандартных функций карты.

И вот мой пример:

[ОБНОВЛЕНО]

Была проблема - когда сенсорные события были получены до того, как карта была инициализирована.

проверить нулевые значения в 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-макете фрагмента:

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

В файле xml можно определить пользовательские кнопки масштабирования/местоположения и установить прослушиватели кликов для ручного управления камерой (в этом случае вам необходимо проверить MAX_ZOOM и MIN_ZOOM и проверить, находится ли текущее местоположение в допустимых пределах).

person matreshkin    schedule 22.04.2013
comment
Вы заслуживаете большей репутации. Престижность. - person Vaibhav Jani; 03.11.2015
comment
Не удается разрешить метод getMap(); так что проверить пока нет возможности - person Tushar Gogna; 21.06.2019

Жаль, что Google не позволяет нам перехватывать и блокировать пользователя, я обнаружил, что ответ 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);
}

Затем обработчик, который контролирует прокрутку (и масштабирование), ограничивая ее каждые 100 мс.

/**
 * 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);
}

Надеюсь, вы найдете это полезным!

person Luca Vitucci    schedule 20.03.2014
comment
Согласен, было бы интересно иметь лучшее решение, к сожалению, лучшего я не нашел. - person Luca Vitucci; 04.06.2015
comment
Думаю, единственно правильное решение — полагаться на другую библиотеку или ждать обновления, увы, с другими библиотеками с открытым исходным кодом работать не так приятно;) - person Warpzit; 04.06.2015
comment
Я использую ваш код, но упростил OverscrollHandler двумя строками: CameraUpdate update = CameraUpdateFactory.newLatLngBounds(BOUNDS, 20); mMap.animateCamera(update, 2000, null); - person user3105453; 15.04.2016

Вместо того, чтобы использовать новую и блестящую технологию push, которой является onCameraChange, вы можете попробовать использовать старую технологию опроса: map.getCameraPosition() или map.getProjection().getVisibleRegion(). Затем вы можете проверить, соответствует ли возвращаемое значение тому, что вам нравится, а если нет, map.moveCamera(...).

В основном вам нужен Handler, который получит значение положения камеры в handleMessage, и вы отправляете сообщения этому обработчику каждые 10 мс или около того. Внутри handleMessage делаем sendEmptyMessageDelayed.

Вы также можете использовать Runnable вместо Message (но это дело вкуса).

person MaciejGórski    schedule 29.04.2013

ограничить масштаб, вы можете использовать этот код

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

В Maps API v2 есть Min/MaxZoomLevel в классе GoogleMap, но я не знаю, можно ли его каким-либо образом установить.

Другим подходом было бы добавление

GoogleMap.OnCameraChangeListener

на вашу карту и реализуя

public void onCameraChange(CameraPosition cameraPosition);

Чтобы ограничить видимую область, используйте GoogleMap.moveCamera(cameraPosition)

Это если вы хотите, чтобы пользователь мог прокручивать или масштабировать «некоторые».

Вы также можете полностью отключить события прокрутки/масштабирования через GoogleMapOptions< /а>

person Thibault D.    schedule 20.02.2013
comment
проблема в том, что метод onCameraChange вызывается только тогда, когда движение камеры останавливается, поэтому я могу прокрутить область, и только тогда метод вызовет - person user2090636; 20.02.2013