diff options
author | Tobrun Van Nuland <tobrun.van.nuland@gmail.com> | 2016-06-03 14:29:20 +0200 |
---|---|---|
committer | Tobrun <tobrun.van.nuland@gmail.com> | 2016-06-05 14:23:24 +0200 |
commit | 436c23c40b37c4863ddfbc25c6463ae2f2767a06 (patch) | |
tree | f5f78cd7d9deaaa8e16be10a8480e73f5f93b977 /platform | |
parent | 4094987fa09561c4c69b1b4cf8a6cbf9d3dd0433 (diff) | |
download | qtlocation-mapboxgl-436c23c40b37c4863ddfbc25c6463ae2f2767a06.tar.gz |
[android] #5171 - introduce new select/deselect animation system for view markers
Diffstat (limited to 'platform')
11 files changed, 414 insertions, 167 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 0cd54fc0f0..57e6adb86d 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 @@ -30,6 +30,7 @@ public abstract class BaseMarkerViewOptions<U extends MarkerView, T extends Base protected int deselectAnimRes; protected int rotation; protected boolean visible = true; + protected boolean selected; /** * Default constructor 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 d0e47110f4..877a4e62d6 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,7 +1,9 @@ package com.mapbox.mapboxsdk.annotations; import android.graphics.Bitmap; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.view.View; import com.mapbox.mapboxsdk.maps.MapboxMap; @@ -31,15 +33,14 @@ public class MarkerView extends Marker { private boolean flat; private boolean visible = true; - private int selectAnimRes; - private int deselectAnimRes; - private float tiltValue; private float rotation; private float alpha = 1; private Icon markerViewIcon; + private boolean selected; + /** * Publicly hidden default constructor */ @@ -58,12 +59,11 @@ public class MarkerView extends Marker { this.infoWindowAnchorU = baseMarkerViewOptions.getInfoWindowAnchorU(); this.infoWindowAnchorV = baseMarkerViewOptions.getInfoWindowAnchorV(); this.flat = baseMarkerViewOptions.isFlat(); - this.selectAnimRes = baseMarkerViewOptions.getSelectAnimRes(); - this.deselectAnimRes = baseMarkerViewOptions.getDeselectAnimRes(); this.infoWindowAnchorU = baseMarkerViewOptions.infoWindowAnchorU; this.infoWindowAnchorV = baseMarkerViewOptions.infoWindowAnchorV; this.anchorU = baseMarkerViewOptions.anchorU; this.anchorV = baseMarkerViewOptions.anchorV; + this.selected = baseMarkerViewOptions.selected; } /** @@ -196,42 +196,6 @@ public class MarkerView extends Marker { } /** - * Get the animator resource used to animate to the selected state of a MarkerView. - * - * @return the animator resource used - */ - public int getSelectAnimRes() { - return selectAnimRes; - } - - /** - * Set the animator resource used to animate to the deselected state of a MarkerView. - * - * @param selectAnimRes the animator resource used - */ - public void setSelectAnimRes(int selectAnimRes) { - this.selectAnimRes = selectAnimRes; - } - - /** - * Get the animator resource used to animate to the deslected state of a MarkerView. - * - * @return the animator resource used - */ - public int getDeselectAnimRes() { - return deselectAnimRes; - } - - /** - * Set the animator resource used to animate to the selected state of a MarkerView. - * - * @param deselectAnimRes the animator resource used - */ - public void setDeselectAnimRes(int deselectAnimRes) { - this.deselectAnimRes = deselectAnimRes; - } - - /** * Internal method to get the current tilted value of a MarkerView. * * @return the tilted value @@ -338,6 +302,17 @@ public class MarkerView extends Marker { super.setIcon(transparentIcon); } + public boolean isSelected() { + return selected; + } + + /** + * For internal use only, use {@link MapboxMap#selectMarker(Marker)} instead. + */ + void setSelected(boolean selected) { + this.selected = selected; + } + /** * Get the icon of the MarkerView. * 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 39e17fd683..c8ecb3eb14 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 @@ -51,7 +51,6 @@ public class MarkerViewManager { this.mapView = mapView; this.markerViewMap = new HashMap<>(); this.defaultMarkerViewAdapter = new ImageMarkerViewAdapter(mapView.getContext()); - // FIXME only add this if a MarkerView instance is added to MapboxMap this.markerViewAdapters.add(defaultMarkerViewAdapter); } @@ -166,8 +165,7 @@ public class MarkerViewManager { /** * Animate a MarkerView to a deselected state. * <p> - * The {@link MarkerView#getDeselectAnimRes()} will be called to get the related animation. - * If non are provided, no animation will be started. + * The {@link ImageMarkerViewAdapter#onDeselect(MarkerView, View)} will be called to execute an animation. * </p> * * @param marker the MarkerView to deselect @@ -175,10 +173,29 @@ public class MarkerViewManager { public void deselect(@NonNull MarkerView marker) { final View convertView = markerViewMap.get(marker); if (convertView != null) { - int deselectAnimatorRes = marker.getDeselectAnimRes(); - if (deselectAnimatorRes != 0) { - AnimatorUtils.animate(convertView, deselectAnimatorRes); + for (MapboxMap.MarkerViewAdapter adapter : markerViewAdapters) { + if (adapter.getMarkerClass().equals(marker.getClass())) { + adapter.onDeselect(marker, convertView); + } + } + } + } + + public void select(@NonNull MarkerView marker) { + final View convertView = markerViewMap.get(marker); + for (MapboxMap.MarkerViewAdapter adapter : markerViewAdapters) { + if (adapter.getMarkerClass().equals(marker.getClass())) { + select(marker, convertView, adapter); + } + } + } + + public void select(@NonNull MarkerView marker, View convertView, MapboxMap.MarkerViewAdapter adapter) { + if (convertView != null) { + if (adapter.onSelect(marker, convertView, false)) { + mapboxMap.selectMarker(marker); } + marker.setSelected(true); } } @@ -191,42 +208,35 @@ public class MarkerViewManager { * the {@link MarkerView} from the underlying collection if needed. * </p> * - * @param marker the MarkerView to remove - * @param removeFromMap flag indicating if a MarkerView will be removed from the collection. + * @param marker the MarkerView to remove */ - public void removeMarkerView(MarkerView marker, boolean removeFromMap) { + public void removeMarkerView(MarkerView marker) { final View viewHolder = markerViewMap.get(marker); if (viewHolder != null && marker != null) { for (final MapboxMap.MarkerViewAdapter<?> adapter : markerViewAdapters) { - if (adapter.getMarkerClass() == marker.getClass()) { - - // get pool of Views associated to an adapter - final Pools.SimplePool<View> viewPool = adapter.getViewReusePool(); - - // cancel ongoing animations - viewHolder.animate().cancel(); - viewHolder.setAlpha(1); - AnimatorUtils.alpha(viewHolder, 0, new AnimatorUtils.OnAnimationEndListener() { - @Override - public void onAnimationEnd() { - viewHolder.setVisibility(View.GONE); - viewPool.release(viewHolder); - } - }); + if (adapter.getMarkerClass().equals(marker.getClass())) { + if (adapter.prepareViewForReuse(marker, viewHolder)) { + adapter.releaseView(viewHolder); + } } } } - if (removeFromMap) { - markerViewMap.remove(marker); - } + markerViewMap.remove(marker); } /** - * Add a MarkerViewAdapter. + * Add a MarkerViewAdapter to the MarkerViewManager. + * <p> + * The provided MarkerViewAdapter must use supply a generic subclass of MarkerView. + * </p> * * @param markerViewAdapter the MarkerViewAdapter to add */ - public void addMarkerViewAdapter(@Nullable MapboxMap.MarkerViewAdapter markerViewAdapter) { + public void addMarkerViewAdapter(MapboxMap.MarkerViewAdapter markerViewAdapter) { + if (markerViewAdapter.getMarkerClass().equals(MarkerView.class)) { + throw new RuntimeException("Providing a custom MarkerViewAdapter requires subclassing MarkerView"); + } + if (!markerViewAdapters.contains(markerViewAdapter)) { markerViewAdapters.add(markerViewAdapter); invalidateViewMarkersInBounds(); @@ -288,12 +298,13 @@ public class MarkerViewManager { if (!markers.contains(m)) { // remove marker convertView = markerViewMap.get(m); - int deselectAnimRes = m.getDeselectAnimRes(); - if (deselectAnimRes != 0) { - AnimatorUtils.animate(convertView, deselectAnimRes, 0); + for (MapboxMap.MarkerViewAdapter adapter : markerViewAdapters) { + if (adapter.getMarkerClass().equals(m.getClass())) { + adapter.prepareViewForReuse(m, convertView); + adapter.releaseView(convertView); + iterator.remove(); + } } - removeMarkerView(m, false); - iterator.remove(); } } @@ -301,7 +312,7 @@ public class MarkerViewManager { for (final MarkerView marker : markers) { if (!markerViewMap.containsKey(marker)) { for (final MapboxMap.MarkerViewAdapter adapter : markerViewAdapters) { - if (adapter.getMarkerClass() == marker.getClass()) { + if (adapter.getMarkerClass().equals(marker.getClass())) { convertView = (View) adapter.getViewReusePool().acquire(); final View adaptedView = adapter.getView(marker, convertView, mapView); if (adaptedView != null) { @@ -321,13 +332,11 @@ public class MarkerViewManager { if (mapboxMap.getSelectedMarkers().contains(marker)) { // if a marker to be shown was selected // replay that animation with duration 0 - int selectAnimRes = marker.getSelectAnimRes(); - if (selectAnimRes != 0) { - AnimatorUtils.animate(convertView, selectAnimRes, 0); + if (adapter.onSelect(marker, adaptedView, true)) { + mapboxMap.selectMarker(marker); } } - final int animSelectRes = marker.getSelectAnimRes(); adaptedView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { @@ -342,17 +351,7 @@ public class MarkerViewManager { int infoWindowOffsetY = (int) ((adaptedView.getHeight() * marker.getInfoWindowAnchorV()) - marker.getOffsetY()); marker.setTopOffsetPixels(infoWindowOffsetY); marker.setRightOffsetPixels(infoWindowOffsetX); - - if (animSelectRes != 0) { - AnimatorUtils.animate(v, animSelectRes, new AnimatorUtils.OnAnimationEndListener() { - @Override - public void onAnimationEnd() { - mapboxMap.selectMarker(marker); - } - }); - } else { - mapboxMap.selectMarker(marker); - } + select(marker, v, adapter); } } }); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java index 0c9faed355..ebf98173c1 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewOptions.java @@ -82,8 +82,6 @@ public class MarkerViewOptions extends BaseMarkerViewOptions<MarkerView, MarkerV marker.setFlat(flat); marker.setAnchor(anchorU, anchorV); marker.setInfoWindowAnchor(infoWindowAnchorU, infoWindowAnchorV); - marker.setSelectAnimRes(selectAnimRes); - marker.setDeselectAnimRes(deselectAnimRes); marker.setRotation(rotation); marker.setVisible(visible); return marker; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 81b56cbd4c..bc0924c4f3 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -951,7 +951,7 @@ public class MapboxMap { Marker marker = (Marker) annotation; marker.hideInfoWindow(); if (marker instanceof MarkerView) { - mMarkerViewManager.removeMarkerView((MarkerView) marker, true); + mMarkerViewManager.removeMarkerView((MarkerView) marker); } } long id = annotation.getId(); @@ -985,7 +985,7 @@ public class MapboxMap { Marker marker = (Marker) annotation; marker.hideInfoWindow(); if (marker instanceof MarkerView) { - mMarkerViewManager.removeMarkerView((MarkerView) marker, true); + mMarkerViewManager.removeMarkerView((MarkerView) marker); } } ids[i] = annotationList.get(i).getId(); @@ -1011,7 +1011,7 @@ public class MapboxMap { Marker marker = (Marker) annotation; marker.hideInfoWindow(); if (marker instanceof MarkerView) { - mMarkerViewManager.removeMarkerView((MarkerView) marker, true); + mMarkerViewManager.removeMarkerView((MarkerView) marker); } } } @@ -1841,11 +1841,56 @@ public class MapboxMap { public abstract View getView(@NonNull U marker, @NonNull View convertView, @NonNull ViewGroup parent); /** + * Called when an MarkerView is removed from the MapView or the View object is going to be reused. + * <p> + * <p> + * This method should be used to reset an animated view back to it's original state for view reuse. + * </p> + * <p> + * Returning true indicates you want to the view reuse to be handled automatically. + * Returning false indicates you want to perform an animation and you are required calling {@link #releaseView(View)} yourself. + * </p> + * + * @param marker the model representing the MarkerView + * @param convertView the reusable view + * @return true if you want reuse to occur automatically, false if you want to manage this yourself. + */ + public boolean prepareViewForReuse(@NonNull MarkerView marker, @NonNull View convertView) { + return true; + } + + /** + * Called when a MarkerView is selected from the MapView. + * <p> + * Returning true from this method indicates you want to move the MarkerView to the selected state. + * Returning false indicates you want to animate the View first an manually select the MarkerView when appropriate. + * </p> + * + * @param marker the model representing the MarkerView + * @param convertView the reusable view + * @param reselectionFromRecycling indicates if the onSelect callback is the initial selection + * callback or that selection occurs due to recreation of selected marker + * @return true if you want to select the Marker immediately, false if you want to manage this yourself. + */ + public boolean onSelect(@NonNull U marker, @NonNull View convertView, boolean reselectionFromRecycling) { + return true; + } + + /** + * Called when a MarkerView is deselected from the MapView. + * + * @param marker the model representing the MarkerView + * @param convertView the reusable view + */ + public void onDeselect(@NonNull U marker, @NonNull View convertView) { + } + + /** * Returns the generic type of the used MarkerView. * * @return the generic type */ - public Class<U> getMarkerClass() { + public final Class<U> getMarkerClass() { return persistentClass; } @@ -1854,7 +1899,7 @@ public class MapboxMap { * * @return the pool associated to this adapter */ - public Pools.SimplePool<View> getViewReusePool() { + public final Pools.SimplePool<View> getViewReusePool() { return mViewReusePool; } @@ -1863,9 +1908,19 @@ public class MapboxMap { * * @return the context used */ - public Context getContext() { + public final Context getContext() { return context; } + + /** + * Release a View to the ViewPool. + * + * @param view the view to be released + */ + public final void releaseView(View view) { + view.setVisibility(View.GONE); + mViewReusePool.release(view); + } } /** diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java index 7a2241a84c..e4bf383c96 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java @@ -3,28 +3,28 @@ package com.mapbox.mapboxsdk.testapp.activity.annotation; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.app.ProgressDialog; -import android.content.Context; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import android.support.v4.content.res.ResourcesCompat; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.util.Log; -import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; -import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; +import com.mapbox.mapboxsdk.annotations.Icon; +import com.mapbox.mapboxsdk.annotations.IconFactory; import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.annotations.MarkerOptions; -import com.mapbox.mapboxsdk.annotations.MarkerView; import com.mapbox.mapboxsdk.annotations.MarkerViewOptions; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapView; @@ -86,36 +86,6 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView } } - public static class TextAdapter extends MapboxMap.MarkerViewAdapter<MarkerView> { - - private LayoutInflater inflater; - - public TextAdapter(@NonNull Context context) { - super(context); - this.inflater = LayoutInflater.from(context); - } - - @Nullable - @Override - public View getView(@NonNull MarkerView marker, @Nullable View convertView, @NonNull ViewGroup parent) { - ViewHolder viewHolder; - if (convertView == null) { - viewHolder = new ViewHolder(); - convertView = inflater.inflate(R.layout.view_text_marker, parent, false); - viewHolder.title = (TextView) convertView.findViewById(R.id.textView); - convertView.setTag(viewHolder); - } else { - viewHolder = (ViewHolder) convertView.getTag(); - } - viewHolder.title.setText(marker.getTitle()); - return convertView; - } - - private static class ViewHolder { - TextView title; - } - } - @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { int amount = Integer.valueOf(getResources().getStringArray(R.array.bulk_marker_list)[position]); @@ -126,7 +96,6 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView } } - private void onLatLngListLoaded(List<LatLng> latLngs, int amount) { mLocations = latLngs; showMarkers(amount); @@ -140,26 +109,32 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView } if (mCustomMarkerView) { - showNativeMarkers(amount); + showViewMarkers(amount); } else { showGlMarkers(amount); } } - private void showNativeMarkers(int amount) { + private void showViewMarkers(int amount) { DecimalFormat formatter = new DecimalFormat("#.#####"); Random random = new Random(); int randomIndex; + Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.drawable.ic_droppin_24dp, getTheme()); + int redColor = ResourcesCompat.getColor(getResources(), android.R.color.holo_red_dark, getTheme()); + drawable.setColorFilter(redColor, PorterDuff.Mode.SRC_IN); + Icon icon = IconFactory.getInstance(this).fromDrawable(drawable); + for (int i = 0; i < amount; i++) { randomIndex = random.nextInt(mLocations.size()); LatLng latLng = mLocations.get(randomIndex); mMapboxMap.addMarker(new MarkerViewOptions() .position(latLng) + .icon(icon) .title(String.valueOf(i)) .snippet(formatter.format(latLng.getLatitude()) + ", " + formatter.format(latLng.getLongitude()))); } - } + } private void showGlMarkers(int amount) { List<MarkerOptions> markerOptionsList = new ArrayList<>(); @@ -247,9 +222,6 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView showMarkers(amount); } - // add adapter - mMapboxMap.getMarkerViewManager().addMarkerViewAdapter(new TextAdapter(BulkMarkerActivity.this)); - mMapView.addOnMapChangedListener(new MapView.OnMapChangedListener() { @Override public void onMapChanged(@MapView.MapChange int change) { 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 60ace581a4..93bed990fc 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 @@ -1,5 +1,9 @@ package com.mapbox.mapboxsdk.testapp.activity.annotation; +import android.animation.Animator; +import android.animation.AnimatorInflater; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; import android.content.Context; import android.os.Bundle; import android.support.annotation.NonNull; @@ -18,6 +22,8 @@ import android.widget.Toast; import com.mapbox.mapboxsdk.annotations.Icon; import com.mapbox.mapboxsdk.annotations.IconFactory; import com.mapbox.mapboxsdk.annotations.Marker; +import com.mapbox.mapboxsdk.annotations.MarkerOptions; +import com.mapbox.mapboxsdk.annotations.MarkerView; import com.mapbox.mapboxsdk.annotations.MarkerViewManager; import com.mapbox.mapboxsdk.annotations.MarkerViewOptions; import com.mapbox.mapboxsdk.geometry.LatLng; @@ -28,6 +34,8 @@ import com.mapbox.mapboxsdk.testapp.R; import com.mapbox.mapboxsdk.testapp.model.annotations.CountryMarkerOptions; import com.mapbox.mapboxsdk.testapp.model.annotations.CountryMarkerView; import com.mapbox.mapboxsdk.testapp.model.annotations.CountryMarkerViewOptions; +import com.mapbox.mapboxsdk.testapp.model.annotations.TextMarkerView; +import com.mapbox.mapboxsdk.testapp.model.annotations.TextMarkerViewOptions; public class MarkerViewActivity extends AppCompatActivity { @@ -35,13 +43,10 @@ public class MarkerViewActivity extends AppCompatActivity { private MapView mMapView; private final static LatLng[] LAT_LNGS = new LatLng[]{ - new LatLng(38.907327, -77.041293), + 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.897424, -77.036508), - new LatLng(38.897642, -77.041980), - new LatLng(38.889876, -77.008849), new LatLng(38.889441, -77.050134) }; @@ -83,24 +88,40 @@ public class MarkerViewActivity extends AppCompatActivity { ); } - // add child classes of ViewMarker + // add custom ViewMarker CountryMarkerViewOptions options = new CountryMarkerViewOptions(); options.flagRes(R.drawable.icon_burned); options.abbrevName("Mapbox"); + options.title("Hello"); options.position(new LatLng(38.899774, -77.023237)); options.selectAnimatorResource(R.animator.rotate_360); options.deselectAnimatorResource(R.animator.rotate_360); options.flat(true); mapboxMap.addMarker(options); - // add default GL marker - mMapboxMap.addMarker(new CountryMarkerOptions() + mMapboxMap.addMarker(new MarkerOptions() .title("United States") .position(new LatLng(38.902580, -77.050102)) ); + mMapboxMap.addMarker(new TextMarkerViewOptions() + .text("A") + .position(new LatLng(38.889876, -77.008849)) + ); + + mMapboxMap.addMarker(new TextMarkerViewOptions() + .text("B") + .position(new LatLng(38.907327, -77.041293)) + ); + + mMapboxMap.addMarker(new TextMarkerViewOptions() + .text("C") + .position(new LatLng(38.897642, -77.041980)) + ); + // set adapters for child classes of ViewMarker - markerViewManager.addMarkerViewAdapter(new CountryAdapter(MarkerViewActivity.this)); + markerViewManager.addMarkerViewAdapter(new CountryAdapter(MarkerViewActivity.this, mapboxMap)); + markerViewManager.addMarkerViewAdapter(new TextAdapter(MarkerViewActivity.this, mapboxMap)); // add a change listener to validate the size of amount of child views mMapView.addOnMapChangedListener(new MapView.OnMapChangedListener() { @@ -129,10 +150,12 @@ public class MarkerViewActivity extends AppCompatActivity { private static class CountryAdapter extends MapboxMap.MarkerViewAdapter<CountryMarkerView> { private LayoutInflater inflater; + private MapboxMap mapboxMap; - public CountryAdapter(@NonNull Context context) { + public CountryAdapter(@NonNull Context context, @NonNull MapboxMap mapboxMap) { super(context); this.inflater = LayoutInflater.from(context); + this.mapboxMap = mapboxMap; } @Nullable @@ -153,12 +176,143 @@ public class MarkerViewActivity extends AppCompatActivity { return convertView; } + @Override + public boolean onSelect(@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); + rotateAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + convertView.setLayerType(View.LAYER_TYPE_NONE, null); + mapboxMap.selectMarker(marker); + } + }); + rotateAnimator.start(); + + // false indicates that we are calling selectMarker after our animation ourselves + // true will let the system call it for you, which will result in showing an InfoWindow instantly + return false; + } + + @Override + public void onDeselect(@NonNull CountryMarkerView marker, @NonNull final View convertView) { + convertView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + ObjectAnimator rotateAnimator = ObjectAnimator.ofFloat(convertView, View.ROTATION, 360, 0); + rotateAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + convertView.setLayerType(View.LAYER_TYPE_NONE, null); + } + }); + rotateAnimator.start(); + } + private static class ViewHolder { ImageView flag; TextView abbrev; } } + + private static class TextAdapter extends MapboxMap.MarkerViewAdapter<TextMarkerView> { + + private LayoutInflater inflater; + private MapboxMap mapboxMap; + + public TextAdapter(@NonNull Context context, @NonNull MapboxMap mapboxMap) { + super(context); + this.inflater = LayoutInflater.from(context); + this.mapboxMap = mapboxMap; + } + + @Nullable + @Override + public View getView(@NonNull TextMarkerView marker, @Nullable View convertView, @NonNull ViewGroup parent) { + ViewHolder viewHolder; + if (convertView == null) { + viewHolder = new ViewHolder(); + convertView = inflater.inflate(R.layout.view_text_marker, parent, false); + viewHolder.textView = (TextView) convertView.findViewById(R.id.textView); + convertView.setTag(viewHolder); + } else { + viewHolder = (ViewHolder) convertView.getTag(); + } + viewHolder.textView.setText(marker.getText()); + return convertView; + } + + @Override + public boolean onSelect(@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 + // true will let the system call it for you, which will result in showing an InfoWindow instantly + return false; + } + + @Override + public void onDeselect(@NonNull TextMarkerView marker, @NonNull final View convertView) { + animateShrink(convertView, 350); + } + + @Override + public boolean prepareViewForReuse(@NonNull MarkerView marker, @NonNull View convertView) { + // this method is called before a view will be reused, we need to restore view state + // as we have scaled the view in onSelect. If not correctly applied other MarkerView will + // become large since these have been recycled + + // cancel ongoing animation + convertView.animate().cancel(); + + if (marker.isSelected()) { + // shrink view to be able to be reused + animateShrink(convertView, 0); + } + + // true if you want reuse to occur automatically, false if you want to manage this yourself + return true; + } + + private void animateGrow(@NonNull final MarkerView marker, @NonNull final View convertView, int duration) { + convertView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + Animator animator = AnimatorInflater.loadAnimator(convertView.getContext(), R.animator.scale_up); + animator.setDuration(duration); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + convertView.setLayerType(View.LAYER_TYPE_NONE, null); + mapboxMap.selectMarker(marker); + } + }); + animator.setTarget(convertView); + animator.start(); + } + + private void animateShrink(@NonNull final View convertView, int duration) { + convertView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + Animator animator = AnimatorInflater.loadAnimator(convertView.getContext(), R.animator.scale_down); + animator.setDuration(duration); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + convertView.setLayerType(View.LAYER_TYPE_NONE, null); + } + }); + animator.setTarget(convertView); + animator.start(); + } + + private static class ViewHolder { + TextView textView; + } + } + + @Override public void onResume() { super.onResume(); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarkerViewOptions.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarkerViewOptions.java index c4ef4a8d13..ab0fd396ea 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarkerViewOptions.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarkerViewOptions.java @@ -64,6 +64,7 @@ public class CountryMarkerViewOptions extends BaseMarkerViewOptions<CountryMarke out.writeInt(getSelectAnimRes()); out.writeInt(getDeselectAnimRes()); out.writeInt(getRotation()); + out.writeByte((byte) (selected ? 1 :0)); Icon icon = getIcon(); out.writeByte((byte) (icon != null ? 1 : 0)); if (icon != null) { diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/TextMarkerView.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/TextMarkerView.java new file mode 100644 index 0000000000..c0a589cb57 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/TextMarkerView.java @@ -0,0 +1,18 @@ +package com.mapbox.mapboxsdk.testapp.model.annotations; + +import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions; +import com.mapbox.mapboxsdk.annotations.MarkerView; + +public class TextMarkerView extends MarkerView { + + private String text; + + public TextMarkerView(BaseMarkerViewOptions baseMarkerViewOptions, String text) { + super(baseMarkerViewOptions); + this.text = text; + } + + public String getText() { + return text; + } +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/TextMarkerViewOptions.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/TextMarkerViewOptions.java new file mode 100644 index 0000000000..48ab57345e --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/TextMarkerViewOptions.java @@ -0,0 +1,91 @@ +package com.mapbox.mapboxsdk.testapp.model.annotations; + +import android.graphics.Bitmap; +import android.os.Parcel; +import android.os.Parcelable; + +import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions; +import com.mapbox.mapboxsdk.annotations.Icon; +import com.mapbox.mapboxsdk.annotations.IconFactory; +import com.mapbox.mapboxsdk.geometry.LatLng; + +public class TextMarkerViewOptions extends BaseMarkerViewOptions<TextMarkerView,TextMarkerViewOptions> { + + private String text; + + public TextMarkerViewOptions() { + } + + protected TextMarkerViewOptions(Parcel in) { + position((LatLng) in.readParcelable(LatLng.class.getClassLoader())); + snippet(in.readString()); + title(in.readString()); + flat(in.readByte() != 0); + anchor(in.readFloat(), in.readFloat()); + infoWindowAnchor(in.readFloat(), in.readFloat()); + selectAnimatorResource(in.readInt()); + deselectAnimatorResource(in.readInt()); + rotation(in.readInt()); + if (in.readByte() != 0) { + // this means we have an icon + String iconId = in.readString(); + Bitmap iconBitmap = in.readParcelable(Bitmap.class.getClassLoader()); + Icon icon = IconFactory.recreate(iconId, iconBitmap); + icon(icon); + } + text(in.readString()); + } + + @Override + public TextMarkerViewOptions getThis() { + return this; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(getPosition(), flags); + out.writeString(getSnippet()); + out.writeString(getTitle()); + out.writeByte((byte) (isFlat() ? 1 : 0)); + out.writeFloat(getAnchorU()); + out.writeFloat(getAnchorV()); + out.writeFloat(getInfoWindowAnchorU()); + out.writeFloat(getInfoWindowAnchorV()); + out.writeInt(getSelectAnimRes()); + out.writeInt(getDeselectAnimRes()); + out.writeInt(getRotation()); + Icon icon = getIcon(); + out.writeByte((byte) (icon != null ? 1 : 0)); + if (icon != null) { + out.writeString(getIcon().getId()); + out.writeParcelable(getIcon().getBitmap(), flags); + } + out.writeString(text); + } + + @Override + public TextMarkerView getMarker() { + return new TextMarkerView(this, text); + } + + public TextMarkerViewOptions text(String text) { + this.text = text; + return getThis(); + } + + public static final Parcelable.Creator<CountryMarkerViewOptions> CREATOR + = new Parcelable.Creator<CountryMarkerViewOptions>() { + public CountryMarkerViewOptions createFromParcel(Parcel in) { + return new CountryMarkerViewOptions(in); + } + + public CountryMarkerViewOptions[] newArray(int size) { + return new CountryMarkerViewOptions[size]; + } + }; +} 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 7d874f21fe..a4b3248f2e 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 @@ -3,7 +3,6 @@ package com.mapbox.mapboxsdk.annotations; import android.os.Parcelable; import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.testapp.R; import com.mapbox.mapboxsdk.utils.MockParcel; import org.junit.Test; @@ -98,22 +97,6 @@ public class MarkerViewTest { } @Test - public void testSelectAnimRes() { - int animatorRes = R.animator.rotate_360; - MarkerViewOptions markerOptions = new MarkerViewOptions().selectAnimatorResource(animatorRes); - MarkerView marker = markerOptions.getMarker(); - assertEquals("select animator resource should match ", animatorRes, marker.getSelectAnimRes(), 0); - } - - @Test - public void testDeselectAnimRes() { - int animatorRes = R.animator.rotate_360; - MarkerViewOptions markerOptions = new MarkerViewOptions().deselectAnimatorResource(animatorRes); - MarkerView marker = markerOptions.getMarker(); - assertEquals("deselect animator resource should match ", animatorRes, marker.getDeselectAnimRes(), 0); - } - - @Test public void testRotation() { int rotation = 90; MarkerViewOptions markerOptions = new MarkerViewOptions().rotation(rotation); |