diff options
author | Łukasz Paczos <lukasz.paczos@mapbox.com> | 2018-02-16 10:41:23 +0100 |
---|---|---|
committer | Łukasz Paczos <lukasz.paczos@mapbox.com> | 2018-02-16 10:41:23 +0100 |
commit | 9222131b2a4a3022c97a3c6220501e645fe6accc (patch) | |
tree | 91106c243378f09511f81fe15b41b1a36241e4d6 | |
parent | b3c30e65e582cc131c9f56f6c0818152e48e5cbd (diff) | |
download | qtlocation-mapboxgl-9222131b2a4a3022c97a3c6220501e645fe6accc.tar.gz |
[android] new gesture library - scale velocity animator
3 files changed, 125 insertions, 20 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java index 5e779438a5..4317b37885 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java @@ -85,7 +85,7 @@ public class MapboxConstants { /** * The currently used maximum scale factor to clamp to when a quick zoom gesture occurs */ - public static final float MAXIMUM_SCALE_FACTOR_CLAMP = 0.11f; + public static final float MAXIMUM_SCALE_FACTOR_CLAMP = 0.15f; /** * Fragment Argument Key for MapboxMapOptions diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java index f2b5864655..cc2a0b0490 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java @@ -1,13 +1,18 @@ package com.mapbox.mapboxsdk.maps; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; import android.content.Context; import android.graphics.PointF; +import android.os.Handler; import android.support.annotation.Nullable; import android.support.v4.view.GestureDetectorCompat; import android.view.InputDevice; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.VelocityTracker; +import android.view.animation.DecelerateInterpolator; import com.mapbox.android.gestures.AndroidGesturesManager; import com.mapbox.android.gestures.MoveGestureDetector; @@ -19,14 +24,20 @@ import com.mapbox.android.gestures.StandardScaleGestureDetector; import com.mapbox.android.telemetry.Event; import com.mapbox.android.telemetry.MapEventFactory; import com.mapbox.android.telemetry.MapState; +import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.utils.MathUtils; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; +import timber.log.Timber; + +import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.REASON_API_ANIMATION; import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE; /** @@ -91,6 +102,10 @@ final class MapGestureDetector { private AndroidGesturesManager gesturesManager; private boolean executeDoubleTap; + private Animator scaleAnimator; + private final List<Animator> animatorsToExecute = new ArrayList<>(); + private Handler mainHandler = new Handler(); + MapGestureDetector(Context context, Transform transform, Projection projection, UiSettings uiSettings, TrackingSettings trackingSettings, AnnotationManager annotationManager, CameraChangeDispatcher cameraChangeDispatcher) { @@ -129,7 +144,9 @@ final class MapGestureDetector { gesturesManager.setStandardGestureListener(new StandardGestureListener()); gesturesManager.setMoveGestureListener(new MoveGestureListener()); - gesturesManager.setStandardScaleGestureListener(new ScaleGestureListener()); + gesturesManager.setStandardScaleGestureListener(new ScaleGestureListener( + context.getResources().getDimension(R.dimen.mapbox_minimum_scale_velocity) + )); gesturesManager.setRotateGestureListener(new RotateGestureListener()); gesturesManager.setShoveGestureListener(new ShoveGestureListener()); gesturesManager.setMultiFingerTapGestureListener(new TapGestureListener()); @@ -312,6 +329,7 @@ final class MapGestureDetector { switch (motionEvent.getActionMasked()) { case MotionEvent.ACTION_DOWN: + cancelAnimators(); transform.setGestureInProgress(true); break; case MotionEvent.ACTION_UP: @@ -320,6 +338,10 @@ final class MapGestureDetector { if (executeDoubleTap(motionEvent)) { return true; } + + for (Animator animator : animatorsToExecute) { + animator.start(); + } break; case MotionEvent.ACTION_CANCEL: @@ -358,6 +380,27 @@ final class MapGestureDetector { return true; } + private void cancelAnimators() { + for (Animator animator : animatorsToExecute) { + animator.cancel(); + } + + animatorsToExecute.clear(); + } + + private Runnable cancelAnimatorsRunnable = new Runnable() { + @Override + public void run() { + cancelAnimators(); + } + }; + + private void scheduleAnimator(Animator animator) { + animatorsToExecute.add(animator); + mainHandler.removeCallbacksAndMessages(null); + mainHandler.postDelayed(cancelAnimatorsRunnable, 250); + } + /** * Called for events that don't fit the other handlers. * <p> @@ -532,6 +575,15 @@ final class MapGestureDetector { } private final class ScaleGestureListener extends StandardScaleGestureDetector.SimpleStandardOnScaleGestureListener { + + private final float minimumVelocity; + + private PointF scaleFocalPoint; + + public ScaleGestureListener(float minimumVelocity) { + this.minimumVelocity = minimumVelocity; + } + @Override public boolean onScaleBegin(StandardScaleGestureDetector detector) { if (!uiSettings.isZoomGesturesEnabled()) { @@ -541,6 +593,8 @@ final class MapGestureDetector { executeDoubleTap = false; transform.cancelTransitions(); + + // notify camera change listener cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); quickZoom = detector.getPointersCount() == 1; @@ -548,6 +602,19 @@ final class MapGestureDetector { gesturesManager.getMoveGestureDetector().setEnabled(false); } + trackingSettings.resetTrackingModesIfRequired(!quickZoom, false, false); + + if (focalPoint != null) { + // around user provided focal point + scaleFocalPoint = focalPoint; + } else if (quickZoom) { + // around center + scaleFocalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); + } else { + // around gesture + scaleFocalPoint = detector.getFocalPoint(); + } + if (isZoomValid(transform)) { MapEventFactory mapEventFactory = new MapEventFactory(); LatLng latLng = projection.fromScreenLocation(detector.getFocalPoint()); @@ -562,25 +629,8 @@ final class MapGestureDetector { @Override public boolean onScale(StandardScaleGestureDetector detector) { float scaleFactor = detector.getScaleFactor(); - - // notify camera change listener - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - - trackingSettings.resetTrackingModesIfRequired(!quickZoom, false, false); - double zoomBy = getNewZoom(scaleFactor, quickZoom); - if (focalPoint != null) { - // around user provided focal point - transform.zoomBy(zoomBy, focalPoint.x, focalPoint.y); - } else if (quickZoom) { - cameraChangeDispatcher.onCameraMove(); - // around center - transform.zoomBy(zoomBy, uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - } else { - // around gesture - transform.zoomBy(zoomBy, detector.getFocalPoint().x, detector.getFocalPoint().y); - } - + transform.zoomBy(zoomBy, scaleFocalPoint.x, scaleFocalPoint.y); return true; } @@ -589,6 +639,58 @@ final class MapGestureDetector { if (quickZoom) { gesturesManager.getMoveGestureDetector().setEnabled(true); } + + float velocityXY = Math.abs(velocityX) + Math.abs(velocityY); + if (velocityXY > minimumVelocity) { + double zoomAddition = calculateScale(velocityXY, detector.isScalingOut()); + Timber.d("velocity: " + velocityXY + ", zoom: " + zoomAddition); + double currentZoom = transform.getRawZoom(); + //long animationTime = TimeUnit.SECONDS.toMillis((long) Math.abs(zoomAddition)) / 4; //todo make divider public + long animationTime = (long) (Math.abs(zoomAddition) * 1000 / 4); + scaleAnimator = createScaleAnimator(currentZoom, zoomAddition, animationTime, detector.getFocalPoint()); + scheduleAnimator(scaleAnimator); + } + } + + private double calculateScale(double velocityXY, boolean isScalingOut) { + double zoomAddition = (float) Math.log(velocityXY / 1000 + 1); + if (isScalingOut) { + zoomAddition = -zoomAddition; + } + return zoomAddition; + } + + private Animator createScaleAnimator(double currentZoom, double zoomAddition, long animationTime, PointF focal) { + ValueAnimator animator = ValueAnimator.ofFloat((float) currentZoom, (float) (currentZoom + zoomAddition)); + animator.setDuration(animationTime); + animator.setInterpolator(new DecelerateInterpolator()); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + transform.setZoom((Float) animation.getAnimatedValue(), focal, 0, true); + } + }); + + animator.addListener(new AnimatorListenerAdapter() { + + @Override + public void onAnimationStart(Animator animation) { + transform.cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_ANIMATION); + } + + @Override + public void onAnimationCancel(Animator animation) { + } + + @Override + public void onAnimationEnd(Animator animation) { + animatorsToExecute.remove(scaleAnimator); + cameraChangeDispatcher.onCameraIdle(); + } + }); + return animator; } private double getNewZoom(float scaleFactor, boolean quickZoom) { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml index 1c6a265587..d170720bc9 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml +++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml @@ -6,4 +6,7 @@ <dimen name="mapbox_eight_dp">8dp</dimen> <dimen name="mapbox_ninety_two_dp">92dp</dimen> <dimen name="mapbox_my_locationview_outer_circle">18dp</dimen> + + <!--Minimum scale velocity required to start animation--> + <dimen name="mapbox_minimum_scale_velocity">150dp</dimen> </resources> |