2013-02-20 3 views
16

Я добавил GroundOverlay, чтобы отобразить и ограничить прокрутку и масштабирование в этой области.предельная прокрутка и масштабирование API Google Maps для Android v2

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

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

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

ответ

2

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

Другой подход должен был бы добавить

GoogleMap.OnCameraChangeListener 

на карту и реализации

public void onCameraChange(CameraPosition cameraPosition); 

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

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

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

+5

проблема заключается в том, что метод onCameraChange вызывает только при остановке движения камеры, так что я могу overscroll области и только тогда метод будет вызывать – user2090636

+0

Может быть с помощью MAP апи ISN Тогда правильный подход. –

13

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

  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 и проверить, находится ли текущее местоположение в пределах разрешенных границ).

+1

Вы заслуживаете больше репутации. Престижность. –

3

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

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

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

5

Слишком плохо, что 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); 
} 

Затем обработчик, который контролирует сверхпрокрутку (и overzoom), ограничивая ее каждые 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); 
} 

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

+1

Да, это «работает», но пользовательский опыт в области терапии, когда он прыгает. – Warpzit

+0

Согласен, было бы интересно иметь лучшее решение, к сожалению, я не мог найти лучшего. –

+0

Думайте, что единственная правильная вещь - полагаться на другую библиотеку или ждать обновления, увы, из других библиотек с открытым исходным кодом просто не так приятно работать;) – Warpzit

10

сдерживающий камеры имеет (! Наконец) был добавлен в качестве функции в рамках выпуска 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); 

Вы можете найти подробное объяснение в документации: Restricting the user's panning to a given area и sample activity in GitHub.

2

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

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

 Смежные вопросы

  • Нет связанных вопросов^_^