[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

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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;
@@ -97,22 +96,6 @@ public class MarkerViewTest {
assertEquals("anchorU should match ", 0, marker.getInfoWindowAnchorV(), 0);
}
@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;