diff options
Diffstat (limited to 'platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations')
11 files changed, 665 insertions, 208 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/ArrowDirection.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/ArrowDirection.java new file mode 100644 index 0000000000..2fe5f8f420 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/ArrowDirection.java @@ -0,0 +1,30 @@ +package com.mapbox.mapboxsdk.annotations; + +import android.support.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +class ArrowDirection { + @IntDef( {LEFT, RIGHT, TOP, BOTTOM}) + @Retention(RetentionPolicy.SOURCE) + @interface Value { + } + + static final int LEFT = 0; + static final int RIGHT = 1; + static final int TOP = 2; + static final int BOTTOM = 3; + + @Value + private final int value; + + ArrowDirection(@Value int value) { + this.value = value; + } + + @Value + public int getValue() { + return value; + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Bubble.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Bubble.java new file mode 100644 index 0000000000..6fad249780 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Bubble.java @@ -0,0 +1,311 @@ +package com.mapbox.mapboxsdk.annotations; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; + +class Bubble extends Drawable { + + private RectF rect; + private float arrowWidth; + private float arrowHeight; + private float arrowPosition; + private float cornersRadius; + private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + private float strokeWidth; + private Paint strokePaint; + private Path strokePath; + private Path path = new Path(); + + Bubble(RectF rect, ArrowDirection arrowDirection, float arrowWidth, float arrowHeight, float arrowPosition, + float cornersRadius, int bubbleColor, float strokeWidth, int strokeColor) { + this.rect = rect; + this.arrowWidth = arrowWidth; + this.arrowHeight = arrowHeight; + this.arrowPosition = arrowPosition; + this.cornersRadius = cornersRadius; + paint.setColor(bubbleColor); + this.strokeWidth = strokeWidth; + + if (strokeWidth > 0) { + strokePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + strokePaint.setColor(strokeColor); + strokePath = new Path(); + initPath(arrowDirection, path, strokeWidth); + initPath(arrowDirection, strokePath, 0); + } else { + initPath(arrowDirection, path, 0); + } + } + + @Override + protected void onBoundsChange(Rect bounds) { + super.onBoundsChange(bounds); + } + + @Override + public void draw(Canvas canvas) { + if (strokeWidth > 0) { + canvas.drawPath(strokePath, strokePaint); + } + canvas.drawPath(path, paint); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + @Override + public void setAlpha(int alpha) { + paint.setAlpha(alpha); + } + + @Override + public void setColorFilter(ColorFilter cf) { + paint.setColorFilter(cf); + } + + @Override + public int getIntrinsicWidth() { + return (int) rect.width(); + } + + @Override + public int getIntrinsicHeight() { + return (int) rect.height(); + } + + private void initPath(ArrowDirection arrowDirection, Path path, float strokeWidth) { + switch (arrowDirection.getValue()) { + case ArrowDirection.LEFT: + if (cornersRadius <= 0) { + initLeftSquarePath(rect, path, strokeWidth); + break; + } + + if (strokeWidth > 0 && strokeWidth > cornersRadius) { + initLeftSquarePath(rect, path, strokeWidth); + break; + } + + initLeftRoundedPath(rect, path, strokeWidth); + break; + case ArrowDirection.TOP: + if (cornersRadius <= 0) { + initTopSquarePath(rect, path, strokeWidth); + break; + } + + if (strokeWidth > 0 && strokeWidth > cornersRadius) { + initTopSquarePath(rect, path, strokeWidth); + break; + } + + initTopRoundedPath(rect, path, strokeWidth); + break; + case ArrowDirection.RIGHT: + if (cornersRadius <= 0) { + initRightSquarePath(rect, path, strokeWidth); + break; + } + + if (strokeWidth > 0 && strokeWidth > cornersRadius) { + initRightSquarePath(rect, path, strokeWidth); + break; + } + + initRightRoundedPath(rect, path, strokeWidth); + break; + case ArrowDirection.BOTTOM: + if (cornersRadius <= 0) { + initBottomSquarePath(rect, path, strokeWidth); + break; + } + + if (strokeWidth > 0 && strokeWidth > cornersRadius) { + initBottomSquarePath(rect, path, strokeWidth); + break; + } + + initBottomRoundedPath(rect, path, strokeWidth); + break; + } + } + + private void initLeftSquarePath(RectF rect, Path path, float strokeWidth) { + path.moveTo(arrowWidth + rect.left + strokeWidth, rect.top + strokeWidth); + path.lineTo(rect.width() - strokeWidth, rect.top + strokeWidth); + + path.lineTo(rect.right - strokeWidth, rect.bottom - strokeWidth); + + path.lineTo(rect.left + arrowWidth + strokeWidth, rect.bottom - strokeWidth); + + path.lineTo(rect.left + arrowWidth + strokeWidth, arrowHeight + arrowPosition - (strokeWidth / 2)); + path.lineTo(rect.left + strokeWidth + strokeWidth, arrowPosition + arrowHeight / 2); + path.lineTo(rect.left + arrowWidth + strokeWidth, arrowPosition + (strokeWidth / 2)); + + path.lineTo(rect.left + arrowWidth + strokeWidth, rect.top + strokeWidth); + + path.close(); + } + + private void initLeftRoundedPath(RectF rect, Path path, float strokeWidth) { + path.moveTo(arrowWidth + rect.left + cornersRadius + strokeWidth, rect.top + strokeWidth); + path.lineTo(rect.width() - cornersRadius - strokeWidth, rect.top + strokeWidth); + path.arcTo(new RectF(rect.right - cornersRadius, rect.top + strokeWidth, rect.right - strokeWidth, + cornersRadius + rect.top), 270, 90); + + path.lineTo(rect.right - strokeWidth, rect.bottom - cornersRadius - strokeWidth); + path.arcTo(new RectF(rect.right - cornersRadius, rect.bottom - cornersRadius, + rect.right - strokeWidth, rect.bottom - strokeWidth), 0, 90); + + path.lineTo(rect.left + arrowWidth + cornersRadius + strokeWidth, rect.bottom - strokeWidth); + + path.arcTo(new RectF(rect.left + arrowWidth + strokeWidth, rect.bottom - cornersRadius, + cornersRadius + rect.left + arrowWidth, rect.bottom - strokeWidth), 90, 90); + + path.lineTo(rect.left + arrowWidth + strokeWidth, arrowHeight + arrowPosition - (strokeWidth / 2)); + + path.lineTo(rect.left + strokeWidth + strokeWidth, arrowPosition + arrowHeight / 2); + + path.lineTo(rect.left + arrowWidth + strokeWidth, arrowPosition + (strokeWidth / 2)); + + path.lineTo(rect.left + arrowWidth + strokeWidth, rect.top + cornersRadius + strokeWidth); + + path.arcTo(new RectF(rect.left + arrowWidth + strokeWidth, rect.top + strokeWidth, cornersRadius + + rect.left + arrowWidth, cornersRadius + rect.top), 180, 90); + + path.close(); + } + + private void initTopSquarePath(RectF rect, Path path, float strokeWidth) { + path.moveTo(rect.left + arrowPosition + strokeWidth, rect.top + arrowHeight + strokeWidth); + + path.lineTo(rect.left + arrowPosition + (strokeWidth / 2), rect.top + arrowHeight + strokeWidth); + path.lineTo(rect.left + arrowWidth / 2 + arrowPosition, rect.top + strokeWidth + strokeWidth); + path.lineTo(rect.left + arrowWidth + arrowPosition - (strokeWidth / 2), rect.top + arrowHeight + strokeWidth); + path.lineTo(rect.right - strokeWidth, rect.top + arrowHeight + strokeWidth); + + path.lineTo(rect.right - strokeWidth, rect.bottom - strokeWidth); + + path.lineTo(rect.left + strokeWidth, rect.bottom - strokeWidth); + + path.lineTo(rect.left + strokeWidth, rect.top + arrowHeight + strokeWidth); + + path.lineTo(rect.left + arrowPosition + strokeWidth, rect.top + arrowHeight + strokeWidth); + + path.close(); + } + + private void initTopRoundedPath(RectF rect, Path path, float strokeWidth) { + path.moveTo(rect.left + Math.min(arrowPosition, cornersRadius) + strokeWidth, rect.top + arrowHeight + + strokeWidth); + path.lineTo(rect.left + arrowPosition + (strokeWidth / 2), rect.top + arrowHeight + strokeWidth); + path.lineTo(rect.left + arrowWidth / 2 + arrowPosition, rect.top + strokeWidth + strokeWidth); + path.lineTo(rect.left + arrowWidth + arrowPosition - (strokeWidth / 2), rect.top + arrowHeight + strokeWidth); + path.lineTo(rect.right - cornersRadius - strokeWidth, rect.top + arrowHeight + strokeWidth); + + path.arcTo(new RectF(rect.right - cornersRadius, + rect.top + arrowHeight + strokeWidth, rect.right - strokeWidth, cornersRadius + rect.top + arrowHeight), + 270, 90); + path.lineTo(rect.right - strokeWidth, rect.bottom - cornersRadius - strokeWidth); + + path.arcTo(new RectF(rect.right - cornersRadius, rect.bottom - cornersRadius, + rect.right - strokeWidth, rect.bottom - strokeWidth), 0, 90); + path.lineTo(rect.left + cornersRadius + strokeWidth, rect.bottom - strokeWidth); + + path.arcTo(new RectF(rect.left + strokeWidth, rect.bottom - cornersRadius, + cornersRadius + rect.left, rect.bottom - strokeWidth), 90, 90); + + path.lineTo(rect.left + strokeWidth, rect.top + arrowHeight + cornersRadius + strokeWidth); + + path.arcTo(new RectF(rect.left + strokeWidth, rect.top + arrowHeight + strokeWidth, cornersRadius + + rect.left, cornersRadius + rect.top + arrowHeight), 180, 90); + + path.close(); + } + + private void initRightSquarePath(RectF rect, Path path, float strokeWidth) { + path.moveTo(rect.left + strokeWidth, rect.top + strokeWidth); + path.lineTo(rect.width() - arrowWidth - strokeWidth, rect.top + strokeWidth); + + path.lineTo(rect.right - arrowWidth - strokeWidth, arrowPosition + (strokeWidth / 2)); + path.lineTo(rect.right - strokeWidth - strokeWidth, arrowPosition + arrowHeight / 2); + path.lineTo(rect.right - arrowWidth - strokeWidth, arrowPosition + arrowHeight - (strokeWidth / 2)); + + path.lineTo(rect.right - arrowWidth - strokeWidth, rect.bottom - strokeWidth); + + path.lineTo(rect.left + strokeWidth, rect.bottom - strokeWidth); + path.lineTo(rect.left + strokeWidth, rect.top + strokeWidth); + + path.close(); + } + + private void initRightRoundedPath(RectF rect, Path path, float strokeWidth) { + path.moveTo(rect.left + cornersRadius + strokeWidth, rect.top + strokeWidth); + path.lineTo(rect.width() - cornersRadius - arrowWidth - strokeWidth, rect.top + strokeWidth); + path.arcTo(new RectF(rect.right - cornersRadius - arrowWidth, + rect.top + strokeWidth, rect.right - arrowWidth - strokeWidth, cornersRadius + rect.top), 270, 90); + + path.lineTo(rect.right - arrowWidth - strokeWidth, arrowPosition + (strokeWidth / 2)); + path.lineTo(rect.right - strokeWidth - strokeWidth, arrowPosition + arrowHeight / 2); + path.lineTo(rect.right - arrowWidth - strokeWidth, arrowPosition + arrowHeight - (strokeWidth / 2)); + path.lineTo(rect.right - arrowWidth - strokeWidth, rect.bottom - cornersRadius - strokeWidth); + + path.arcTo(new RectF(rect.right - cornersRadius - arrowWidth, rect.bottom - cornersRadius, + rect.right - arrowWidth - strokeWidth, rect.bottom - strokeWidth), 0, 90); + path.lineTo(rect.left + arrowWidth + strokeWidth, rect.bottom - strokeWidth); + + path.arcTo(new RectF(rect.left + strokeWidth, rect.bottom - cornersRadius, + cornersRadius + rect.left, rect.bottom - strokeWidth), 90, 90); + + path.arcTo(new RectF(rect.left + strokeWidth, rect.top + strokeWidth, cornersRadius + + rect.left, cornersRadius + rect.top), 180, 90); + path.close(); + } + + private void initBottomSquarePath(RectF rect, Path path, float strokeWidth) { + path.moveTo(rect.left + strokeWidth, rect.top + strokeWidth); + path.lineTo(rect.right - strokeWidth, rect.top + strokeWidth); + path.lineTo(rect.right - strokeWidth, rect.bottom - arrowHeight - strokeWidth); + + path.lineTo(rect.left + arrowWidth + arrowPosition - (strokeWidth / 2), rect.bottom - arrowHeight - strokeWidth); + path.lineTo(rect.left + arrowPosition + arrowWidth / 2, rect.bottom - strokeWidth - strokeWidth); + path.lineTo(rect.left + arrowPosition + (strokeWidth / 2), rect.bottom - arrowHeight - strokeWidth); + path.lineTo(rect.left + arrowPosition + strokeWidth, rect.bottom - arrowHeight - strokeWidth); + + path.lineTo(rect.left + strokeWidth, rect.bottom - arrowHeight - strokeWidth); + path.lineTo(rect.left + strokeWidth, rect.top + strokeWidth); + path.close(); + } + + private void initBottomRoundedPath(RectF rect, Path path, float strokeWidth) { + path.moveTo(rect.left + cornersRadius + strokeWidth, rect.top + strokeWidth); + path.lineTo(rect.width() - cornersRadius - strokeWidth, rect.top + strokeWidth); + path.arcTo(new RectF(rect.right - cornersRadius, + rect.top + strokeWidth, rect.right - strokeWidth, cornersRadius + rect.top), 270, 90); + + path.lineTo(rect.right - strokeWidth, rect.bottom - arrowHeight - cornersRadius - strokeWidth); + path.arcTo(new RectF(rect.right - cornersRadius, rect.bottom - cornersRadius - arrowHeight, + rect.right - strokeWidth, rect.bottom - arrowHeight - strokeWidth), 0, 90); + + path.lineTo(rect.left + arrowWidth + arrowPosition - (strokeWidth / 2), rect.bottom - arrowHeight - strokeWidth); + path.lineTo(rect.left + arrowPosition + arrowWidth / 2, rect.bottom - strokeWidth - strokeWidth); + path.lineTo(rect.left + arrowPosition + (strokeWidth / 2), rect.bottom - arrowHeight - strokeWidth); + path.lineTo(rect.left + Math.min(cornersRadius, arrowPosition) + strokeWidth, rect.bottom - arrowHeight + - strokeWidth); + + path.arcTo(new RectF(rect.left + strokeWidth, rect.bottom - cornersRadius - arrowHeight, + cornersRadius + rect.left, rect.bottom - arrowHeight - strokeWidth), 90, 90); + path.lineTo(rect.left + strokeWidth, rect.top + cornersRadius + strokeWidth); + path.arcTo(new RectF(rect.left + strokeWidth, rect.top + strokeWidth, cornersRadius + + rect.left, cornersRadius + rect.top), 180, 90); + path.close(); + } +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BubbleLayout.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BubbleLayout.java new file mode 100644 index 0000000000..2e6445170f --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BubbleLayout.java @@ -0,0 +1,231 @@ +package com.mapbox.mapboxsdk.annotations; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.widget.LinearLayout; + +import com.mapbox.mapboxsdk.R; + +/** + * Bubble View for Android with custom stroke width and color, arrow size, position and direction. + */ +class BubbleLayout extends LinearLayout { + + public static final float DEFAULT_STROKE_WIDTH = -1; + private ArrowDirection arrowDirection; + private float arrowWidth; + private float arrowHeight; + private float arrowPosition; + private float cornersRadius; + private Bubble bubble; + private int bubbleColor; + private float strokeWidth; + private int strokeColor; + + public BubbleLayout(Context context) { + this(context, null, 0); + } + + public BubbleLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public BubbleLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.mapbox_BubbleLayout); + @ArrowDirection.Value + int location = a.getInt(R.styleable.mapbox_BubbleLayout_mapbox_bl_arrowDirection, + ArrowDirection.LEFT); + arrowDirection = new ArrowDirection(location); + arrowWidth = a.getDimension(R.styleable.mapbox_BubbleLayout_mapbox_bl_arrowWidth, + convertDpToPixel(8, context)); + arrowHeight = a.getDimension(R.styleable.mapbox_BubbleLayout_mapbox_bl_arrowHeight, + convertDpToPixel(8, context)); + arrowPosition = a.getDimension(R.styleable.mapbox_BubbleLayout_mapbox_bl_arrowPosition, + convertDpToPixel(12, context)); + cornersRadius = a.getDimension(R.styleable.mapbox_BubbleLayout_mapbox_bl_cornersRadius, 0); + bubbleColor = a.getColor(R.styleable.mapbox_BubbleLayout_mapbox_bl_bubbleColor, Color.WHITE); + strokeWidth = + a.getDimension(R.styleable.mapbox_BubbleLayout_mapbox_bl_strokeWidth, DEFAULT_STROKE_WIDTH); + strokeColor = a.getColor(R.styleable.mapbox_BubbleLayout_mapbox_bl_strokeColor, Color.GRAY); + + a.recycle(); + initPadding(); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + initDrawable(0, getWidth(), 0, getHeight()); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + if (bubble != null) { + bubble.draw(canvas); + } + super.dispatchDraw(canvas); + } + + static float convertDpToPixel(float dp, Context context) { + DisplayMetrics metrics = context.getResources().getDisplayMetrics(); + return dp * (metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT); + } + + public ArrowDirection getArrowDirection() { + return arrowDirection; + } + + public BubbleLayout setArrowDirection(ArrowDirection arrowDirection) { + resetPadding(); + this.arrowDirection = arrowDirection; + initPadding(); + return this; + } + + public float getArrowWidth() { + return arrowWidth; + } + + public BubbleLayout setArrowWidth(float arrowWidth) { + resetPadding(); + this.arrowWidth = arrowWidth; + initPadding(); + return this; + } + + public float getArrowHeight() { + return arrowHeight; + } + + public BubbleLayout setArrowHeight(float arrowHeight) { + resetPadding(); + this.arrowHeight = arrowHeight; + initPadding(); + return this; + } + + public float getArrowPosition() { + return arrowPosition; + } + + public BubbleLayout setArrowPosition(float arrowPosition) { + resetPadding(); + this.arrowPosition = arrowPosition; + initPadding(); + return this; + } + + public float getCornersRadius() { + return cornersRadius; + } + + public BubbleLayout setCornersRadius(float cornersRadius) { + this.cornersRadius = cornersRadius; + requestLayout(); + return this; + } + + public int getBubbleColor() { + return bubbleColor; + } + + public BubbleLayout setBubbleColor(int bubbleColor) { + this.bubbleColor = bubbleColor; + requestLayout(); + return this; + } + + public float getStrokeWidth() { + return strokeWidth; + } + + public BubbleLayout setStrokeWidth(float strokeWidth) { + resetPadding(); + this.strokeWidth = strokeWidth; + initPadding(); + return this; + } + + public int getStrokeColor() { + return strokeColor; + } + + public BubbleLayout setStrokeColor(int strokeColor) { + this.strokeColor = strokeColor; + requestLayout(); + return this; + } + + private void initPadding() { + int paddingLeft = getPaddingLeft(); + int paddingRight = getPaddingRight(); + int paddingTop = getPaddingTop(); + int paddingBottom = getPaddingBottom(); + switch (arrowDirection.getValue()) { + case ArrowDirection.LEFT: + paddingLeft += arrowWidth; + break; + case ArrowDirection.RIGHT: + paddingRight += arrowWidth; + break; + case ArrowDirection.TOP: + paddingTop += arrowHeight; + break; + case ArrowDirection.BOTTOM: + paddingBottom += arrowHeight; + break; + } + if (strokeWidth > 0) { + paddingLeft += strokeWidth; + paddingRight += strokeWidth; + paddingTop += strokeWidth; + paddingBottom += strokeWidth; + } + setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom); + } + + private void initDrawable(int left, int right, int top, int bottom) { + if (right < left || bottom < top) { + return; + } + + RectF rectF = new RectF(left, top, right, bottom); + bubble = new Bubble(rectF, arrowDirection, arrowWidth, arrowHeight, arrowPosition, cornersRadius, + bubbleColor, strokeWidth, strokeColor); + } + + private void resetPadding() { + int paddingLeft = getPaddingLeft(); + int paddingRight = getPaddingRight(); + int paddingTop = getPaddingTop(); + int paddingBottom = getPaddingBottom(); + switch (arrowDirection.getValue()) { + case ArrowDirection.LEFT: + paddingLeft -= arrowWidth; + break; + case ArrowDirection.RIGHT: + paddingRight -= arrowWidth; + break; + case ArrowDirection.TOP: + paddingTop -= arrowHeight; + break; + case ArrowDirection.BOTTOM: + paddingBottom -= arrowHeight; + break; + } + if (strokeWidth > 0) { + paddingLeft -= strokeWidth; + paddingRight -= strokeWidth; + paddingTop -= strokeWidth; + paddingBottom -= strokeWidth; + } + setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom); + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BubblePopupHelper.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BubblePopupHelper.java new file mode 100644 index 0000000000..215445abaa --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BubblePopupHelper.java @@ -0,0 +1,33 @@ +package com.mapbox.mapboxsdk.annotations; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.support.annotation.NonNull; +import android.view.ViewGroup; +import android.widget.PopupWindow; + +import com.mapbox.mapboxsdk.R; + +class BubblePopupHelper { + + static PopupWindow create(@NonNull Context context, @NonNull BubbleLayout bubbleLayout) { + PopupWindow popupWindow = new PopupWindow(context); + + popupWindow.setContentView(bubbleLayout); + popupWindow.setOutsideTouchable(true); + popupWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT); + popupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT); + popupWindow.setAnimationStyle(android.R.style.Animation_Dialog); + // change background color to transparent + Drawable drawable; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + drawable = context.getDrawable(R.drawable.mapbox_popup_window_transparent); + } else { + drawable = context.getResources().getDrawable(R.drawable.mapbox_popup_window_transparent); + } + popupWindow.setBackgroundDrawable(drawable); + + return popupWindow; + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java index 052d5592e4..57aa512401 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java @@ -3,8 +3,6 @@ package com.mapbox.mapboxsdk.annotations; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Build; @@ -23,7 +21,10 @@ import java.io.IOException; import java.io.InputStream; /** - * Factory for creating {@link Icon} objects. + * Factory for creating Icons from bitmap images. + * <p> + * {@link Icon} is used to display bitmaps on top of the map using {@link Marker} and {@link MarkerView}. + * </p> * * @see Icon */ @@ -33,23 +34,23 @@ public final class IconFactory { public static final Bitmap ICON_MARKERVIEW_BITMAP = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8); public static final String ICON_MARKERVIEW_ID = ICON_ID_PREFIX + "marker_view"; - private Context mContext; - private static IconFactory sInstance; - private Icon mDefaultMarker; - private Icon mDefaultMarkerView; - private BitmapFactory.Options mOptions; + private Context context; + private static IconFactory instance; + private Icon defaultMarker; + private Icon defaultMarkerView; + private BitmapFactory.Options options; - private int mNextId = 0; + private int nextId = 0; public static synchronized IconFactory getInstance(@NonNull Context context) { - if (sInstance == null) { - sInstance = new IconFactory(context.getApplicationContext()); + if (instance == null) { + instance = new IconFactory(context.getApplicationContext()); } - return sInstance; + return instance; } private IconFactory(@NonNull Context context) { - mContext = context; + this.context = context; DisplayMetrics realMetrics = null; DisplayMetrics metrics = new DisplayMetrics(); WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); @@ -60,12 +61,12 @@ public final class IconFactory { } wm.getDefaultDisplay().getMetrics(metrics); - mOptions = new BitmapFactory.Options(); - mOptions.inScaled = true; - mOptions.inDensity = DisplayMetrics.DENSITY_DEFAULT; - mOptions.inTargetDensity = metrics.densityDpi; + options = new BitmapFactory.Options(); + options.inScaled = true; + options.inDensity = DisplayMetrics.DENSITY_DEFAULT; + options.inTargetDensity = metrics.densityDpi; if (realMetrics != null) { - mOptions.inScreenDensity = realMetrics.densityDpi; + options.inScreenDensity = realMetrics.densityDpi; } } @@ -76,73 +77,27 @@ public final class IconFactory { * @return The {@link Icon} using the given Bitmap image. */ public Icon fromBitmap(@NonNull Bitmap bitmap) { - if (mNextId < 0) { + if (nextId < 0) { throw new TooManyIconsException(); } - String id = ICON_ID_PREFIX + ++mNextId; + String id = ICON_ID_PREFIX + ++nextId; return new Icon(id, bitmap); } /** - * Create an {@link Icon} from a given {@link Drawable}. - * - * @param drawable A {@link Drawable} object used for creating the {@link Icon}. - * @return {@link Icon} with the provided {@link Drawable}. - */ - public Icon fromDrawable(@NonNull Drawable drawable) { - int width = drawable.getIntrinsicWidth(); - int height = drawable.getIntrinsicHeight(); - return fromDrawable(drawable, width, height); - } - - /** - * Create an {@link Icon} from a given {@link Drawable}. - * - * @param drawable A {@link Drawable} object used for creating the {@link Icon}. - * @param width An integer greater then zero defining the {@link Icon} width. - * @param height An integer greater then zero defining the {@link Icon} height. - * @return {@link Icon} with the provided {@link Drawable}. - */ - public Icon fromDrawable(@NonNull Drawable drawable, int width, int height) { - if ((width < 0) || (height < 0)) { - return null; - } - - Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - Rect temp = drawable.getBounds(); - Rect bounds = new Rect(0, 0, width, height); - drawable.setBounds(bounds); - drawable.draw(canvas); - drawable.setBounds(temp); - return fromBitmap(bitmap); - } - - /** * Create an {@link Icon} using the resource ID of a Bitmap image. * * @param resourceId The resource ID of a Bitmap image. * @return The {@link Icon} that was loaded from the asset or {@code null} if failed to load. */ public Icon fromResource(@DrawableRes int resourceId) { - Drawable drawable = ContextCompat.getDrawable(mContext, resourceId); - Bitmap bitmap; + Drawable drawable = ContextCompat.getDrawable(context, resourceId); if (drawable instanceof BitmapDrawable) { BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; - bitmap = bitmapDrawable.getBitmap(); + return fromBitmap(bitmapDrawable.getBitmap()); } else { - if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { - bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); - } else { - bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), - Bitmap.Config.ARGB_8888); - } - - Canvas canvas = new Canvas(bitmap); - drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); - drawable.draw(canvas); + throw new IllegalArgumentException("Failed to decode image. The resource provided must be a Bitmap."); } - return fromBitmap(bitmap); } /** @@ -151,10 +106,10 @@ public final class IconFactory { * @return An {@link Icon} with the default {@link Marker} icon. */ public Icon defaultMarker() { - if (mDefaultMarker == null) { - mDefaultMarker = fromResource(R.drawable.mapbox_marker_icon_default); + if (defaultMarker == null) { + defaultMarker = fromResource(R.drawable.mapbox_marker_icon_default); } - return mDefaultMarker; + return defaultMarker; } /** @@ -163,14 +118,14 @@ public final class IconFactory { * @return An {@link Icon} with the default {@link MarkerView} icon. */ public Icon defaultMarkerView() { - if (mDefaultMarkerView == null) { - mDefaultMarkerView = fromResource(R.drawable.mapbox_markerview_icon_default); + if (defaultMarkerView == null) { + defaultMarkerView = fromResource(R.drawable.mapbox_markerview_icon_default); } - return mDefaultMarkerView; + return defaultMarkerView; } private Icon fromInputStream(@NonNull InputStream is) { - Bitmap bitmap = BitmapFactory.decodeStream(is, null, mOptions); + Bitmap bitmap = BitmapFactory.decodeStream(is, null, options); return fromBitmap(bitmap); } @@ -183,7 +138,7 @@ public final class IconFactory { public Icon fromAsset(@NonNull String assetName) { InputStream is; try { - is = mContext.getAssets().open(assetName); + is = context.getAssets().open(assetName); } catch (IOException ioException) { return null; } @@ -198,7 +153,7 @@ public final class IconFactory { * load. */ public Icon fromPath(@NonNull String absolutePath) { - Bitmap bitmap = BitmapFactory.decodeFile(absolutePath, mOptions); + Bitmap bitmap = BitmapFactory.decodeFile(absolutePath, options); return fromBitmap(bitmap); } @@ -209,12 +164,12 @@ public final class IconFactory { * @param fileName The name of the Bitmap image file. * @return The {@link Icon} that was loaded from the asset or {@code null} if failed to load. * @see <a href="https://developer.android.com/guide/topics/data/data-storage.html#filesInternal"> - * Using the Internal Storage</a> + * Using the Internal Storage</a> */ public Icon fromFile(@NonNull String fileName) { FileInputStream is; try { - is = mContext.openFileInput(fileName); + is = context.openFileInput(fileName); } catch (FileNotFoundException fileNotFoundException) { return null; } @@ -232,4 +187,5 @@ public final class IconFactory { public static Icon recreate(@NonNull String iconId, @NonNull Bitmap bitmap) { return new Icon(iconId, bitmap); } + } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java index 4e080d98f6..cf42bfe738 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java @@ -133,7 +133,7 @@ public class InfoWindow { float x = coordinates.x - (view.getMeasuredWidth() / 2) + offsetX; float y = coordinates.y - view.getMeasuredHeight() + offsetY; - if (view instanceof InfoWindowView) { + if (view instanceof BubbleLayout) { // only apply repositioning/margin for InfoWindowView Resources resources = mapView.getContext().getResources(); @@ -187,8 +187,7 @@ public class InfoWindow { } // Adjust tipView - InfoWindowView infoWindowView = (InfoWindowView) view; - infoWindowView.setTipViewMarginLeft((int) tipViewMarginLeft); + ((BubbleLayout) view).setArrowPosition(tipViewMarginLeft); } // set anchor popupwindowview @@ -198,7 +197,7 @@ public class InfoWindow { // Calculate x-offset for update method viewWidthOffset = x - coordinates.x - offsetX; - close(); //if it was already opened + close(); // if it was already opened mapView.addView(view, lp); isVisible = true; } @@ -284,7 +283,7 @@ public class InfoWindow { if (mapboxMap != null && marker != null && view != null) { coordinates = mapboxMap.getProjection().toScreenLocation(marker.getPosition()); - if (view instanceof InfoWindowView) { + if (view instanceof BubbleLayout) { view.setX(coordinates.x + viewWidthOffset - markerWidthOffset); } else { view.setX(coordinates.x - (view.getMeasuredWidth() / 2) - markerWidthOffset); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindowTipView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindowTipView.java deleted file mode 100644 index abcebfec83..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindowTipView.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Path; -import android.util.AttributeSet; -import android.view.View; - -import com.mapbox.mapboxsdk.R; - -final class InfoWindowTipView extends View { - - private Paint mPaint; - private Path mPath; - private int mLineWidth; - - public InfoWindowTipView(Context context, AttributeSet attrs) { - super(context, attrs); - - mPath = new Path(); - mLineWidth = (int) context.getResources().getDimension(R.dimen.mapbox_infowindow_line_width); - mPaint = new Paint(); - mPaint.setColor(Color.WHITE); - mPaint.setAntiAlias(true); - mPaint.setStrokeWidth(0.0f); - mPaint.setStyle(Paint.Style.FILL); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - int height = getMeasuredHeight(); - int width = getMeasuredWidth(); - - mPath.rewind(); - - this.mPaint.setColor(Color.WHITE); - this.mPaint.setAntiAlias(true); - this.mPaint.setStrokeWidth(0.0f); - this.mPaint.setStyle(Paint.Style.FILL); - - mPath.moveTo(0, 0); - mPath.lineTo(width, 0); - mPath.lineTo((width / 2), height); - mPath.lineTo(0, 0); - canvas.drawPath(mPath, this.mPaint); - - mPath.rewind(); - - this.mPaint.setColor(Color.parseColor("#C2C2C2")); - this.mPaint.setAntiAlias(true); - this.mPaint.setStrokeWidth(mLineWidth); - this.mPaint.setStyle(Paint.Style.STROKE); - - mPath.moveTo(0, 0); - mPath.lineTo(width / 2, height); - mPath.lineTo(width, 0); - canvas.drawPath(mPath, this.mPaint); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindowView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindowView.java deleted file mode 100644 index d1a59aae4e..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindowView.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.widget.RelativeLayout; - -import com.mapbox.mapboxsdk.R; - -class InfoWindowView extends RelativeLayout { - - private InfoWindowTipView mTipView; - - public InfoWindowView(Context context) { - this(context, null); - } - - public InfoWindowView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public InfoWindowView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - initialize(context); - } - - private void initialize(Context context) { - LayoutInflater.from(context).inflate(R.layout.mapbox_infowindow_content, this); - mTipView = (InfoWindowTipView) findViewById(R.id.infowindow_tipview); - } - - void setTipViewMarginLeft(int marginLeft) { - RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) mTipView.getLayoutParams(); - layoutParams.leftMargin = marginLeft; - // This is a bit of a hack but prevents an occasional gap between the InfoWindow - layoutParams.topMargin = (int) getResources().getDimension(R.dimen.mapbox_infowindow_offset); - } -} 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 edf118205b..18f74cd990 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 @@ -30,6 +30,8 @@ public class Marker extends Annotation { private LatLng position; private String snippet; private Icon icon; + //Redundantly stored for JNI access + private String iconId; private String title; private InfoWindow infoWindow; @@ -51,24 +53,19 @@ public class Marker extends Annotation { * @param baseMarkerOptions The builder used to construct the Marker. */ public Marker(BaseMarkerOptions baseMarkerOptions) { - position = baseMarkerOptions.position; - snippet = baseMarkerOptions.snippet; - icon = baseMarkerOptions.icon; - title = baseMarkerOptions.title; + this(baseMarkerOptions.position, baseMarkerOptions.icon, baseMarkerOptions.title, baseMarkerOptions.snippet); } Marker(BaseMarkerViewOptions baseMarkerViewOptions) { - position = baseMarkerViewOptions.position; - snippet = baseMarkerViewOptions.snippet; - icon = baseMarkerViewOptions.icon; - title = baseMarkerViewOptions.title; + this(baseMarkerViewOptions.position, baseMarkerViewOptions.icon, + baseMarkerViewOptions.title, baseMarkerViewOptions.snippet); } Marker(LatLng position, Icon icon, String title, String snippet) { this.position = position; - this.icon = icon; this.title = title; this.snippet = snippet; + setIcon(icon); } /** @@ -148,6 +145,7 @@ public class Marker extends Annotation { */ public void setIcon(@Nullable Icon icon) { this.icon = icon; + this.iconId = icon != null ? icon.getId() : null; MapboxMap map = getMapboxMap(); if (map != null) { map.updateMarker(this); @@ -240,7 +238,7 @@ public class Marker extends Annotation { private InfoWindow getInfoWindow(@NonNull MapView mapView) { if (infoWindow == null && mapView.getContext() != null) { - infoWindow = new InfoWindow(mapView, R.layout.mapbox_infowindow_view, getMapboxMap()); + infoWindow = new InfoWindow(mapView, R.layout.mapbox_infowindow_content, getMapboxMap()); } return infoWindow; } 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 c1b643eb4c..220d3322cb 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 @@ -266,18 +266,9 @@ public class MarkerView extends Marker { * @param rotation the rotation value to animate to. */ public void setRotation(float rotation) { - // limit to 0 - 360 degrees - float newRotation = rotation; - while (newRotation > 360) { - newRotation -= 360; - } - while (newRotation < 0) { - newRotation += 360; - } - - this.rotation = newRotation; + this.rotation = rotation; if (markerViewManager != null) { - markerViewManager.animateRotationBy(this, newRotation); + markerViewManager.setRotation(this, rotation); } } @@ -342,6 +333,7 @@ public class MarkerView extends Marker { public void setPosition(LatLng position) { super.setPosition(position); if (markerViewManager != null) { + markerViewManager.setWaitingForRenderInvoke(true); markerViewManager.update(); } } 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 ad221691b4..17a1866379 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 @@ -125,6 +125,14 @@ public class MarkerViewManager implements MapView.OnMapChangedListener { } } + public void setRotation(@NonNull MarkerView marker, float rotation) { + View convertView = markerViewMap.get(marker); + if (convertView != null) { + convertView.animate().cancel(); + convertView.setRotation(rotation); + } + } + /** * Animate a MarkerView to a given alpha value. * <p> @@ -466,7 +474,6 @@ public class MarkerViewManager implements MapView.OnMapChangedListener { if (adapter.getMarkerClass().equals(marker.getClass())) { adapter.prepareViewForReuse(marker, convertView); adapter.releaseView(convertView); - marker.setMapboxMap(null); iterator.remove(); } } @@ -573,7 +580,7 @@ public class MarkerViewManager implements MapView.OnMapChangedListener { if (view != null) { if (marker.getWidth() == 0) { if (view.getMeasuredWidth() == 0) { - //Ensure the marker's view is measured first + // Ensure the marker's view is measured first view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); } marker.setWidth(view.getMeasuredWidth()); |