Reputation: 89
I wanted to add an animation as the "current marker selected" in my map as shown below:
The animation is a LottieAnimation resource (extends from ImageView).
I had been searching and google maps developer docs said that I can only use Bitmap resources as markers. I tried to convert this one as a bitmap but without success.
Uber or Lyft has some animations as markers or for current position, so how they achieve this?
Or is it possible that they are listening to the map movement and update the location of their marker without using the marker property?
Upvotes: 5
Views: 7279
Reputation: 2487
As far as I know, Google map Marker and InfoWindow is drawn on the map as a flat bitmap. So you cannot do animation, component touch interaction or even draw a shadow around them officially.
Uber app uses a different layer placed on top of the map (You can try to do some gesture on the map and see that the marker move but a bit delay). It's not easy because you have to catch every map's gesture and animate the overlay view accordingly. I tried that before but it's too much work to do so I left it behind.
Your Pulse effect can be achieved using Circle
with ValueAnimator
private @ColorInt int mPulseEffectColor;
private int[] mPulseEffectColorElements;
private ValueAnimator mPulseEffectAnimator;
private Circle mPulseCircle;
private void initPulseEffect() {
mPulseEffectColor = ContextCompat.getColor(mContext, R.color.pink);
mPulseEffectColorElements = new int[] {
Color.red(mPulseEffectColor),
Color.green(mPulseEffectColor),
Color.blue(mPulseEffectColor)
};
mPulseEffectAnimator = ValueAnimator.ofFloat(0, calculatePulseRadius(current_map_zoom_level));
mPulseEffectAnimator.setStartDelay(3000);
mPulseEffectAnimator.setDuration(400);
mPulseEffectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
}
Calculate radius base on map's zoom level
private static float calculatePulseRadius(float zoomLevel) {
return (float) Math.pow(2, 16 - zoomLevel) * 160;
}
Register OnCameraIdleListener
to listen to map's zoom changed
@Override
public void onCameraIdle() {
CameraPosition cameraPosition = mMap.getCameraPosition();
if (mPulseEffectAnimator != null)
mPulseEffectAnimator.setFloatValues(0, calculatePulseRadius(cameraPosition.zoom));
}
Start the animation
private void startPulseAnimation() {
if (mPulseCircle != null)
mPulseCircle.remove();
if (mPulseEffectAnimator != null) {
mPulseEffectAnimator.removeAllUpdateListeners();
mPulseEffectAnimator.removeAllListeners();
mPulseEffectAnimator.end();
}
if (your_marker_coordinate != null) {
mPulseCircle = mMap.addCircle(new CircleOptions()
.center(your_marker_coordinate)
.radius(0).strokeWidth(0)
.fillColor(mPulseEffectColor));
mPulseEffectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
if (mPulseCircle == null)
return;
int alpha = (int) ((1 - valueAnimator.getAnimatedFraction()) * 128);
mPulseCircle.setFillColor(Color.argb(alpha,
mPulseEffectColorElements[0], mPulseEffectColorElements[1], mPulseEffectColorElements[2]));
mPulseCircle.setRadius((float) valueAnimator.getAnimatedValue());
}
});
mPulseEffectAnimator.addListener(new AnimatorListenerAdapter {
@Override
public void onAnimationEnd(Animator animation) {
mPulseEffectAnimator.setStartDelay(2000);
mPulseEffectAnimator.start();
}
});
mPulseEffectAnimator.start();
}
}
Explanation:
calculatePulseRadius
to recalculate the radius base on the map zoom level, because the circle's radius is measured by the real-world distance.int[] mPulseEffectColorElements
to store r,g,b of the circle color so I don't have to recreate the color every frame. I only want to change the alpha valueThis code only applied for 1 circle at a time, multiple circles require harder work but same logic.
Result:
Upvotes: 8