[android] #5171 - introduce new select/deselect animation system for view markers

This commit is contained in:
Tobrun Van Nuland
2016-06-03 14:29:20 +02:00
committed by Tobrun
parent 4094987fa0
commit 436c23c40b
11 changed files with 414 additions and 167 deletions

View File

@@ -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

View File

@@ -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;
}
/**
@@ -195,42 +195,6 @@ public class MarkerView extends Marker {
this.flat = flat;
}
/**
* 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.
*
@@ -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.
*

View File

@@ -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,13 +173,32 @@ 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);
}
}
/**
* Remove a MarkerView from a map.
* <p>
@@ -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);
}
}
});

View File

@@ -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;

View File

@@ -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);
}
}
}
@@ -1840,12 +1840,57 @@ public class MapboxMap {
@Nullable
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);
}
}
/**