# Animated icon movement

> **Note (legacy): A newer version of the Maps SDK is available**
> 
> This page uses v9.7.1 of the Mapbox Maps SDK. A newer version of the SDK is available. Learn about the latest version, v11.31.0, in the [Maps SDK documentation](https://docs.mapbox.com/android/ja/maps/guides/).

Your browser doesn't support HTML5 video. Open [link to the video](https://docs.mapbox.com/android/ja/android/ja/assets/medias/maps-example-value-animator-icon-animation-activity-83bb8deced66d858ab83d644d30ee517.mp4).

> **Note**
> 
> This example is a part of the [Mapbox Android Demo app](https://github.com/mapbox/mapbox-android-demo). You can find the values for all referenced resources in the [`res`](https://github.com/mapbox/mapbox-android-demo/tree/master/MapboxAndroidDemo/src/main/res) directory. For example, see [`res/values/activity_strings.xml`](https://github.com/mapbox/mapbox-android-demo/tree/master/MapboxAndroidDemo/src/main/res/values/activity_strings.xml) for `R.string.*` references used in this example. The dependencies can be found [here.](https://github.com/mapbox/mapbox-navigation-android-examples/blob/main/app/build.gradle) The examples use View binding. [See setup documention if necessary.](https://developer.android.com/topic/libraries/view-binding)

Title: `activity_animated_pin_drop`

[View on GitHub](https://github.com/mapbox/mapbox-android-demo/tree/master/MapboxAndroidDemo/src/main/res/layout/activity_animated_pin_drop.xml)

```xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:fab="http://schemas.android.com/apk/res-auto"
    xmlns:mapbox="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".examples.labs.ValueAnimatorIconAnimationActivity">


    <com.mapbox.mapboxsdk.maps.MapView
        android:id="@+id/mapView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        mapbox:mapbox_cameraTargetLat="0.282219"
        mapbox:mapbox_cameraTargetLng="117.226205"
        mapbox:mapbox_cameraTilt="55"
        mapbox:mapbox_cameraZoom="4"
        mapbox:mapbox_uiRotateGestures="false" />

    <com.getbase.floatingactionbutton.FloatingActionsMenu
        android:id="@+id/interpolator_selection_fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:layout_margin="16dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginBottom="8dp"
        fab:fab_addButtonColorNormal="@color/mapboxRed"
        fab:fab_addButtonColorPressed="@color/mapboxWhite"
        fab:fab_addButtonPlusIconColor="@color/mapboxWhite"
        fab:fab_labelStyle="@style/menu_labels_style"
        mapbox:layout_constraintBottom_toBottomOf="parent"
        mapbox:layout_constraintEnd_toEndOf="parent">

        <com.getbase.floatingactionbutton.FloatingActionButton
            android:id="@+id/fab_bounce_interpolator"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            fab:fab_colorNormal="@color/mapboxBlue"
            fab:fab_colorPressed="@color/mapboxWhite"
            fab:fab_size="mini"
            fab:fab_title="@string/bounce_interpolator" />

        <com.getbase.floatingactionbutton.FloatingActionButton
            android:id="@+id/fab_linear_interpolator"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            fab:fab_colorNormal="@color/mapboxGreen"
            fab:fab_colorPressed="@color/mapboxWhite"
            fab:fab_size="mini"
            fab:fab_title="@string/linear_interpolator" />

        <com.getbase.floatingactionbutton.FloatingActionButton
            android:id="@+id/fab_accelerate_interpolator"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            fab:fab_colorNormal="@color/mapboxRed"
            fab:fab_colorPressed="@color/mapboxWhite"
            fab:fab_size="mini"
            fab:fab_title="@string/accelerate_interpolator" />

        <com.getbase.floatingactionbutton.FloatingActionButton
            android:id="@+id/fab_decelerate_interpolator"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            fab:fab_colorNormal="@color/mapboxYellow"
            fab:fab_colorPressed="@color/mapboxWhite"
            fab:fab_size="mini"
            fab:fab_title="@string/decelerate_interpolator" />

    </com.getbase.floatingactionbutton.FloatingActionsMenu>


</androidx.constraintlayout.widget.ConstraintLayout>
```

**Java**

Title: `ValueAnimatorIconAnimationActivity.java`

[View on GitHub](https://github.com/mapbox/mapbox-android-demo/tree/master/MapboxAndroidDemo/src/main/java/com/mapbox/mapboxandroiddemo/examples/labs/ValueAnimatorIconAnimationActivity.java)

```java
package com.mapbox.mapboxandroiddemo.examples.labs;

import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.BounceInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.LinearInterpolator;

import com.getbase.floatingactionbutton.FloatingActionButton;
import com.mapbox.geojson.Feature;
import com.mapbox.geojson.FeatureCollection;
import com.mapbox.geojson.Point;
import com.mapbox.mapboxandroiddemo.R;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.utils.BitmapUtils;

import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconIgnorePlacement;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconOffset;

/**
 * Combine SymbolLayer icons with the Android system's ValueAnimator and interpolator
 * animation for a fun pin drop effect. The interpolator movement can also be used with other
 * types of map layers, such as a LineLayer or CircleLayer.
 * <p>
 * More info about https://developer.android.com/reference/android/view/animation/Interpolator
 */
public class ValueAnimatorIconAnimationActivity extends AppCompatActivity implements
  OnMapReadyCallback, MapView.OnDidFinishRenderingMapListener {

  private static final String ICON_ID = "red-pin-icon-id";

  // This float's actual value will depend on the height of the SymbolLayer icon
  private static final float DEFAULT_DESIRED_ICON_OFFSET = -16;
  private static final float STARTING_DROP_HEIGHT = -100;
  private static final long DROP_SPEED_MILLISECONDS = 1200;
  private static final String SYMBOL_LAYER_ID = "symbol-layer-id";
  private MapView mapView;
  private SymbolLayer pinSymbolLayer;
  private Style style;
  private TimeInterpolator currentSelectedTimeInterpolator = new BounceInterpolator();
  private ValueAnimator animator;
  private boolean firstRunThrough = true;
  private boolean animationHasStarted;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Mapbox access token is configured here. This needs to be called either in your application
    // object or in the same activity which contains the mapview.
    Mapbox.getInstance(this, getString(R.string.access_token));

    // This contains the MapView in XML and needs to be called after the access token is configured.
    setContentView(R.layout.activity_animated_pin_drop);

    // Initialize the map view
    mapView = findViewById(R.id.mapView);
    mapView.onCreate(savedInstanceState);
    mapView.getMapAsync(this);
  }

  @Override
  public void onMapReady(@NonNull final MapboxMap mapboxMap) {
    mapboxMap.setStyle(new Style.Builder().fromUri(Style.LIGHT)
        // Add GeoJsonSource with random Features to the map.
        .withSource(new GeoJsonSource("source-id",
          FeatureCollection.fromFeatures(new Feature[] {
            Feature.fromGeometry(Point.fromLngLat(
              119.86083984375,
              -1.834403324493515)),
            Feature.fromGeometry(Point.fromLngLat(
              116.06637239456177,
              5.970619502704659)),
            Feature.fromGeometry(Point.fromLngLat(
              114.58740234375,
              4.54357027937176)),
            Feature.fromGeometry(Point.fromLngLat(
              118.19091796875,
              5.134714634014467)),
            Feature.fromGeometry(Point.fromLngLat(
              110.36865234374999,
              1.4500404973608074)),
            Feature.fromGeometry(Point.fromLngLat(
              109.40185546874999,
              0.3076157096439005)),
            Feature.fromGeometry(Point.fromLngLat(
              115.79589843749999,
              1.5159363834516861)),
            Feature.fromGeometry(Point.fromLngLat(
              113.291015625,
              -0.9667509997666298)),
            Feature.fromGeometry(Point.fromLngLat(
              116.40083312988281,
              -0.3392008994314591))
          })
        ))
        .withImage(ICON_ID, BitmapUtils.getBitmapFromDrawable(
          getResources().getDrawable(R.drawable.map_marker_push_pin_pink))), new Style.OnStyleLoaded() {
            @Override
            public void onStyleLoaded(@NonNull Style style) {
              ValueAnimatorIconAnimationActivity.this.style = style;
              mapView.addOnDidFinishRenderingMapListener(ValueAnimatorIconAnimationActivity.this);
            }
          }
    );
  }

  /**
   * Implementing this interface so that animation only starts once all tiles have been loaded
   *
   * @param fully whether or not the map is finished rendering
   */
  @Override
  public void onDidFinishRenderingMap(boolean fully) {
    initAnimation(currentSelectedTimeInterpolator);
    initInterpolatorButtons();
  }

  /**
   * Initialize and start the animation.
   *
   * @param desiredTimeInterpolator the type of Android system movement to animate the
   *                                SymbolLayer icons with.
   */
  private void initAnimation(TimeInterpolator desiredTimeInterpolator) {
    if (animator != null) {
      animator.cancel();
    }
    animator = ValueAnimator.ofFloat(STARTING_DROP_HEIGHT, -17);
    animator.setDuration(DROP_SPEED_MILLISECONDS);
    animator.setInterpolator(desiredTimeInterpolator);
    animator.setStartDelay(1000);
    animator.start();
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator valueAnimator) {
        if (!animationHasStarted) {
          initSymbolLayer();
          animationHasStarted = true;
        }
        pinSymbolLayer.setProperties(iconOffset(new Float[] {0f, (Float) valueAnimator.getAnimatedValue()}));
      }
    });
  }

  /**
   * Add the SymbolLayer to the map
   */
  private void initSymbolLayer() {
    pinSymbolLayer = new SymbolLayer(SYMBOL_LAYER_ID,
      "source-id");
    pinSymbolLayer.setProperties(
      iconImage(ICON_ID),
      iconIgnorePlacement(true),
      iconAllowOverlap(true),
      iconOffset(new Float[] {0f, DEFAULT_DESIRED_ICON_OFFSET}));
    style.addLayer(pinSymbolLayer);
  }

  /**
   * Initialize the interpolator selection spinner menu
   */
  private void initInterpolatorButtons() {

    FloatingActionButton bounceInterpolatorFab = findViewById(R.id.fab_bounce_interpolator);
    bounceInterpolatorFab.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View view) {
        currentSelectedTimeInterpolator = new BounceInterpolator();
        resetIcons();
      }
    });

    FloatingActionButton linearInterpolatorFab = findViewById(R.id.fab_linear_interpolator);
    linearInterpolatorFab.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View view) {
        currentSelectedTimeInterpolator = new LinearInterpolator();
        firstRunThrough = false;
        resetIcons();
      }
    });

    FloatingActionButton accelerateInterpolatorFab = findViewById(R.id.fab_accelerate_interpolator);
    accelerateInterpolatorFab.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View view) {
        currentSelectedTimeInterpolator = new AccelerateInterpolator();
        firstRunThrough = false;
        resetIcons();
      }
    });

    FloatingActionButton decelerateInterpolatorFab = findViewById(R.id.fab_decelerate_interpolator);
    decelerateInterpolatorFab.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View view) {
        currentSelectedTimeInterpolator = new DecelerateInterpolator();
        firstRunThrough = false;
        resetIcons();
      }
    });
  }

  private void resetIcons() {
    if (!firstRunThrough) {
      animationHasStarted = false;
      style.removeLayer(SYMBOL_LAYER_ID);
      initAnimation(currentSelectedTimeInterpolator);
    }
  }

  @Override
  protected void onStart() {
    super.onStart();
    mapView.onStart();
  }

  @Override
  public void onResume() {
    super.onResume();
    mapView.onResume();
  }

  @Override
  public void onPause() {
    super.onPause();
    mapView.onPause();
  }

  @Override
  protected void onStop() {
    super.onStop();
    if (animator != null) {
      animator.end();
    }
    mapView.onStop();
  }

  @Override
  protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    mapView.onSaveInstanceState(outState);
  }

  @Override
  public void onLowMemory() {
    super.onLowMemory();
    mapView.onLowMemory();
  }

  @Override
  protected void onDestroy() {
    super.onDestroy();
    mapView.onDestroy();
  }
}
```