2013-02-20 11 views

Antwort

2

In Karten API v2 gibt es eine Min/MaxZoomLevel auf der GoogleMap-Klasse, aber ich weiß nicht, ob Sie es in irgendeiner Weise einstellen können.

würde Ein weiterer Ansatz, eine

GoogleMap.OnCameraChangeListener 

zu Ihrer Karte hinzuzufügen und von

public void onCameraChange(CameraPosition cameraPosition); 

Durchführung des sichtbaren Bereichs, mit GoogleMap.moveCamera (camera)

Das bedeutet zu beschränken wenn Sie möchten, dass der Benutzer in der Lage ist, "etwas" zu scrollen oder zu zoomen.

Sie können auch vollständig deaktivieren Sie die Scroll/Zoom-Ereignisse über GoogleMapOptions

+5

das Problem ist, dass onCameraChange Methode nur aufgerufen, wenn die Kamera keine Bewegung mehr, also kann ich den Bereich Overscroll und nur dann wäre die Methode aufrufen – user2090636

+0

Vielleicht das MAP-api isn mit Dann ist der richtige Ansatz. –

13

sein kann es zu spät ist, aber hier ist meine Lösung:

  1. Behinderte Einbau in GoogleMap Gesten.

  2. Benutzerdefinierte Gesten hinzugefügt (zum Scrollen, Verschieben und Skalieren).

  3. Überprüfung auf erlaubte Fläche bei der Verarbeitung von Ereignissen.

  4. Grenzen setzen/zoomen manuell mit Standard Map-Funktionen.

Und hier ist mein Beispiel:

[UPDATED]

Es gab ein Problem - wenn Touch-Ereignisse empfangen wurden, bevor Karte initialisiert wurde.

Prüfung Nullwert in onInterceptTouchEvent

Auch habe ich entdeckt, dass meine Lösung etwas langsamer als build-in Funktion ist.

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

Definition in meinem XML-Layout-Fragment:

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

In xml Datei es ist möglich, benutzerdefinierte Zoom/Standort Tasten zu definieren und für die manuelle Manipulieren Kamera klicken Hörer einstellen (in diesem Fall, dass Sie Überprüfen Sie MAX_ZOOM und MIN_ZOOM, und überprüfen Sie, ob der aktuelle Standort innerhalb der zulässigen Grenzen liegt).

+1

Sie verdienen mehr Ansehen. Kudos. –

3

Anstelle der neuen und glänzenden Push-Technologie, die onCameraChange ist, können Sie versuchen, alte Poll-Technologie zu verwenden: map.getCameraPosition() oder map.getProjection().getVisibleRegion(). Sie können dann überprüfen, ob der zurückgegebene Wert dem entspricht, was Sie möchten, und wenn nicht, map.moveCamera(...).

Grundsätzlich benötigen Sie eine Handler, die den Wert der Kameraposition in handleMessage erhalten wird und Sie alle 10ms Nachrichten an diesen Handler senden. Inside handleMessage do sendEmptyMessageDelayed.

Sie können auch Runnable statt Message verwenden (aber das ist Geschmackssache).

5

Schade, dass Google uns den Benutzer nicht abfangen und blockieren lässt, ich fand, dass die Antwort MaciejGórski diejenige ist, die am besten zu meinen Bedürfnissen passt.

Ich wollte meine Lösung (basierend auf seiner Antwort) mit Ihnen teilen.

Zuerst definiert i die Grenzen und Max/Min-Zoom:

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; 

Dann habe ich diese kleine Funktion zu testen, ob aktuelle Kamera Grenzen außerhalb von max Grenzen sind und die Differenz in der Breite und Länge zurück.

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

Dann der Handler, der den Overscroll (und Overzoom) steuert, der es alle 100ms begrenzt.

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

Dieser Handler muss in aktueller Klasse als Feld definiert werden (Ich tat dies in einer Klasse, die SupportMapFragment erstreckt), zum ersten Mal

private OverscrollHandler mOverscrollHandler = new OverscrollHandler(); 

Und schließlich aufgerufen werden muß ich es tat am Ende von onActivityCreated, um sicher zu sein, dass die Karte existiert.

@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); 
} 

Hoffe, dass Sie es nützlich finden werden!

+1

Ja, es funktioniert, aber crapy Benutzererfahrung, wie es herumspringt. – Warpzit

+0

Einverstanden, es wäre interessant, eine bessere Lösung zu haben, leider konnte ich keine bessere finden. –

+0

Denken Sie, das einzige Richtige ist, sich auf eine andere Bibliothek zu verlassen oder auf ein Update zu warten, leider sind die anderen Open-Source-Bibliotheken nicht so gut mit;) – Warpzit

10

die Kamera Einschränkende hat (endlich!) Als ein Merkmal im Rahmen der Veröffentlichung von hinzugefügt Google Play-Diensten 9.4 - Sie setLatLngBoundsForCameraTarget(LatLngBounds bounds) nennen können den zulässigen Schwenkbereich einzustellen.

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

Sie können eine ausführliche Erklärung in der Dokumentation: Restricting the user's panning to a given area und sample activity in GitHub.

2

Grenze Zoom Sie Benutzer kann diesen Code

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