[android] clear CameraChangeDispatcher message queue when camera move is restarted
This commit is contained in:
committed by
Łukasz Paczos
parent
66e95ac2ab
commit
095ea17c7d
@@ -1,14 +1,19 @@
|
||||
package com.mapbox.mapboxsdk.maps;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.support.annotation.IntDef;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraIdleListener;
|
||||
import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveCanceledListener;
|
||||
import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveListener;
|
||||
import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener;
|
||||
import static java.lang.annotation.RetentionPolicy.SOURCE;
|
||||
|
||||
/**
|
||||
* Class responsible for dispatching camera change events to registered listeners.
|
||||
@@ -16,7 +21,7 @@ import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener;
|
||||
class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, MapboxMap.OnCameraMoveListener,
|
||||
MapboxMap.OnCameraMoveCanceledListener, OnCameraIdleListener {
|
||||
|
||||
private final Handler handler = new Handler();
|
||||
private final CameraChangeHandler handler = new CameraChangeHandler(this);
|
||||
|
||||
private boolean idle = true;
|
||||
private int moveStartedReason;
|
||||
@@ -31,83 +36,36 @@ class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, M
|
||||
private OnCameraMoveListener onCameraMoveListener;
|
||||
private OnCameraIdleListener onCameraIdleListener;
|
||||
|
||||
private final Runnable onCameraMoveStartedRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!idle) {
|
||||
return;
|
||||
}
|
||||
idle = false;
|
||||
@Retention(SOURCE)
|
||||
@IntDef( {MOVE_STARTED, MOVE, MOVE_CANCELED, IDLE})
|
||||
@interface CameraChange {
|
||||
}
|
||||
|
||||
// deprecated API
|
||||
if (onCameraMoveStartedListener != null) {
|
||||
onCameraMoveStartedListener.onCameraMoveStarted(moveStartedReason);
|
||||
}
|
||||
private static final int MOVE_STARTED = 0;
|
||||
private static final int MOVE = 1;
|
||||
private static final int MOVE_CANCELED = 2;
|
||||
private static final int IDLE = 3;
|
||||
|
||||
// new API
|
||||
if (!onCameraMoveStarted.isEmpty()) {
|
||||
for (OnCameraMoveStartedListener cameraMoveStartedListener : onCameraMoveStarted) {
|
||||
cameraMoveStartedListener.onCameraMoveStarted(moveStartedReason);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public void onCameraMoveStarted(final int reason) {
|
||||
moveStartedReason = reason;
|
||||
handler.scheduleMessage(MOVE_STARTED);
|
||||
}
|
||||
|
||||
private final Runnable onCameraMoveRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// deprecated API
|
||||
if (onCameraMoveListener != null && !idle) {
|
||||
onCameraMoveListener.onCameraMove();
|
||||
}
|
||||
@Override
|
||||
public void onCameraMove() {
|
||||
handler.scheduleMessage(MOVE);
|
||||
}
|
||||
|
||||
// new API
|
||||
if (!onCameraMove.isEmpty() && !idle) {
|
||||
for (OnCameraMoveListener cameraMoveListener : onCameraMove) {
|
||||
cameraMoveListener.onCameraMove();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public void onCameraMoveCanceled() {
|
||||
handler.scheduleMessage(MOVE_CANCELED);
|
||||
}
|
||||
|
||||
private final Runnable onCameraMoveCancelRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// deprecated API
|
||||
if (onCameraMoveCanceledListener != null && !idle) {
|
||||
onCameraMoveCanceledListener.onCameraMoveCanceled();
|
||||
}
|
||||
|
||||
// new API
|
||||
if (!onCameraMoveCanceled.isEmpty() && !idle) {
|
||||
for (OnCameraMoveCanceledListener cameraMoveCanceledListener : onCameraMoveCanceled) {
|
||||
cameraMoveCanceledListener.onCameraMoveCanceled();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable onCameraIdleRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (idle) {
|
||||
return;
|
||||
}
|
||||
idle = true;
|
||||
|
||||
// deprecated API
|
||||
if (onCameraIdleListener != null) {
|
||||
onCameraIdleListener.onCameraIdle();
|
||||
}
|
||||
|
||||
// new API
|
||||
if (!onCameraIdle.isEmpty()) {
|
||||
for (OnCameraIdleListener cameraIdleListener : onCameraIdle) {
|
||||
cameraIdleListener.onCameraIdle();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public void onCameraIdle() {
|
||||
handler.scheduleMessage(IDLE);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
void setOnCameraMoveStartedListener(OnCameraMoveStartedListener onCameraMoveStartedListener) {
|
||||
@@ -129,27 +87,6 @@ class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, M
|
||||
this.onCameraIdleListener = onCameraIdleListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraMoveStarted(final int reason) {
|
||||
moveStartedReason = reason;
|
||||
handler.post(onCameraMoveStartedRunnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraMove() {
|
||||
handler.post(onCameraMoveRunnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraMoveCanceled() {
|
||||
handler.post(onCameraMoveCancelRunnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraIdle() {
|
||||
handler.post(onCameraIdleRunnable);
|
||||
}
|
||||
|
||||
void addOnCameraIdleListener(@NonNull OnCameraIdleListener listener) {
|
||||
onCameraIdle.add(listener);
|
||||
}
|
||||
@@ -189,4 +126,122 @@ class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, M
|
||||
onCameraMove.remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
private void executeOnCameraMoveStarted() {
|
||||
if (!idle) {
|
||||
return;
|
||||
}
|
||||
idle = false;
|
||||
|
||||
// deprecated API
|
||||
if (onCameraMoveStartedListener != null) {
|
||||
onCameraMoveStartedListener.onCameraMoveStarted(moveStartedReason);
|
||||
}
|
||||
|
||||
// new API
|
||||
if (!onCameraMoveStarted.isEmpty()) {
|
||||
for (OnCameraMoveStartedListener cameraMoveStartedListener : onCameraMoveStarted) {
|
||||
cameraMoveStartedListener.onCameraMoveStarted(moveStartedReason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void executeOnCameraMove() {
|
||||
// deprecated API
|
||||
if (onCameraMoveListener != null && !idle) {
|
||||
onCameraMoveListener.onCameraMove();
|
||||
}
|
||||
|
||||
// new API
|
||||
if (!onCameraMove.isEmpty() && !idle) {
|
||||
for (OnCameraMoveListener cameraMoveListener : onCameraMove) {
|
||||
cameraMoveListener.onCameraMove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void executeOnCameraMoveCancelled() {
|
||||
// deprecated API
|
||||
if (onCameraMoveCanceledListener != null && !idle) {
|
||||
onCameraMoveCanceledListener.onCameraMoveCanceled();
|
||||
}
|
||||
|
||||
// new API
|
||||
if (!onCameraMoveCanceled.isEmpty() && !idle) {
|
||||
for (OnCameraMoveCanceledListener cameraMoveCanceledListener : onCameraMoveCanceled) {
|
||||
cameraMoveCanceledListener.onCameraMoveCanceled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void executeOnCameraIdle() {
|
||||
if (idle) {
|
||||
return;
|
||||
}
|
||||
idle = true;
|
||||
|
||||
// deprecated API
|
||||
if (onCameraIdleListener != null) {
|
||||
onCameraIdleListener.onCameraIdle();
|
||||
}
|
||||
|
||||
// new API
|
||||
if (!onCameraIdle.isEmpty()) {
|
||||
for (OnCameraIdleListener cameraIdleListener : onCameraIdle) {
|
||||
cameraIdleListener.onCameraIdle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class CameraChangeHandler extends Handler {
|
||||
|
||||
private WeakReference<CameraChangeDispatcher> dispatcherWeakReference;
|
||||
|
||||
CameraChangeHandler(CameraChangeDispatcher dispatcher) {
|
||||
super();
|
||||
this.dispatcherWeakReference = new WeakReference<>(dispatcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
CameraChangeDispatcher dispatcher = dispatcherWeakReference.get();
|
||||
if (dispatcher != null) {
|
||||
switch (msg.what) {
|
||||
case MOVE_STARTED:
|
||||
dispatcher.executeOnCameraMoveStarted();
|
||||
break;
|
||||
case MOVE:
|
||||
dispatcher.executeOnCameraMove();
|
||||
break;
|
||||
case MOVE_CANCELED:
|
||||
dispatcher.executeOnCameraMoveCancelled();
|
||||
break;
|
||||
case IDLE:
|
||||
dispatcher.executeOnCameraIdle();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void scheduleMessage(@CameraChange int change) {
|
||||
CameraChangeDispatcher dispatcher = dispatcherWeakReference.get();
|
||||
if (dispatcher != null) {
|
||||
// if there is a movement that is cancelled/stopped and restarted in the same code block
|
||||
// we can safely assume that the movement will continue, no need for dispatching unnecessary callbacks sequence
|
||||
if (change == MOVE_STARTED) {
|
||||
boolean shouldReturn = !dispatcher.idle && (hasMessages(IDLE) || hasMessages(MOVE_CANCELED));
|
||||
removeMessages(IDLE);
|
||||
removeMessages(MOVE_CANCELED);
|
||||
|
||||
if (shouldReturn) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Message message = new Message();
|
||||
message.what = change;
|
||||
this.sendMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import android.support.annotation.Nullable;
|
||||
import android.view.InputDevice;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
|
||||
import com.mapbox.android.gestures.AndroidGesturesManager;
|
||||
import com.mapbox.android.gestures.Constants;
|
||||
import com.mapbox.android.gestures.MoveGestureDetector;
|
||||
@@ -222,16 +223,21 @@ final class MapGestureDetector {
|
||||
case MotionEvent.ACTION_UP:
|
||||
transform.setGestureInProgress(false);
|
||||
|
||||
// Start all awaiting velocity animations
|
||||
animationsTimeoutHandler.removeCallbacksAndMessages(null);
|
||||
for (Animator animator : scheduledAnimators) {
|
||||
animator.start();
|
||||
if (scheduledAnimators.isEmpty()) {
|
||||
cameraChangeDispatcher.onCameraIdle();
|
||||
} else {
|
||||
// Start all awaiting velocity animations
|
||||
animationsTimeoutHandler.removeCallbacksAndMessages(null);
|
||||
for (Animator animator : scheduledAnimators) {
|
||||
animator.start();
|
||||
}
|
||||
scheduledAnimators.clear();
|
||||
}
|
||||
scheduledAnimators.clear();
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
scheduledAnimators.clear();
|
||||
transform.cancelTransitions();
|
||||
transform.setGestureInProgress(false);
|
||||
break;
|
||||
}
|
||||
@@ -526,8 +532,6 @@ final class MapGestureDetector {
|
||||
|
||||
@Override
|
||||
public void onScaleEnd(StandardScaleGestureDetector detector, float velocityX, float velocityY) {
|
||||
cameraChangeDispatcher.onCameraIdle();
|
||||
|
||||
if (quickZoom) {
|
||||
//if quickzoom, re-enabling move gesture detector
|
||||
gesturesManager.getMoveGestureDetector().setEnabled(true);
|
||||
@@ -542,18 +546,19 @@ final class MapGestureDetector {
|
||||
|
||||
notifyOnScaleEndListeners(detector);
|
||||
|
||||
if (!uiSettings.isScaleVelocityAnimationEnabled()) {
|
||||
float velocityXY = Math.abs(velocityX) + Math.abs(velocityY);
|
||||
|
||||
if (!uiSettings.isScaleVelocityAnimationEnabled() || velocityXY < minimumVelocity) {
|
||||
// notifying listeners that camera is idle only if there is no follow-up animation
|
||||
cameraChangeDispatcher.onCameraIdle();
|
||||
return;
|
||||
}
|
||||
|
||||
float velocityXY = Math.abs(velocityX) + Math.abs(velocityY);
|
||||
if (velocityXY > minimumVelocity) {
|
||||
double zoomAddition = calculateScale(velocityXY, detector.isScalingOut());
|
||||
double currentZoom = transform.getRawZoom();
|
||||
long animationTime = (long) (Math.abs(zoomAddition) * 1000 / 4);
|
||||
scaleAnimator = createScaleAnimator(currentZoom, zoomAddition, scaleFocalPoint, animationTime);
|
||||
scheduleAnimator(scaleAnimator);
|
||||
}
|
||||
double zoomAddition = calculateScale(velocityXY, detector.isScalingOut());
|
||||
double currentZoom = transform.getRawZoom();
|
||||
long animationTime = (long) (Math.abs(zoomAddition) * 1000 / 4);
|
||||
scaleAnimator = createScaleAnimator(currentZoom, zoomAddition, scaleFocalPoint, animationTime);
|
||||
scheduleAnimator(scaleAnimator);
|
||||
}
|
||||
|
||||
private void setScaleFocalPoint(StandardScaleGestureDetector detector) {
|
||||
@@ -650,8 +655,6 @@ final class MapGestureDetector {
|
||||
|
||||
@Override
|
||||
public void onRotateEnd(RotateGestureDetector detector, float velocityX, float velocityY, float angularVelocity) {
|
||||
cameraChangeDispatcher.onCameraIdle();
|
||||
|
||||
if (uiSettings.isIncreaseScaleThresholdWhenRotating()) {
|
||||
// resetting default scale threshold values
|
||||
gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold(defaultSpanSinceStartThreshold);
|
||||
@@ -659,11 +662,9 @@ final class MapGestureDetector {
|
||||
|
||||
notifyOnRotateEndListeners(detector);
|
||||
|
||||
if (!uiSettings.isRotateVelocityAnimationEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.abs(angularVelocity) < minimumAngularVelocity) {
|
||||
if (!uiSettings.isRotateVelocityAnimationEnabled() || Math.abs(angularVelocity) < minimumAngularVelocity) {
|
||||
// notifying listeners that camera is idle only if there is no follow-up animation
|
||||
cameraChangeDispatcher.onCameraIdle();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user