aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTobrun <tobrun.van.nuland@gmail.com>2016-10-10 23:51:30 +0200
committerGitHub <noreply@github.com>2016-10-10 23:51:30 +0200
commite598bbfa7c29ad3a384e157b504c4509f1fcdbb5 (patch)
tree6dcdb0f9a358c0168b83097a97abb85b5f6aa729
parent5891c7028dbc6ae6decb5a3b059fd48f4e700d44 (diff)
Rotate markerview (#6640)
* [android] - MarkerView rotation bug, limit degrees from 0 to 360 while creating a MarkerView * update MarkerView#setRotation to limit input from 0 - 360 degrees, calculates difference in angle and animates rotation by a certain amount instead of animating to a value. Exposed new AnimatorUtils#animateRotationTo. Integrated into MarkerViewManager. Cleanup some unused imports. Updated javadoc. * updated MarkerViewActivity in test application to test for regressions. * added tests to validate changes to MarkerView#setRotate
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java34
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java14
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/AnimatorUtils.java12
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java76
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java102
6 files changed, 212 insertions, 32 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java
index a5c6397b6..d4eb390ab 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerViewOptions.java
@@ -126,6 +126,12 @@ public abstract class BaseMarkerViewOptions<U extends MarkerView, T extends Base
*/
public T rotation(float rotation) {
this.rotation = rotation;
+ while (this.rotation > 360) {
+ this.rotation -= 360;
+ }
+ while (this.rotation < 0) {
+ this.rotation += 360;
+ }
return getThis();
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java
index b94b24025..b1008e290 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerView.java
@@ -1,18 +1,12 @@
package com.mapbox.mapboxsdk.annotations;
-import android.animation.AnimatorSet;
import android.graphics.Bitmap;
-import android.graphics.PointF;
import android.support.annotation.FloatRange;
-import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import android.view.View;
-import android.view.animation.AnimationUtils;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.utils.AnimatorUtils;
/**
* MarkerView is an annotation that shows an View at a geographical location.
@@ -231,7 +225,10 @@ public class MarkerView extends Marker {
}
/**
- * Set the rotation value of the MarkerView.
+ * Set the rotation value of the MarkerView in degrees.
+ * <p>
+ * Input will be limited to 0 - 360 degrees
+ * </p>
* <p>
* This will result in animating the rotation of the MarkerView using an rotation animator
* from current value to the provided parameter value.
@@ -240,9 +237,26 @@ public class MarkerView extends Marker {
* @param rotation the rotation value to animate to
*/
public void setRotation(float rotation) {
- this.rotation = rotation;
+ // limit to 0 - 360 degrees
+ float newRotation = rotation;
+ while (newRotation > 360) {
+ newRotation -= 360;
+ }
+ while (newRotation < 0) {
+ newRotation += 360;
+ }
+
+ // calculate new direction
+ float diff = newRotation - this.rotation;
+ if (diff > 180.0f) {
+ diff -= 360.0f;
+ } else if (diff < -180.0f) {
+ diff += 360.f;
+ }
+
+ this.rotation = newRotation;
if (markerViewManager != null) {
- markerViewManager.animateRotation(this, rotation);
+ markerViewManager.animateRotationBy(this, diff);
}
}
@@ -339,7 +353,7 @@ public class MarkerView extends Marker {
public void setMapboxMap(MapboxMap mapboxMap) {
super.setMapboxMap(mapboxMap);
- if(isFlat()) {
+ if (isFlat()) {
// initial tilt value if MapboxMap is started with a tilt attribute
tiltValue = (float) mapboxMap.getCameraPosition().tilt;
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java
index 4742c9b66..fe1a48993 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java
@@ -15,7 +15,6 @@ import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.Projection;
import com.mapbox.mapboxsdk.utils.AnimatorUtils;
import java.util.ArrayList;
@@ -72,6 +71,19 @@ public class MarkerViewManager {
}
/**
+ * Animate a MarkerView with a given rotation.
+ *
+ * @param marker the MarkerView to rotate by
+ * @param rotation the rotation by value
+ */
+ public void animateRotationBy(@NonNull MarkerView marker, float rotation) {
+ View convertView = markerViewMap.get(marker);
+ if (convertView != null) {
+ AnimatorUtils.rotateBy(convertView, rotation);
+ }
+ }
+
+ /**
* Animate a MarkerView to a given alpha value.
* <p>
* The {@link MarkerView} will be transformed from its current alpha value to the given value.
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/AnimatorUtils.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/AnimatorUtils.java
index 495393c25..ab3b84104 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/AnimatorUtils.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/AnimatorUtils.java
@@ -7,6 +7,7 @@ import android.animation.ObjectAnimator;
import android.support.annotation.AnimatorRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.view.View;
public class AnimatorUtils {
@@ -61,6 +62,17 @@ public class AnimatorUtils {
rotateAnimator.start();
}
+ public static void rotateBy(@NonNull final View view, float rotationBy) {
+ view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ view.animate().rotationBy(rotationBy).setInterpolator(new FastOutSlowInInterpolator()).setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ view.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+ });
+ }
+
public static void alpha(@NonNull final View convertView, float alpha, @Nullable final OnAnimationEndListener listener) {
convertView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ObjectAnimator rotateAnimator = ObjectAnimator.ofFloat(convertView, View.ALPHA, convertView.getAlpha(), alpha);
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java
index ccce72713..8d678794c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MarkerViewActivity.java
@@ -41,24 +41,30 @@ import java.util.Random;
public class MarkerViewActivity extends AppCompatActivity {
+ private static final LatLng[] LAT_LNGS = new LatLng[]{
+ new LatLng(38.897424, -77.036508),
+ new LatLng(38.909698, -77.029642),
+ new LatLng(38.907227, -77.036530),
+ new LatLng(38.905607, -77.031916),
+ new LatLng(38.889441, -77.050134),
+ new LatLng(38.888000, -77.050000) //Slight overlap to show re-ordering on selection
+ };
+
private MapboxMap mapboxMap;
private MapView mapView;
+ // MarkerView location updates
private MarkerView movingMarkerOne;
private MarkerView movingMarkerTwo;
private Random randomAnimator = new Random();
private Handler locationUpdateHandler = new Handler();
private Runnable moveMarkerRunnable = new MoveMarkerRunnable();
- private int rotation = 0;
- private static final LatLng[] LAT_LNGS = new LatLng[]{
- new LatLng(38.897424, -77.036508),
- new LatLng(38.909698, -77.029642),
- new LatLng(38.907227, -77.036530),
- new LatLng(38.905607, -77.031916),
- new LatLng(38.889441, -77.050134),
- new LatLng(38.888000, -77.050000) //Slight overlap to show re-ordering on selection
- };
+ // MarkerView rotate updates
+ private MarkerView rotateMarker;
+ private Handler rotateUpdateHandler = new Handler();
+ private Runnable rotateMarkerRunnable = new RotateMarkerRunnable();
+ private int rotation = 360;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -111,10 +117,13 @@ public class MarkerViewActivity extends AppCompatActivity {
.position(new LatLng(38.902580, -77.050102))
);
- MarkerViewActivity.this.mapboxMap.addMarker(new TextMarkerViewOptions()
+ rotateMarker = MarkerViewActivity.this.mapboxMap.addMarker(new TextMarkerViewOptions()
.text("A")
+ .rotation(rotation = 270)
.position(new LatLng(38.889876, -77.008849))
);
+ loopMarkerRotate();
+
MarkerViewActivity.this.mapboxMap.addMarker(new TextMarkerViewOptions()
.text("B")
@@ -167,6 +176,10 @@ public class MarkerViewActivity extends AppCompatActivity {
});
}
+ private void loopMarkerRotate() {
+ rotateUpdateHandler.postDelayed(rotateMarkerRunnable, 800);
+ }
+
@Override
protected void onStart() {
super.onStart();
@@ -183,27 +196,49 @@ public class MarkerViewActivity extends AppCompatActivity {
locationUpdateHandler.removeCallbacks(moveMarkerRunnable);
}
+ /**
+ * Updates the position of a Marker
+ */
private class MoveMarkerRunnable implements Runnable {
@Override
public void run() {
int i = randomAnimator.nextInt(9);
if (randomAnimator.nextInt() % 2 == 0) {
movingMarkerOne.setPosition(CarLocation.CAR_0_LNGS[i]);
- movingMarkerOne.setRotation(rotation = rotation + 45);
} else {
movingMarkerTwo.setPosition(CarLocation.CAR_1_LNGS[i]);
- movingMarkerTwo.setRotation(rotation = rotation + 90);
}
loopMarkerMove();
}
}
+ /**
+ * Updates the rotation of a Marker
+ */
+ private class RotateMarkerRunnable implements Runnable {
+
+ private final static int ROTATION_INCREASE_VALUE = 9;
+
+ @Override
+ public void run() {
+ rotation -= ROTATION_INCREASE_VALUE;
+ if (rotation >= 0) {
+ rotation += 360;
+ }
+ rotateMarker.setRotation(rotation);
+ loopMarkerRotate();
+ }
+ }
+
+ /**
+ * Adapts a MarkerView to display an abbreviated name in a TextView and a flag in an ImageView.
+ */
private static class CountryAdapter extends MapboxMap.MarkerViewAdapter<CountryMarkerView> {
private LayoutInflater inflater;
private MapboxMap mapboxMap;
- public CountryAdapter(@NonNull Context context, @NonNull MapboxMap mapboxMap) {
+ CountryAdapter(@NonNull Context context, @NonNull MapboxMap mapboxMap) {
super(context);
this.inflater = LayoutInflater.from(context);
this.mapboxMap = mapboxMap;
@@ -229,7 +264,7 @@ public class MarkerViewActivity extends AppCompatActivity {
@Override
public boolean onSelect(
- @NonNull final CountryMarkerView marker, @NonNull final View convertView, boolean reselectionForViewReuse) {
+ @NonNull final CountryMarkerView marker, @NonNull final View convertView, boolean reselectionForViewReuse) {
convertView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ObjectAnimator rotateAnimator = ObjectAnimator.ofFloat(convertView, View.ROTATION, 0, 360);
rotateAnimator.setDuration(reselectionForViewReuse ? 0 : 350);
@@ -268,13 +303,15 @@ public class MarkerViewActivity extends AppCompatActivity {
}
}
-
+ /**
+ * Adapts a MarkerView to display text in a TextView.
+ */
private static class TextAdapter extends MapboxMap.MarkerViewAdapter<TextMarkerView> {
private LayoutInflater inflater;
private MapboxMap mapboxMap;
- public TextAdapter(@NonNull Context context, @NonNull MapboxMap mapboxMap) {
+ TextAdapter(@NonNull Context context, @NonNull MapboxMap mapboxMap) {
super(context);
this.inflater = LayoutInflater.from(context);
this.mapboxMap = mapboxMap;
@@ -298,7 +335,7 @@ public class MarkerViewActivity extends AppCompatActivity {
@Override
public boolean onSelect(
- @NonNull final TextMarkerView marker, @NonNull final View convertView, boolean reselectionForViewReuse) {
+ @NonNull final TextMarkerView marker, @NonNull final View convertView, boolean reselectionForViewReuse) {
animateGrow(marker, convertView, 0);
// false indicates that we are calling selectMarker after our animation ourselves
@@ -409,7 +446,7 @@ public class MarkerViewActivity extends AppCompatActivity {
private static class CarLocation {
- public static LatLng[] CAR_0_LNGS = new LatLng[]{
+ static LatLng[] CAR_0_LNGS = new LatLng[]{
new LatLng(38.92334425495122, -77.0533673443786),
new LatLng(38.9234737236897, -77.05389484528261),
new LatLng(38.9257094658146, -76.98819752280579),
@@ -421,7 +458,7 @@ public class MarkerViewActivity extends AppCompatActivity {
new LatLng(38.862930274733635, -76.99647808241964)
};
- public static LatLng[] CAR_1_LNGS = new LatLng[]{
+ static LatLng[] CAR_1_LNGS = new LatLng[]{
new LatLng(38.94237975070426, -76.98324549005675),
new LatLng(38.941520236084486, -76.98234257804742),
new LatLng(38.85972219720714, -76.98955808483929),
@@ -432,6 +469,5 @@ public class MarkerViewActivity extends AppCompatActivity {
new LatLng(38.882869724926245, -77.02992539231113),
new LatLng(38.9371988177896, -76.97786740676564)
};
-
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java
index e6c30f4aa..6cef1898b 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerViewTest.java
@@ -4,18 +4,36 @@ import android.os.Parcelable;
import com.mapbox.mapboxsdk.exceptions.InvalidMarkerPositionException;
import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.utils.MockParcel;
+import org.junit.Before;
import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
public class MarkerViewTest {
+ @Mock
+ MapboxMap mapboxMap;
+
+ @Mock
+ MarkerViewManager markerViewManager;
+
+ @Before
+ public void beforeTest() {
+ MockitoAnnotations.initMocks(this);
+ }
+
@Test
public void testSanity() {
MarkerViewOptions markerOptions = new MarkerViewOptions();
@@ -29,7 +47,7 @@ public class MarkerViewTest {
}
@Test(expected = InvalidMarkerPositionException.class)
- public void testInvalidMarker(){
+ public void testInvalidMarker() {
new MarkerViewOptions().getMarker();
}
@@ -111,6 +129,88 @@ public class MarkerViewTest {
}
@Test
+ public void testRotationAboveMax() {
+ MarkerViewOptions markerOptions = new MarkerViewOptions().rotation(390).position(new LatLng());
+ MarkerView marker = markerOptions.getMarker();
+ assertEquals(marker.getRotation(), 30, 0);
+ }
+
+ @Test
+ public void testRotationBelowMin() {
+ MarkerViewOptions markerOptions = new MarkerViewOptions().rotation(-10).position(new LatLng());
+ MarkerView marker = markerOptions.getMarker();
+ assertEquals(marker.getRotation(), 350, 0);
+ }
+
+ @Test
+ public void testRotationUpdatePositive() {
+ float startRotation = 45;
+ float endRotation = 180;
+ float animationValue = 135;
+
+ // allow calls to our mock
+ when(mapboxMap.getMarkerViewManager()).thenReturn(markerViewManager);
+
+ MarkerViewOptions markerOptions = new MarkerViewOptions().position(new LatLng()).rotation(startRotation);
+ MarkerView marker = markerOptions.getMarker();
+ marker.setMapboxMap(mapboxMap);
+
+ marker.setRotation(endRotation);
+ verify(markerViewManager, times(1)).animateRotationBy(marker, animationValue);
+ }
+
+ @Test
+ public void testRotationUpdateNegative() {
+ float startRotation = 10;
+ float endRotation = 270;
+ float animationValue = -100;
+
+ // allow calls to our mock
+ when(mapboxMap.getMarkerViewManager()).thenReturn(markerViewManager);
+
+ MarkerViewOptions markerOptions = new MarkerViewOptions().position(new LatLng()).rotation(startRotation);
+ MarkerView marker = markerOptions.getMarker();
+ marker.setMapboxMap(mapboxMap);
+
+ marker.setRotation(endRotation);
+ verify(markerViewManager, times(1)).animateRotationBy(marker, animationValue);
+ }
+
+ @Test
+ public void testRotationUpdateMax() {
+ float startRotation = 359;
+ float endRotation = 0;
+ float animationValue = 1;
+
+ // allow calls to our mock
+ when(mapboxMap.getMarkerViewManager()).thenReturn(markerViewManager);
+
+ MarkerViewOptions markerOptions = new MarkerViewOptions().position(new LatLng()).rotation(startRotation);
+ MarkerView marker = markerOptions.getMarker();
+ marker.setMapboxMap(mapboxMap);
+
+ marker.setRotation(endRotation);
+ verify(markerViewManager, times(1)).animateRotationBy(marker, animationValue);
+ }
+
+ @Test
+ public void testRotationUpdateMin() {
+ float startRotation = 0;
+ float endRotation = 359;
+ float animationValue = -1;
+
+ // allow calls to our mock
+ when(mapboxMap.getMarkerViewManager()).thenReturn(markerViewManager);
+
+ MarkerViewOptions markerOptions = new MarkerViewOptions().position(new LatLng()).rotation(startRotation);
+ MarkerView marker = markerOptions.getMarker();
+ marker.setMapboxMap(mapboxMap);
+
+ marker.setRotation(endRotation);
+ verify(markerViewManager, times(1)).animateRotationBy(marker, animationValue);
+ }
+
+ @Test
public void testVisible() {
boolean visible = false;
MarkerViewOptions markerOptions = new MarkerViewOptions().visible(visible).position(new LatLng());