diff options
author | Tobrun <tobrun.van.nuland@gmail.com> | 2016-05-18 09:45:56 +0200 |
---|---|---|
committer | Tobrun <tobrun@mapbox.com> | 2016-05-20 12:03:27 +0200 |
commit | c3aeb9facc0a0fd193d118fe25bf300c7d7931b7 (patch) | |
tree | 479993a80b9f94afedb1788589291c8e1aaa6fc6 | |
parent | 9b3a6c3a1bbf754e3296b1d529b7378e58db45a8 (diff) | |
download | qtlocation-mapboxgl-c3aeb9facc0a0fd193d118fe25bf300c7d7931b7.tar.gz |
[android] #3276 - View annotation integration in Marker
9 files changed, 440 insertions, 243 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerOptions.java index fc2022f9e1..27bf8345e7 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerOptions.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerOptions.java @@ -18,6 +18,7 @@ public abstract class BaseMarkerOptions<U extends Marker, T extends BaseMarkerOp protected String snippet; protected String title; protected Icon icon; + protected boolean markerView; public T position(LatLng position) { this.position = position; @@ -39,8 +40,16 @@ public abstract class BaseMarkerOptions<U extends Marker, T extends BaseMarkerOp return getThis(); } + public T markerView(boolean isMarkerView) { + this.markerView = isMarkerView; + return getThis(); + } + public abstract T getThis(); public abstract U getMarker(); + public boolean isViewMarker() { + return markerView; + } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java index 8a6ff519ad..cd2cbe6645 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java @@ -4,15 +4,16 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.view.View; -import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; /** * Marker is an annotation that shows an icon image at a geographical location. - * + * <p> * An {@link InfoWindow} can be shown when a Marker is pressed + * </p> */ public class Marker extends Annotation { @@ -20,9 +21,14 @@ public class Marker extends Annotation { private String snippet; private Icon icon; private String title; - private InfoWindow infoWindow = null; - private boolean infoWindowShown = false; + + private InfoWindow infoWindow; + private boolean infoWindowShown; + private int topOffsetPixels; + private int rightOffsetPixels; + + private boolean markerView; /** * Constructor @@ -36,6 +42,7 @@ public class Marker extends Annotation { snippet = baseMarkerOptions.snippet; icon = baseMarkerOptions.icon; title = baseMarkerOptions.title; + markerView = baseMarkerOptions.markerView; } public LatLng getPosition() { @@ -50,6 +57,10 @@ public class Marker extends Annotation { return title; } + public boolean isViewMarker() { + return markerView; + } + /** * Do not use this method. Used internally by the SDK. */ @@ -105,6 +116,10 @@ public class Marker extends Annotation { this.title = title; } + void setMarkerView(boolean isMarkerView){ + markerView = isMarkerView; + } + /** * Do not use this method. Used internally by the SDK. */ @@ -129,7 +144,7 @@ public class Marker extends Annotation { } private InfoWindow showInfoWindow(InfoWindow iw, MapView mapView) { - iw.open(mapView, this, getPosition(), 0, topOffsetPixels); + iw.open(mapView, this, getPosition(), rightOffsetPixels, topOffsetPixels); infoWindowShown = true; return iw; } @@ -148,6 +163,13 @@ public class Marker extends Annotation { this.topOffsetPixels = topOffsetPixels; } + /** + * Do not use this method. Used internally by the SDK. + */ + public void setRightOffsetPixels(int rightOffsetPixels) { + this.rightOffsetPixels = rightOffsetPixels; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java index 4800bc7a3e..a39bf4a4a4 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java @@ -3,7 +3,6 @@ package com.mapbox.mapboxsdk.annotations; import android.graphics.Bitmap; import android.os.Parcel; import android.os.Parcelable; -import android.support.annotation.Nullable; import com.mapbox.mapboxsdk.geometry.LatLng; @@ -38,7 +37,8 @@ public final class MarkerOptions extends BaseMarkerOptions<Marker, MarkerOptions position((LatLng) in.readParcelable(LatLng.class.getClassLoader())); snippet(in.readString()); title(in.readString()); - if(in.readByte()!=0){ + markerView(in.readByte() != 0); + if (in.readByte() != 0) { // this means we have an icon String iconId = in.readString(); Bitmap iconBitmap = in.readParcelable(Bitmap.class.getClassLoader()); @@ -63,6 +63,7 @@ public final class MarkerOptions extends BaseMarkerOptions<Marker, MarkerOptions out.writeString(getSnippet()); out.writeString(getTitle()); Icon icon = getIcon(); + out.writeByte((byte) (markerView ? 1 : 0)); out.writeByte((byte) (icon != null ? 1 : 0)); if (icon != null) { out.writeString(getIcon().getId()); @@ -86,6 +87,7 @@ public final class MarkerOptions extends BaseMarkerOptions<Marker, MarkerOptions marker.setSnippet(snippet); marker.setTitle(title); marker.setIcon(icon); + marker.setMarkerView(markerView); return marker; } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewSettings.java new file mode 100644 index 0000000000..2712578dad --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewSettings.java @@ -0,0 +1,160 @@ +package com.mapbox.mapboxsdk.annotations; + +import android.graphics.Point; +import android.graphics.PointF; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.AnimatorRes; +import android.support.annotation.NonNull; + +public class MarkerViewSettings implements Parcelable { + + private PointF centerOffset; + private Point infoWindowOffset; + private boolean flat; + + private int animEnterRes; + private int animSelectRes; + private int animDeselectRes; + private int animExitRes; + + MarkerViewSettings(PointF centerOffset, Point infoWindowOffset, boolean flat, int animEnterRes, int animSelectRes, int animDeselectRes, int animExitRes) { + this.centerOffset = centerOffset; + this.infoWindowOffset = infoWindowOffset; + this.flat = flat; + this.animEnterRes = animEnterRes; + this.animSelectRes = animSelectRes; + this.animDeselectRes = animDeselectRes; + this.animExitRes = animExitRes; + } + + MarkerViewSettings(Parcel parcel) { + this.centerOffset = parcel.readParcelable(PointF.class.getClassLoader()); + this.infoWindowOffset = parcel.readParcelable(Point.class.getClassLoader()); + this.flat = parcel.readByte() != 0; + this.animEnterRes = parcel.readInt(); + this.animSelectRes = parcel.readInt(); + this.animDeselectRes = parcel.readInt(); + this.animExitRes = parcel.readInt(); + } + + @NonNull + public PointF getCenterOffset() { + return centerOffset; + } + + @NonNull + public Point getInfoWindowOffset() { + return infoWindowOffset; + } + + public boolean isFlat() { + return flat; + } + + @AnimatorRes + public int getAnimEnterRes() { + return animEnterRes; + } + + @AnimatorRes + public int getAnimSelectRes() { + return animSelectRes; + } + + @AnimatorRes + public int getAnimDeselectRes() { + return animDeselectRes; + } + + @AnimatorRes + public int getAnimExitRes() { + return animExitRes; + } + + public static final Parcelable.Creator<MarkerViewSettings> CREATOR + = new Parcelable.Creator<MarkerViewSettings>() { + public MarkerViewSettings createFromParcel(Parcel in) { + return new MarkerViewSettings(in); + } + + public MarkerViewSettings[] newArray(int size) { + return new MarkerViewSettings[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(centerOffset, flags); + dest.writeParcelable(infoWindowOffset, flags); + dest.writeByte((byte) (flat ? 1 : 0)); + dest.writeInt(animEnterRes); + dest.writeInt(animSelectRes); + dest.writeInt(animDeselectRes); + dest.writeInt(animExitRes); + } + + /** + * Builder for composing LatLngBounds objects. + */ + public static final class Builder { + + private PointF centerOffset; + private Point infoWindowOffset; + private boolean flat; + + private int animEnterRes; + private int animSelectRes; + private int animDeselectRes; + private int animExitRes; + + public Builder() { + centerOffset = new PointF(); + infoWindowOffset = new Point(); + } + + public Builder centerOffset(float dx, float dy) { + centerOffset.offset(dx, dy); + return this; + } + + public Builder infoWindowOffset(int dx, int dy) { + infoWindowOffset.offset(dx, -dy); + return this; + } + + public Builder flat(boolean isFlat) { + flat = isFlat; + return this; + } + + public Builder animEnterRes(@AnimatorRes int animatorRes) { + animEnterRes = animatorRes; + return this; + } + + public Builder animSelectRes(@AnimatorRes int animatorRes) { + animSelectRes = animatorRes; + return this; + } + + public Builder animDeselectRes(@AnimatorRes int animatorRes) { + animDeselectRes = animatorRes; + return this; + } + + public Builder animExitRes(@AnimatorRes int animatorRes) { + animExitRes = animatorRes; + return this; + } + + public MarkerViewSettings build() { + return new MarkerViewSettings(centerOffset, infoWindowOffset, flat, animEnterRes, animSelectRes, animDeselectRes, animExitRes); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index c8a5edbb8c..3853cd362f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -25,7 +25,6 @@ import android.location.Location; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; import android.os.SystemClock; import android.support.annotation.CallSuper; @@ -35,7 +34,6 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.UiThread; import android.support.v4.content.ContextCompat; -import android.support.v4.util.LongSparseArray; import android.support.v4.view.GestureDetectorCompat; import android.support.v4.view.ScaleGestureDetectorCompat; import android.support.v4.view.animation.FastOutSlowInInterpolator; @@ -59,7 +57,6 @@ import android.widget.ArrayAdapter; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ListView; -import android.widget.TextView; import android.widget.ZoomButtonsController; import com.almeros.android.multitouch.gesturedetectors.RotateGestureDetector; @@ -98,7 +95,6 @@ import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.List; @@ -479,88 +475,15 @@ public class MapView extends FrameLayout { if (mViewMarkersUpdateRunning || currentTime < mViewMarkerBoundsUpdateTime) { return; } - mViewMarkerBoundsUpdateTime = currentTime + 300; - mViewMarkersUpdateRunning = true; - List<Marker> inBounds = new ArrayList<>(); - Map<Marker, View> outBounds = new HashMap<>(); - - LatLngBounds bounds = mMapboxMap.getProjection().getVisibleRegion().latLngBounds; - long[] ids = mNativeMapView.getAnnotationsInBounds(bounds); - LongSparseArray<View> markerViews = mMapboxMap.getMarkerViews(); - Log.v(MapboxConstants.TAG, "Annotations in bounds: " + ids.length); - - boolean found; - long key; - - // introduce new markers - for (long id : ids) { - found = false; - for (int i = 0; i < markerViews.size(); i++) { - key = markerViews.keyAt(i); - - if (id == key) { - found = true; - } - } - - if (!found) { - Annotation annotation = mMapboxMap.getAnnotation(id); - if (annotation instanceof Marker) { - inBounds.add((Marker) annotation); - } else { - Log.v(MapboxConstants.TAG, "Not instance of Marker" + id); - } - } else { - Log.v(MapboxConstants.TAG, "Already added " + id); - } - } + mMapboxMap.invalidateViewMarkersInBounds(); - // clean up out of bound markers - for (int i = 0; i < markerViews.size(); i++) { - found = false; - key = markerViews.keyAt(i); - for (long id : ids) { - if (id == key) { - found = true; - } - } - if (!found) { - Annotation annotation = mMapboxMap.getAnnotation(key); - if (annotation instanceof Marker) { - outBounds.put((Marker) annotation, markerViews.get(key)); - } else { - Log.v(MapboxConstants.TAG, "Not instance of Marker" + key); - } - } - } - Result result = new Result(inBounds, outBounds); - mMapboxMap.setViewMarkersBoundsTaskResult(result); mViewMarkersUpdateRunning = false; Log.v(MapboxConstants.TAG, "Amount of child views " + getChildCount()); } - public class Result { - private List<Marker> inBounds; - private Map<Marker, View> outBounds; - - public Result(List<Marker> inBounds, Map<Marker, View> outBounds) { - this.inBounds = inBounds; - this.outBounds = outBounds; - } - - public List<Marker> getInBounds() { - return inBounds; - } - - public Map<Marker, View> getOutBounds() { - return outBounds; - } - } -// } - /** * You must call this method from the parent's {@link android.app.Activity#onSaveInstanceState(Bundle)} * or {@link android.app.Fragment#onSaveInstanceState(Bundle)}. @@ -970,7 +893,7 @@ public class MapView extends FrameLayout { * <p> * DEPRECATED @see MapboxAccountManager#getAccessToken() * </p> - * <p> + * <p/> * Returns the current Mapbox access token used to load map styles and tiles. * </p> * @@ -1169,7 +1092,7 @@ public class MapView extends FrameLayout { mNativeMapView.removeAnnotations(ids); } - private List<Marker> getMarkersInBounds(@NonNull LatLngBounds bbox) { + List<Marker> getMarkersInBounds(@NonNull LatLngBounds bbox) { if (mDestroyed || bbox == null) { return new ArrayList<>(); } @@ -1438,27 +1361,29 @@ public class MapView extends FrameLayout { mCompassView.update(getDirection()); mMyLocationView.update(); - LongSparseArray<View> viewMarkers = mMapboxMap.getMarkerViews(); - for (int i = 0; i < viewMarkers.size(); i++) { - mViewHolder = viewMarkers.valueAt(i); + Map<Marker, View> viewMarkers = mMapboxMap.getMarkerViewMap(); + for (Marker marker : viewMarkers.keySet()) { + mViewHolder = viewMarkers.get(marker); if (mViewHolder != null) { - Marker marker = (Marker) mMapboxMap.getAnnotation(viewMarkers.keyAt(i)); - if (marker != null) { - PointF point = mMapboxMap.getProjection().toScreenLocation(marker.getPosition()); - mViewHolder.setX(point.x - (mViewHolder.getMeasuredWidth() / 2)); - mViewHolder.setY(point.y - (mViewHolder.getMeasuredHeight() / 2)); - - if (mViewHolder.getVisibility() == GONE) { - mViewHolder.animate().cancel(); - mViewHolder.setAlpha(0); - mViewHolder.animate().alpha(1).setDuration(MapboxConstants.ANIMATION_DURATION_SHORT).setInterpolator(new FastOutSlowInInterpolator()).setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - super.onAnimationStart(animation); - mViewHolder.setVisibility(VISIBLE); - } - }).start(); - } + PointF point = mMapboxMap.getProjection().toScreenLocation(marker.getPosition()); + mViewHolder.setX(point.x - (mViewHolder.getMeasuredWidth() / 2)); + mViewHolder.setY(point.y - (mViewHolder.getMeasuredHeight() / 2)); + + if (mViewHolder.getVisibility() == GONE) { + mViewHolder.animate().cancel(); + mViewHolder.setAlpha(0); + mViewHolder.animate() + .alpha(1) + .setDuration(MapboxConstants.ANIMATION_DURATION_SHORT) + .setInterpolator(new FastOutSlowInInterpolator()) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + super.onAnimationStart(animation); + mViewHolder.setVisibility(VISIBLE); + } + }) + .start(); } } } @@ -1756,7 +1681,7 @@ public class MapView extends FrameLayout { if (annotation.getId() == newSelectedMarkerId) { if (selectedMarkers.isEmpty() || !selectedMarkers.contains(annotation)) { // only handle click if no marker view is available - if(mMapboxMap.getMarkerViews().get(annotation.getId())==null) { + if (mMapboxMap.getMarkerViewMap().get(annotation) == null) { mMapboxMap.selectMarker((Marker) annotation); } } 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 d2531bb67e..14e5d706e8 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 @@ -6,6 +6,8 @@ import android.animation.AnimatorInflater; import android.animation.AnimatorListenerAdapter; import android.content.Context; import android.graphics.Bitmap; +import android.graphics.Point; +import android.graphics.PointF; import android.location.Location; import android.os.SystemClock; import android.support.annotation.AnimatorRes; @@ -31,6 +33,7 @@ import com.mapbox.mapboxsdk.annotations.IconFactory; import com.mapbox.mapboxsdk.annotations.InfoWindow; import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.annotations.MarkerOptions; +import com.mapbox.mapboxsdk.annotations.MarkerViewSettings; import com.mapbox.mapboxsdk.annotations.Polygon; import com.mapbox.mapboxsdk.annotations.PolygonOptions; import com.mapbox.mapboxsdk.annotations.Polyline; @@ -49,6 +52,8 @@ import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings; import java.lang.reflect.ParameterizedType; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -74,15 +79,14 @@ public class MapboxMap { private LongSparseArray<Annotation> mAnnotations; private List<Marker> mSelectedMarkers; - private LongSparseArray<View> mMarkerViews; + private Map<Marker, View> mMarkerViewMap; + private LongSparseArray<MarkerViewSettings> mMarkerViewSettingsMap; private List<InfoWindow> mInfoWindows; private MapboxMap.InfoWindowAdapter mInfoWindowAdapter; private OnMarkerViewClickListener mOnMarkerViewClickListener; - private int mMarkerViewItemAnimatorInRes; - private int mMarkerViewItemAnimatorOutRes; - private Bitmap mViewMarkerBitmap; + private Bitmap mViewMarkerBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); private boolean mMyLocationEnabled; private boolean mAllowConcurrentMultipleInfoWindows; @@ -112,10 +116,11 @@ public class MapboxMap { mTrackingSettings = new TrackingSettings(mMapView, mUiSettings); mProjection = new Projection(mapView); mAnnotations = new LongSparseArray<>(); - mMarkerViews = new LongSparseArray<>(); mMarkerViewAdapters = new ArrayList<>(); mSelectedMarkers = new ArrayList<>(); mInfoWindows = new ArrayList<>(); + mMarkerViewMap = new HashMap<>(); + mMarkerViewSettingsMap = new LongSparseArray<>(); } // @@ -648,86 +653,107 @@ public class MapboxMap { // Annotations // - void setViewMarkersBoundsTaskResult(MapView.Result result) { - Map<Marker, View> outBoundsMarker = result.getOutBounds(); + void invalidateViewMarkersInBounds() { + List<Marker> markers = mMapView.getMarkersInBounds(mProjection.getVisibleRegion().latLngBounds); + Log.v(MapboxConstants.TAG, "Annotations in bounds: " + markers.size()); + View convertView; - // out of bounds markers - for (Map.Entry<Marker, View> outBoundsEntry : outBoundsMarker.entrySet()) { - convertView = outBoundsEntry.getValue(); - if (convertView != null) { - if (mMarkerViewItemAnimatorOutRes != 0) { - Animator animator = AnimatorInflater.loadAnimator(mMapView.getContext(), mMarkerViewItemAnimatorOutRes); + // remove old markers + Iterator<Marker> iterator = mMarkerViewMap.keySet().iterator(); + while (iterator.hasNext()) { + Marker m = iterator.next(); + if (!markers.contains(m)) { + // remove marker + convertView = mMarkerViewMap.get(m); + MarkerViewSettings settings = mMarkerViewSettingsMap.get(m.getId()); + int deselectAnimRes = settings.getAnimDeselectRes(); + if (deselectAnimRes != 0) { + Animator animator = AnimatorInflater.loadAnimator(mMapView.getContext(), deselectAnimRes); animator.setDuration(0); animator.setTarget(convertView); animator.start(); } - removeMarkerView(outBoundsEntry.getKey().getId()); + removeMarkerView(m); + iterator.remove(); } } - // in bounds markers - List<Marker> inBoundsMarkers = result.getInBounds(); - for (final Marker marker : inBoundsMarkers) { - Log.v("TAG", "Calling get view for " + marker.getId()); - for (final MarkerViewAdapter adapter : mMarkerViewAdapters) { - if (adapter.getMarkerClass() == marker.getClass()) { - convertView = (View) adapter.getViewReusePool().acquire(); - View adaptedView = adapter.getView(marker, convertView, mMapView); - if (adaptedView != null) { - // hack to hide old marker, todo replace with visibility - Icon icon = marker.getIcon(); - if (!icon.getBitmap().equals(mViewMarkerBitmap)) { - if (mViewMarkerBitmap == null) { - Bitmap.Config conf = Bitmap.Config.ARGB_8888; - mViewMarkerBitmap = Bitmap.createBitmap(icon.getBitmap().getWidth(), icon.getBitmap().getHeight(), conf); - } - marker.setIcon(IconFactory.recreate(icon.getId(), mViewMarkerBitmap)); - } + // introduce new markers + for (final Marker marker : markers) { + if (marker.isViewMarker()) { + if (!mMarkerViewMap.containsKey(marker)) { + Log.v("TAG", "Calling get view for " + marker.getId()); + for (final MarkerViewAdapter adapter : mMarkerViewAdapters) { + if (adapter.getMarkerClass() == marker.getClass()) { - if (mSelectedMarkers.contains(marker)) { - if (mMarkerViewItemAnimatorInRes != 0) { - Animator animator = AnimatorInflater.loadAnimator(mMapView.getContext(), mMarkerViewItemAnimatorInRes); - animator.setDuration(0); - animator.setTarget(convertView); - animator.start(); + if (mMarkerViewSettingsMap.get(marker.getId()) == null) { + mMarkerViewSettingsMap.put(marker.getId(), adapter.getMarkerViewSettings(marker)); } - } - - adaptedView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - boolean clickHandled = false; - if (mOnMarkerViewClickListener != null) { - clickHandled = mOnMarkerViewClickListener.onMarkerClick(marker, v, adapter); + convertView = (View) adapter.getViewReusePool().acquire(); + View adaptedView = adapter.getView(marker, convertView, mMapView); + + // set user provided offset to view marker + final MarkerViewSettings markerViewSettings = mMarkerViewSettingsMap.get(marker.getId()); + Point infoWindowOffset = markerViewSettings.getInfoWindowOffset(); + marker.setTopOffsetPixels(infoWindowOffset.y); + marker.setRightOffsetPixels(infoWindowOffset.x); + + if (adaptedView != null) { + if (mSelectedMarkers.contains(marker)) { + // if a marker to be shown was selected + // replay that animation with duration 0 + int selectAnimRes = markerViewSettings.getAnimSelectRes(); + if (selectAnimRes != 0) { + Animator animator = AnimatorInflater.loadAnimator(mMapView.getContext(), selectAnimRes); + animator.setDuration(0); + animator.setTarget(convertView); + animator.start(); + } } - if (!clickHandled) { - if (mMarkerViewItemAnimatorInRes != 0) { - Animator animator = AnimatorInflater.loadAnimator(mMapView.getContext(), mMarkerViewItemAnimatorInRes); - animator.setTarget(v); - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); + adaptedView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + boolean clickHandled = false; + if (mOnMarkerViewClickListener != null) { + clickHandled = mOnMarkerViewClickListener.onMarkerClick(marker, v, adapter); + } + + if (!clickHandled) { + int enterAnimatorRes = markerViewSettings.getAnimSelectRes(); + if (enterAnimatorRes != 0) { + Animator animator = AnimatorInflater.loadAnimator(mMapView.getContext(), enterAnimatorRes); + animator.setTarget(v); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + selectMarker(marker); + } + }); + animator.start(); + } else { selectMarker(marker); } - }); - animator.start(); - } else { - selectMarker(marker); + } } + }); + + mMarkerViewMap.put(marker, adaptedView); + if (convertView == null) { + mMapView.addView(adaptedView); } } - }); - - mMarkerViews.append(marker.getId(), adaptedView); - if (convertView == null) { - mMapView.addView(adaptedView); } } + } else { + //already added + Log.v(MapboxConstants.TAG, "Marker already added"); } + } else { + Log.v(MapboxConstants.TAG, "Marker is not a view marker"); } } } @@ -1010,16 +1036,16 @@ public class MapboxMap { if (annotation instanceof Marker) { Marker marker = (Marker) annotation; marker.hideInfoWindow(); - removeMarkerView(annotation.getId()); + removeMarkerView(marker); + mMarkerViewMap.remove(marker); } long id = annotation.getId(); mMapView.removeAnnotation(id); mAnnotations.remove(id); } - private void removeMarkerView(long id) { - final View viewHolder = mMarkerViews.get(id); - final Marker marker = (Marker) getAnnotation(id); + private void removeMarkerView(Marker marker) { + final View viewHolder = mMarkerViewMap.get(marker); if (viewHolder != null && marker != null) { for (final MarkerViewAdapter<?> adapter : mMarkerViewAdapters) { if (adapter.getMarkerClass() == marker.getClass()) { @@ -1047,9 +1073,7 @@ public class MapboxMap { }); } } - } - mMarkerViews.remove(id); } /** @@ -1075,8 +1099,10 @@ public class MapboxMap { for (int i = 0; i < count; i++) { Annotation annotation = annotationList.get(i); if (annotation instanceof Marker) { - ((Marker) annotation).hideInfoWindow(); - removeMarkerView(annotation.getId()); + Marker marker = (Marker) annotation; + marker.hideInfoWindow(); + removeMarkerView(marker); + mMarkerViewMap.remove(marker); } ids[i] = annotationList.get(i).getId(); } @@ -1098,8 +1124,10 @@ public class MapboxMap { ids[i] = mAnnotations.keyAt(i); annotation = mAnnotations.get(ids[i]); if (annotation instanceof Marker) { - ((Marker) annotation).hideInfoWindow(); - removeMarkerView(annotation.getId()); + Marker marker = (Marker) annotation; + marker.hideInfoWindow(); + removeMarkerView(marker); + mMarkerViewMap.remove(marker); } } mMapView.removeAnnotations(ids); @@ -1251,11 +1279,14 @@ public class MapboxMap { marker.hideInfoWindow(); } - View viewMarker = mMarkerViews.get(marker.getId()); + View viewMarker = mMarkerViewMap.get(marker); if (viewMarker != null) { - Animator animator = AnimatorInflater.loadAnimator(mMapView.getContext(), mMarkerViewItemAnimatorOutRes); - animator.setTarget(viewMarker); - animator.start(); + int deselectAnimatorRes = mMarkerViewSettingsMap.get(marker.getId()).getAnimDeselectRes(); + if (deselectAnimatorRes != 0) { + Animator animator = AnimatorInflater.loadAnimator(mMapView.getContext(), deselectAnimatorRes); + animator.setTarget(viewMarker); + animator.start(); + } } } @@ -1291,8 +1322,13 @@ public class MapboxMap { private Marker prepareMarker(BaseMarkerOptions markerOptions) { Marker marker = markerOptions.getMarker(); - Icon icon = mMapView.loadIconForMarker(marker); - marker.setTopOffsetPixels(mMapView.getTopOffsetPixelsForIcon(icon)); + if (markerOptions.isViewMarker()) { + Icon icon = IconFactory.recreate("markerViewSettings", mViewMarkerBitmap); + marker.setIcon(icon); + } else { + Icon icon = mMapView.loadIconForMarker(marker); + marker.setTopOffsetPixels(mMapView.getTopOffsetPixelsForIcon(icon)); + } return marker; } @@ -1311,25 +1347,6 @@ public class MapboxMap { mOnMarkerViewClickListener = listener; } - public OnMarkerViewClickListener getOnMarkerViewClickListener() { - return mOnMarkerViewClickListener; - } - - public void setMarkerViewItemAnimation(@AnimatorRes int animationInRes, @AnimatorRes int animationOutRes) { - mMarkerViewItemAnimatorInRes = animationInRes; - mMarkerViewItemAnimatorOutRes = animationOutRes; - } - - @AnimatorRes - public int getMarkerViewItemAnimatorInRes() { - return mMarkerViewItemAnimatorInRes; - } - - @AnimatorRes - public int getMarkerViewItemAnimatorOutRes() { - return mMarkerViewItemAnimatorOutRes; - } - // // InfoWindow // @@ -1385,9 +1402,8 @@ public class MapboxMap { return mInfoWindows; } - // used by MapView - LongSparseArray<View> getMarkerViews() { - return mMarkerViews; + Map<Marker, View> getMarkerViewMap() { + return mMarkerViewMap; } private boolean isInfoWindowValidForMarker(@NonNull Marker marker) { @@ -1920,17 +1936,19 @@ public class MapboxMap { public static abstract class MarkerViewAdapter<U extends Marker> { + private Context context; private final Class<U> persistentClass; private final Pools.SimplePool<View> mViewReusePool; @SuppressWarnings("unchecked") public MarkerViewAdapter(Context context) { + this.context = context; persistentClass = (Class<U>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; mViewReusePool = new Pools.SimplePool<>(20); } @Nullable - public abstract View getView(@NonNull U marker, @Nullable View convertView, @NonNull ViewGroup parent); + public abstract View getView(@NonNull U marker, @NonNull View convertView, @NonNull ViewGroup parent); public Class<U> getMarkerClass() { return persistentClass; @@ -1939,6 +1957,15 @@ public class MapboxMap { public Pools.SimplePool<View> getViewReusePool() { return mViewReusePool; } + + public Context getContext() { + return context; + } + + public MarkerViewSettings getMarkerViewSettings(Marker marker) { + return new MarkerViewSettings.Builder().build(); + } + } public interface OnMarkerViewClickListener { 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 17e8946b3c..ac2ff05e67 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 @@ -1,9 +1,5 @@ package com.mapbox.mapboxsdk.testapp.activity.annotation; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.animation.PropertyValuesHolder; import android.app.ProgressDialog; import android.content.Context; import android.os.AsyncTask; @@ -12,15 +8,12 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; -import android.support.v7.view.ViewPropertyAnimatorCompatSet; 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.view.ViewPropertyAnimator; -import android.view.animation.BounceInterpolator; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Spinner; @@ -30,6 +23,7 @@ import android.widget.Toast; import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.annotations.MarkerOptions; +import com.mapbox.mapboxsdk.annotations.MarkerViewSettings; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; @@ -48,6 +42,7 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView private MapboxMap mMapboxMap; private MapView mMapView; + private boolean mCustomMarkerView; @Override protected void onCreate(Bundle savedInstanceState) { @@ -88,6 +83,8 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView @Override public void onClick(View v) { if (mMapboxMap != null) { + mCustomMarkerView = true; + fab.animate().alpha(0).start(); mMapboxMap.addMarkerViewAdapter(new TextAdapter(BulkMarkerActivity.this)); mMapView.addOnMapChangedListener(new MapView.OnMapChangedListener() { @@ -95,20 +92,25 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView public void onMapChanged(@MapView.MapChange int change) { if (change == MapView.REGION_IS_CHANGING || change == MapView.REGION_DID_CHANGE) { if (!mMapboxMap.getMarkerViewAdapters().isEmpty()) { - viewCountView.setText("ViewCache size " + (mMapView.getChildCount()-5)); + viewCountView.setText("ViewCache size " + (mMapView.getChildCount() - 5)); } } } }); - mMapboxMap.setMarkerViewItemAnimation(R.animator.scale_up, R.animator.scale_down); mMapboxMap.setOnMarkerViewClickListener(new MapboxMap.OnMarkerViewClickListener() { @Override public boolean onMarkerClick(@NonNull Marker marker, @NonNull View view, @NonNull MapboxMap.MarkerViewAdapter adapter) { - Toast.makeText(BulkMarkerActivity.this, "Hello "+marker.getId(), Toast.LENGTH_SHORT).show(); + Toast.makeText(BulkMarkerActivity.this, "Hello " + marker.getId(), Toast.LENGTH_SHORT).show(); return false; } }); + + // reload markers + Spinner spinner = (Spinner) findViewById(R.id.spinner); + if (spinner != null) { + loadBulkMarkers(spinner.getSelectedItemPosition()); + } } } }); @@ -148,8 +150,12 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { - int markersAmount = Integer.valueOf(getResources().getStringArray(R.array.bulk_marker_list)[position]); - new LoadBulkMarkerTask(this, mMapboxMap, markersAmount).execute(); + loadBulkMarkers(position); + } + + private void loadBulkMarkers(int selectedSpinnerPosition) { + int markersAmount = Integer.valueOf(getResources().getStringArray(R.array.bulk_marker_list)[selectedSpinnerPosition]); + new LoadBulkMarkerTask(this, mMapboxMap, markersAmount, mCustomMarkerView).execute(); } @Override @@ -205,13 +211,15 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView private Context mAppContext; private ProgressDialog mProgressDialog; private int mAmount; + private boolean mMarkerView; - public LoadBulkMarkerTask(Context context, MapboxMap mapboxMap, int amount) { + public LoadBulkMarkerTask(Context context, MapboxMap mapboxMap, int amount, boolean markerView) { mMapboxMap = mapboxMap; mapboxMap.removeAnnotations(); mProgressDialog = ProgressDialog.show(context, "Loading", "Fetching markers", false); mAppContext = context.getApplicationContext(); mAmount = amount; + mMarkerView = markerView; } @Override @@ -231,10 +239,10 @@ public class BulkMarkerActivity extends AppCompatActivity implements AdapterView location = locations.get(i); markerOptions.add(new MarkerOptions() .position(location) + .markerView(mMarkerView) .title(String.valueOf(i)) .snippet(formatter.format(location.getLatitude()) + ", " + formatter.format(location.getLongitude()))); } - } catch (IOException | JSONException e) { Log.e(TAG, "Could not add markers,", e); } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MultipleViewMarkerAdapterActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MultipleViewMarkerAdapterActivity.java index 91e54180eb..dfcbddb446 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MultipleViewMarkerAdapterActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/MultipleViewMarkerAdapterActivity.java @@ -7,7 +7,6 @@ import android.support.annotation.Nullable; 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; @@ -18,6 +17,7 @@ import android.widget.Toast; import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.annotations.MarkerOptions; +import com.mapbox.mapboxsdk.annotations.MarkerViewSettings; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; @@ -39,8 +39,8 @@ public class MultipleViewMarkerAdapterActivity extends AppCompatActivity { new LatLng(38.897424, -77.036508), new LatLng(38.897642, -77.041980), new LatLng(38.889876, -77.008849), - new LatLng(38.889441, -77.050134), - new LatLng(38.902580, -77.050102)}; + new LatLng(38.889441, -77.050134) + }; @Override protected void onCreate(Bundle savedInstanceState) { @@ -59,33 +59,53 @@ public class MultipleViewMarkerAdapterActivity extends AppCompatActivity { final TextView viewCountView = (TextView) findViewById(R.id.countView); mMapView = (MapView) findViewById(R.id.mapView); - mMapView.setAccessToken(getString(R.string.mapbox_access_token)); mMapView.onCreate(savedInstanceState); mMapView.getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(@NonNull MapboxMap mapboxMap) { mMapboxMap = mapboxMap; - mMapboxMap.addMarker(new CountryMarkerOptions().title("United States").abbrevName("us").flagRes(R.drawable.ic_us).position(new LatLng(38.899774, -77.023237))); - mMapboxMap.addMarkerViewAdapter(new TextAdapter(MultipleViewMarkerAdapterActivity.this)); - mMapboxMap.addMarkerViewAdapter(new CountryAdapter(MultipleViewMarkerAdapterActivity.this)); - + int infoWindowOffset = (int) getResources().getDimension(R.dimen.coordinatebounds_margin); + + // add flag marker + mMapboxMap.addMarker(new CountryMarkerOptions() + .markerView(true) + .title("United States") + .abbrevName("us") + .flagRes(R.drawable.ic_us) + .position(new LatLng(38.899774, -77.023237)) + ); + + mMapboxMap.addMarker(new CountryMarkerOptions() + .title("United States") + .abbrevName("us") + .flagRes(R.drawable.ic_us) + .position(new LatLng(38.902580, -77.050102)) + ); + + // add text markers for (int i = 0; i < LAT_LNGS.length; i++) { - mMapboxMap.addMarker(new MarkerOptions().position(LAT_LNGS[i]).title(String.valueOf(i))); + mMapboxMap.addMarker(new MarkerOptions() + .position(LAT_LNGS[i]) + .markerView(true) + .title(String.valueOf(i))); } + // set adapters + mMapboxMap.addMarkerViewAdapter(new TextAdapter(MultipleViewMarkerAdapterActivity.this)); + mMapboxMap.addMarkerViewAdapter(new CountryAdapter(MultipleViewMarkerAdapterActivity.this)); + mMapView.addOnMapChangedListener(new MapView.OnMapChangedListener() { @Override public void onMapChanged(@MapView.MapChange int change) { if (change == MapView.REGION_IS_CHANGING || change == MapView.REGION_DID_CHANGE) { - if (!mMapboxMap.getMarkerViewAdapters().isEmpty()) { + if (!mMapboxMap.getMarkerViewAdapters().isEmpty() && viewCountView != null) { viewCountView.setText("ViewCache size " + (mMapView.getChildCount() - 5)); } } } }); - mMapboxMap.setMarkerViewItemAnimation(R.animator.scale_up, R.animator.scale_down); mMapboxMap.setOnMarkerViewClickListener(new MapboxMap.OnMarkerViewClickListener() { @Override public boolean onMarkerClick(@NonNull Marker marker, @NonNull View view, @NonNull MapboxMap.MarkerViewAdapter adapter) { @@ -122,6 +142,21 @@ public class MultipleViewMarkerAdapterActivity extends AppCompatActivity { return convertView; } + @Override + public MarkerViewSettings getMarkerViewSettings(Marker marker) { + MarkerViewSettings.Builder builder = new MarkerViewSettings.Builder() + .animSelectRes(R.animator.scale_up) + .animDeselectRes(R.animator.scale_down) + .infoWindowOffset(0, (int) getContext().getResources() + .getDimension(R.dimen.fab_margin)); + + if (marker.getId() == 0) { + builder.flat(true); + } + + return builder.build(); + } + private static class ViewHolder { TextView title; } @@ -154,6 +189,17 @@ public class MultipleViewMarkerAdapterActivity extends AppCompatActivity { return convertView; } + @Override + public MarkerViewSettings getMarkerViewSettings(Marker marker) { + return new MarkerViewSettings.Builder() + .animSelectRes(R.animator.scale_up) + .animDeselectRes(R.animator.scale_down) + .infoWindowOffset(0, (int) getContext().getResources() + .getDimension(R.dimen.coordinatebounds_margin)) + .flat(true) + .build(); + } + private static class ViewHolder { ImageView flag; TextView abbrev; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/ViewMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/ViewMarkerActivity.java index f575755894..b369adfcb7 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/ViewMarkerActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/ViewMarkerActivity.java @@ -14,10 +14,10 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; -import android.widget.Toast; import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; import com.mapbox.mapboxsdk.annotations.Marker; +import com.mapbox.mapboxsdk.annotations.MarkerViewSettings; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapView; @@ -55,13 +55,11 @@ public class ViewMarkerActivity extends AppCompatActivity implements OnMapReadyC @Override public void onMapReady(final MapboxMap mapboxMap) { - - // Add country markers final List<BaseMarkerOptions> countries = new ArrayList<>(); - countries.add(new CountryMarkerOptions().title("China").abbrevName("ch").flagRes(R.drawable.ic_china).position(new LatLng(31.230416, 121.473701))); - countries.add(new CountryMarkerOptions().title("United States").abbrevName("us").flagRes(R.drawable.ic_us).position(new LatLng(38.907192, -77.036871))); - countries.add(new CountryMarkerOptions().title("Brazil").abbrevName("br").flagRes(R.drawable.ic_brazil).position(new LatLng(-15.798200, -47.922363))); - countries.add(new CountryMarkerOptions().title("Germany").abbrevName("de").flagRes(R.drawable.ic_germany).position(new LatLng(52.520007, 13.404954))); + countries.add(new CountryMarkerOptions().markerView(true).title("China").abbrevName("ch").flagRes(R.drawable.ic_china).position(new LatLng(31.230416, 121.473701))); + countries.add(new CountryMarkerOptions().markerView(true).title("United States").abbrevName("us").flagRes(R.drawable.ic_us).position(new LatLng(38.907192, -77.036871))); + countries.add(new CountryMarkerOptions().markerView(true).title("Brazil").abbrevName("br").flagRes(R.drawable.ic_brazil).position(new LatLng(-15.798200, -47.922363))); + countries.add(new CountryMarkerOptions().markerView(true).title("Germany").abbrevName("de").flagRes(R.drawable.ic_germany).position(new LatLng(52.520007, 13.404954))); final List<Marker> markers = mapboxMap.addMarkers(countries); // Add view marker adapter @@ -75,7 +73,7 @@ public class ViewMarkerActivity extends AppCompatActivity implements OnMapReadyC }); View view = findViewById(R.id.fab); - if(view!=null) { + if (view != null) { view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { |