Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>May be it is too late, but here is my solution:</p> <ol> <li><p>Disabled built-in GoogleMap's gestures.</p></li> <li><p>Added custom gestures (for scrolling, fling and scaling).</p></li> <li><p>Checking for allowed area when processing events.</p></li> <li><p>Set bounds/zoom manually with standard Map's functions.</p></li> </ol> <p>And here is my example:</p> <p><strong>[UPDATED]</strong></p> <p>There was a problem - when touch events were recieved before map was initialized.</p> <p>check null values in onInterceptTouchEvent</p> <p>Also I have discovered that my solution is slightly slowly than build-in function.</p> <pre><code>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 &lt; MIN_ZOOM_FOR_FLING) return false; int velocity = (int) Math.sqrt(velocityX * velocityX + velocityY * velocityY); if (velocity &lt; 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 &lt; MIN_ZOOM) if (zoom == MIN_ZOOM) return false; else zoom = MIN_ZOOM; if (zoom &gt; 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 &lt;= 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 &gt; 360d) lonVal -= 360d; while (lonVal &lt; -360d) lonVal += 360d; if (lonVal &lt; 0) lonVal = 360d + lonVal; return lonVal; } private double denorm(double lonVal) { if (lonVal &gt; 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 &lt; MIN_LONGITUDE || right &gt; MAX_LONGITUDE || bottom &lt; MIN_LATITUDE || top &gt; 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); } } </code></pre> <p>Definition within my xml-layout of fragment:</p> <pre><code>&lt;com.package....RestrictedMapView android:id="@+id/mapview" android:layout_width="match_parent" android:layout_height="match_parent" /&gt; </code></pre> <p>In xml file it is possible to define custom zoom/location buttons and set click listeners for manual manipulating camera (in this case you have to check MAX_ZOOM and MIN_ZOOM, and check if the current location is within the allowed bounds).</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload