[android] improve scale + rotation behavior
This commit is contained in:
committed by
Łukasz Paczos
parent
18971395d6
commit
3e73e4d49f
@@ -200,5 +200,6 @@ public class MapboxConstants {
|
||||
public static final String STATE_ROTATE_ANIMATION_ENABLED = "mapbox_rotateAnimationEnabled";
|
||||
public static final String STATE_FLING_ANIMATION_ENABLED = "mapbox_flingAnimationEnabled";
|
||||
public static final String STATE_INCREASE_ROTATE_THRESHOLD = "mapbox_increaseRotateThreshold";
|
||||
public static final String STATE_DISABLE_ROTATE_WHEN_SCALING = "mapbox_disableRotateWhenScaling";
|
||||
public static final String STATE_INCREASE_SCALE_THRESHOLD = "mapbox_increaseScaleThreshold";
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ 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;
|
||||
import com.mapbox.android.gestures.MultiFingerTapGestureDetector;
|
||||
import com.mapbox.android.gestures.RotateGestureDetector;
|
||||
@@ -115,12 +114,12 @@ final class MapGestureDetector {
|
||||
com.mapbox.android.gestures.R.dimen.mapbox_defaultScaleSpanSinceStartThreshold));
|
||||
MoveGestureListener moveGestureListener = new MoveGestureListener();
|
||||
ScaleGestureListener scaleGestureListener = new ScaleGestureListener(
|
||||
context.getResources().getDimension(R.dimen.mapbox_minimum_scale_velocity));
|
||||
context.getResources().getDimension(R.dimen.mapbox_minimum_scale_speed),
|
||||
context.getResources().getDimension(R.dimen.mapbox_minimum_angled_scale_speed),
|
||||
context.getResources().getDimension(R.dimen.mapbox_minimum_scale_velocity)
|
||||
);
|
||||
RotateGestureListener rotateGestureListener = new RotateGestureListener(
|
||||
context.getResources().getDimension(R.dimen.mapbox_minimum_scale_span_when_rotating),
|
||||
context.getResources().getDimension(R.dimen.mapbox_minimum_angular_velocity),
|
||||
context.getResources().getDimension(
|
||||
com.mapbox.android.gestures.R.dimen.mapbox_defaultScaleSpanSinceStartThreshold));
|
||||
context.getResources().getDimension(R.dimen.mapbox_minimum_angular_velocity));
|
||||
ShoveGestureListener shoveGestureListener = new ShoveGestureListener();
|
||||
TapGestureListener tapGestureListener = new TapGestureListener();
|
||||
|
||||
@@ -152,6 +151,7 @@ final class MapGestureDetector {
|
||||
}
|
||||
|
||||
gesturesManager = androidGesturesManager;
|
||||
gesturesManager.getRotateGestureDetector().setAngleThreshold(3f);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -480,10 +480,14 @@ final class MapGestureDetector {
|
||||
|
||||
private final class ScaleGestureListener extends StandardScaleGestureDetector.SimpleStandardOnScaleGestureListener {
|
||||
|
||||
private final float minimumGestureSpeed;
|
||||
private final float minimumAngledGestureSpeed;
|
||||
private final float minimumVelocity;
|
||||
private boolean quickZoom;
|
||||
|
||||
ScaleGestureListener(float minimumVelocity) {
|
||||
ScaleGestureListener(float minimumGestureSpeed, float minimumAngledGestureSpeed, float minimumVelocity) {
|
||||
this.minimumGestureSpeed = minimumGestureSpeed;
|
||||
this.minimumAngledGestureSpeed = minimumAngledGestureSpeed;
|
||||
this.minimumVelocity = minimumVelocity;
|
||||
}
|
||||
|
||||
@@ -501,18 +505,38 @@ final class MapGestureDetector {
|
||||
}
|
||||
// re-try disabling the move detector in case double tap has been interrupted before quickzoom started
|
||||
gesturesManager.getMoveGestureDetector().setEnabled(false);
|
||||
} else {
|
||||
if (detector.getPreviousSpan() > 0) {
|
||||
float currSpan = detector.getCurrentSpan();
|
||||
float prevSpan = detector.getPreviousSpan();
|
||||
double currTime = detector.getCurrentEvent().getEventTime();
|
||||
double prevTime = detector.getPreviousEvent().getEventTime();
|
||||
if (currTime == prevTime) {
|
||||
return false;
|
||||
}
|
||||
double speed = Math.abs(currSpan - prevSpan) / (currTime - prevTime);
|
||||
if (speed < minimumGestureSpeed) {
|
||||
// do not scale if the minimal gesture speed is not met
|
||||
return false;
|
||||
} else if (!gesturesManager.getRotateGestureDetector().isInProgress()) {
|
||||
float rotationDeltaSinceLast = gesturesManager.getRotateGestureDetector().getDeltaSinceLast();
|
||||
if (Math.abs(rotationDeltaSinceLast) > 0.4 && speed < minimumAngledGestureSpeed) {
|
||||
// do not scale in case we're preferring to start rotation
|
||||
return false;
|
||||
}
|
||||
|
||||
if (uiSettings.isDisableRotateWhenScaling()) {
|
||||
// disable rotate gesture when scale is detected first
|
||||
gesturesManager.getRotateGestureDetector().setEnabled(false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
cancelTransitionsIfRequired();
|
||||
|
||||
if (uiSettings.isIncreaseRotateThresholdWhenScaling()) {
|
||||
// increase rotate angle threshold when scale is detected first
|
||||
gesturesManager.getRotateGestureDetector().setAngleThreshold(
|
||||
Constants.DEFAULT_ROTATE_ANGLE_THRESHOLD
|
||||
+ MapboxConstants.ROTATION_THRESHOLD_INCREASE_WHEN_SCALING
|
||||
);
|
||||
}
|
||||
|
||||
notifyOnScaleBeginListeners(detector);
|
||||
|
||||
return true;
|
||||
@@ -538,13 +562,9 @@ final class MapGestureDetector {
|
||||
if (quickZoom) {
|
||||
// re-enabled the move detector if the quickzoom happened
|
||||
gesturesManager.getMoveGestureDetector().setEnabled(true);
|
||||
}
|
||||
|
||||
if (uiSettings.isIncreaseRotateThresholdWhenScaling()) {
|
||||
// resetting default angle threshold
|
||||
gesturesManager.getRotateGestureDetector().setAngleThreshold(
|
||||
Constants.DEFAULT_ROTATE_ANGLE_THRESHOLD
|
||||
);
|
||||
} else {
|
||||
// re-enable rotation in case it's been disabled
|
||||
gesturesManager.getRotateGestureDetector().setEnabled(true);
|
||||
}
|
||||
|
||||
notifyOnScaleEndListeners(detector);
|
||||
@@ -605,15 +625,10 @@ final class MapGestureDetector {
|
||||
}
|
||||
|
||||
private final class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
|
||||
private final float minimumScaleSpanWhenRotating;
|
||||
private final float minimumAngularVelocity;
|
||||
private final float defaultSpanSinceStartThreshold;
|
||||
|
||||
RotateGestureListener(float minimumScaleSpanWhenRotating, float minimumAngularVelocity,
|
||||
float defaultSpanSinceStartThreshold) {
|
||||
this.minimumScaleSpanWhenRotating = minimumScaleSpanWhenRotating;
|
||||
RotateGestureListener(float minimumAngularVelocity) {
|
||||
this.minimumAngularVelocity = minimumAngularVelocity;
|
||||
this.defaultSpanSinceStartThreshold = defaultSpanSinceStartThreshold;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -622,14 +637,25 @@ final class MapGestureDetector {
|
||||
return false;
|
||||
}
|
||||
|
||||
cancelTransitionsIfRequired();
|
||||
|
||||
if (uiSettings.isIncreaseScaleThresholdWhenRotating()) {
|
||||
// when rotation starts, interrupting scale and increasing the threshold
|
||||
// to make rotation without scaling easier
|
||||
gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold(minimumScaleSpanWhenRotating);
|
||||
gesturesManager.getStandardScaleGestureDetector().interrupt();
|
||||
float deltaSinceLast = Math.abs(detector.getDeltaSinceLast());
|
||||
double currTime = detector.getCurrentEvent().getEventTime();
|
||||
double prevTime = detector.getPreviousEvent().getEventTime();
|
||||
if (currTime == prevTime) {
|
||||
return false;
|
||||
}
|
||||
double speed = deltaSinceLast / (currTime - prevTime);
|
||||
float deltaSinceStart = Math.abs(detector.getDeltaSinceStart());
|
||||
|
||||
// adjust the responsiveness of a rotation gesture - the higher the speed, the bigger the threshold
|
||||
if (speed < 0.04
|
||||
|| (speed > 0.07 && deltaSinceStart < 5)
|
||||
|| (speed > 0.15 && deltaSinceStart < 7)
|
||||
|| (speed > 0.5 && deltaSinceStart < 15)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cancelTransitionsIfRequired();
|
||||
|
||||
notifyOnRotateBeginListeners(detector);
|
||||
|
||||
@@ -657,11 +683,6 @@ final class MapGestureDetector {
|
||||
@Override
|
||||
public void onRotateEnd(@NonNull RotateGestureDetector detector, float velocityX,
|
||||
float velocityY, float angularVelocity) {
|
||||
if (uiSettings.isIncreaseScaleThresholdWhenRotating()) {
|
||||
// resetting default scale threshold values
|
||||
gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold(defaultSpanSinceStartThreshold);
|
||||
}
|
||||
|
||||
notifyOnRotateEndListeners(detector);
|
||||
|
||||
if (!uiSettings.isRotateVelocityAnimationEnabled() || Math.abs(angularVelocity) < minimumAngularVelocity) {
|
||||
@@ -681,6 +702,8 @@ final class MapGestureDetector {
|
||||
angularVelocity = -angularVelocity;
|
||||
}
|
||||
|
||||
// todo clear scale velocity if angular velocity exceeds a threshold
|
||||
|
||||
PointF focalPoint = getRotateFocalPoint(detector);
|
||||
rotateAnimator = createRotateAnimator(angularVelocity, animationTime, focalPoint);
|
||||
scheduleAnimator(rotateAnimator);
|
||||
|
||||
@@ -67,6 +67,7 @@ public final class UiSettings {
|
||||
private boolean flingVelocityAnimationEnabled = true;
|
||||
|
||||
private boolean increaseRotateThresholdWhenScaling = true;
|
||||
private boolean disableRotateWhenScaling = true;
|
||||
private boolean increaseScaleThresholdWhenRotating = true;
|
||||
|
||||
private float zoomRate = 1.0f;
|
||||
@@ -132,6 +133,7 @@ public final class UiSettings {
|
||||
outState.putBoolean(MapboxConstants.STATE_ROTATE_ANIMATION_ENABLED, isRotateVelocityAnimationEnabled());
|
||||
outState.putBoolean(MapboxConstants.STATE_FLING_ANIMATION_ENABLED, isFlingVelocityAnimationEnabled());
|
||||
outState.putBoolean(MapboxConstants.STATE_INCREASE_ROTATE_THRESHOLD, isIncreaseRotateThresholdWhenScaling());
|
||||
outState.putBoolean(MapboxConstants.STATE_DISABLE_ROTATE_WHEN_SCALING, isDisableRotateWhenScaling());
|
||||
outState.putBoolean(MapboxConstants.STATE_INCREASE_SCALE_THRESHOLD, isIncreaseScaleThresholdWhenRotating());
|
||||
outState.putBoolean(MapboxConstants.STATE_QUICK_ZOOM_ENABLED, isQuickZoomGesturesEnabled());
|
||||
outState.putFloat(MapboxConstants.STATE_ZOOM_RATE, getZoomRate());
|
||||
@@ -148,6 +150,7 @@ public final class UiSettings {
|
||||
setFlingVelocityAnimationEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_FLING_ANIMATION_ENABLED));
|
||||
setIncreaseRotateThresholdWhenScaling(
|
||||
savedInstanceState.getBoolean(MapboxConstants.STATE_INCREASE_ROTATE_THRESHOLD));
|
||||
setDisableRotateWhenScaling(savedInstanceState.getBoolean(MapboxConstants.STATE_DISABLE_ROTATE_WHEN_SCALING));
|
||||
setIncreaseScaleThresholdWhenRotating(
|
||||
savedInstanceState.getBoolean(MapboxConstants.STATE_INCREASE_SCALE_THRESHOLD));
|
||||
setQuickZoomGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_QUICK_ZOOM_ENABLED));
|
||||
@@ -919,7 +922,9 @@ public final class UiSettings {
|
||||
* Returns whether rotation threshold should be increase whenever scale is detected.
|
||||
*
|
||||
* @return If true, rotation threshold will be increased.
|
||||
* @deprecated unused, see {@link #isDisableRotateWhenScaling()} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean isIncreaseRotateThresholdWhenScaling() {
|
||||
return increaseRotateThresholdWhenScaling;
|
||||
}
|
||||
@@ -928,16 +933,38 @@ public final class UiSettings {
|
||||
* Set whether rotation threshold should be increase whenever scale is detected.
|
||||
*
|
||||
* @param increaseRotateThresholdWhenScaling If true, rotation threshold will be increased.
|
||||
* @deprecated unused, see {@link #setDisableRotateWhenScaling(boolean)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public void setIncreaseRotateThresholdWhenScaling(boolean increaseRotateThresholdWhenScaling) {
|
||||
this.increaseRotateThresholdWhenScaling = increaseRotateThresholdWhenScaling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether rotation gesture detector is disabled when scale is detected first.
|
||||
*
|
||||
* @return If true, rotation gesture detector will be disabled when scale is detected first.
|
||||
*/
|
||||
public boolean isDisableRotateWhenScaling() {
|
||||
return disableRotateWhenScaling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether rotation gesture detector should be disabled when scale is detected first.
|
||||
*
|
||||
* @param disableRotateWhenScaling If true, rotation gesture detector will be disabled when scale is detected first.
|
||||
*/
|
||||
public void setDisableRotateWhenScaling(boolean disableRotateWhenScaling) {
|
||||
this.disableRotateWhenScaling = disableRotateWhenScaling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether scale threshold should be increase whenever rotation is detected.
|
||||
*
|
||||
* @return If true, scale threshold will be increased.
|
||||
* @deprecated unused
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean isIncreaseScaleThresholdWhenRotating() {
|
||||
return increaseScaleThresholdWhenRotating;
|
||||
}
|
||||
@@ -946,7 +973,9 @@ public final class UiSettings {
|
||||
* set whether scale threshold should be increase whenever rotation is detected.
|
||||
*
|
||||
* @param increaseScaleThresholdWhenRotating If true, scale threshold will be increased.
|
||||
* @deprecated unused
|
||||
*/
|
||||
@Deprecated
|
||||
public void setIncreaseScaleThresholdWhenRotating(boolean increaseScaleThresholdWhenRotating) {
|
||||
this.increaseScaleThresholdWhenRotating = increaseScaleThresholdWhenRotating;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
<dimen name="mapbox_infowindow_tipview_width">8dp</dimen>
|
||||
<dimen name="mapbox_infowindow_margin">8dp</dimen>
|
||||
<dimen name="mapbox_four_dp">4dp</dimen>
|
||||
<dimen name="mapbox_minimum_scale_speed">0.6dp</dimen>
|
||||
<dimen name="mapbox_minimum_angled_scale_speed">0.9dp</dimen>
|
||||
<dimen name="mapbox_eight_dp">8dp</dimen>
|
||||
<dimen name="mapbox_ninety_two_dp">92dp</dimen>
|
||||
<dimen name="mapbox_my_locationview_outer_circle">18dp</dimen>
|
||||
@@ -10,9 +12,6 @@
|
||||
<!--Minimum scale velocity required to start animation-->
|
||||
<dimen name="mapbox_minimum_scale_velocity">225dp</dimen>
|
||||
|
||||
<!--Minimum scale span delta required to execute scale gesture when rotating-->
|
||||
<dimen name="mapbox_minimum_scale_span_when_rotating">100dp</dimen>
|
||||
|
||||
<!--Minimum angular velocity required to start rotation animation-->
|
||||
<dimen name="mapbox_minimum_angular_velocity">0.025dp</dimen>
|
||||
|
||||
|
||||
@@ -341,17 +341,17 @@ public class UiSettingsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncreaseRotateThresholdWhenScalingEnabled() {
|
||||
uiSettings.setIncreaseRotateThresholdWhenScaling(true);
|
||||
assertEquals("Rotate threshold increase should be enabled", true,
|
||||
uiSettings.isIncreaseRotateThresholdWhenScaling());
|
||||
public void testDisableRotateWhenScalingEnabled() {
|
||||
uiSettings.setDisableRotateWhenScaling(true);
|
||||
assertEquals("Rotate disabling should be enabled", true,
|
||||
uiSettings.isDisableRotateWhenScaling());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncreaseRotateThresholdWhenScalingDisabled() {
|
||||
uiSettings.setIncreaseRotateThresholdWhenScaling(false);
|
||||
assertEquals("Rotate threshold increase should be disabled", false,
|
||||
uiSettings.isIncreaseRotateThresholdWhenScaling());
|
||||
public void testDisableRotateWhenScalingDisabled() {
|
||||
uiSettings.setDisableRotateWhenScaling(false);
|
||||
assertEquals("Rotate disabling should be disabled", false,
|
||||
uiSettings.isDisableRotateWhenScaling());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user