Tutorials
intermediate
Java

Runtime styling for Android

Prerequisite

An Android application with a map view set up and familiarity with Android Studio and Java.

Runtime styling is a powerful feature within the Mapbox Maps SDK for Android, which enables you to change the map's properties in real time, allowing you to customize every aspect of the map’s appearance down to the smallest detail.

In this tutorial, you'll build a map for Android that changes the fill color of the earth's water and changes several text properties (size, color, halo blur, etc.) of marine labels.

Getting started

This guide assumes that you are familiar with Java and Android Studio. Here are the resources that you’ll need before getting started:

  • A Mapbox account and access token. Sign up for an account at mapbox.com/signup. You can find your access tokens on your Account page. You will add your access token to your strings.xml file.

  • An application including the Mapbox Maps SDK for Android. This guide also assumes that you have already begun building an Android application that uses the Mapbox Maps SDK for Android. If you're new to the Maps SDK for Android, complete the First steps with the Mapbox Maps SDK for Android guide to set up a map view first.

Adding design library

Android Studio uses a toolkit called Gradle to compile resources and source code into an APK. The build.gradle file is used to configure the build and list dependencies, including the Mapbox Maps SDK for Android. In your build.gradle file, add the Android design support library as a dependency. This library isn't required to use runtime styling. It's being added to the app so that a floating action button can be used in this example.

// in addition to the rest of your build.gradle contents
// you should include the following repository and dependency

repositories {
    mavenCentral()
}

dependencies {
    // Add other library dependencies such as the Mapbox Maps SDK for Android
    implementation 'com.android.support:design:27.1.1'
}

The activity_main.xml file is where you'll set the properties for your MapView and where you'll add a button to the layout. Clicking this button will trigger the runtime styling changes. In app > res > layout > activity_main.xml, specify the center of the map view, the zoom level, and the map style used when the application is initialized:

activity_main.xml
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:mapbox="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/runtime_mapview"
android:layout_width="match_parent"
android:layout_height="match_parent"
mapbox:mapbox_cameraTargetLat="19.948045"
mapbox:mapbox_cameraTargetLng="-84.654463"
mapbox:mapbox_cameraZoom="3.371717" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/floatingActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
mapbox:layout_constraintBottom_toBottomOf="parent"
mapbox:layout_constraintEnd_toEndOf="parent" />
</android.support.constraint.ConstraintLayout>

You'll store your access token in the strings.xml file. In app > res > values > strings.xml:

strings.xml
<string name="access_token" translatable="false">YOUR_MAPBOX_ACCESS_TOKEN</string>

MainActivity.java is a Java file where you'll specify Mapbox-specific interactions. In app > java > yourcompany.yourproject > MainActivity.java initialize your map:

MainActivity.java
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
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.Layer;
import com.mapbox.mapboxsdk.style.layers.PropertyFactory;
import android.graphics.Color;
import android.view.View;
public class MainActivity extends AppCompatActivity {
private MapView mapView;
private FloatingActionButton changeMapPropertiesFab;
private Layer waterLayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Mapbox.getInstance(this, getString(R.string.access_token));
setContentView(R.layout.activity_main);
mapView = findViewById(R.id.runtime_mapview);
mapView.onCreate(savedInstanceState);
changeMapPropertiesFab = findViewById(R.id.floatingActionButton);
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(@NonNull final MapboxMap mapboxMap) {
mapboxMap.setStyle(Style.MAPBOX_STREETS, new Style.OnStyleLoaded() {
@Override
public void onStyleLoaded(@NonNull final Style style) {
waterLayer = style.getLayer("water");
changeMapPropertiesFab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (waterLayer != null) {
waterLayer.setProperties(PropertyFactory.fillColor(Color.parseColor("#023689"))
);
}
for (Layer singleMapLayer : style.getLayers()) {
if (singleMapLayer.getId().contains("water-") && !singleMapLayer.getId().equals("water-shadow")) {
singleMapLayer.setProperties(
PropertyFactory.textHaloBlur(10f),
PropertyFactory.textSize(25f),
PropertyFactory.textColor(Color.parseColor("#00FF08")),
PropertyFactory.textOpacity(1f)
);
}
}
}
});
}
});
}
});
}
// Add the mapView's own lifecycle methods to the activity's lifecycle methods
@Override
public void onStart() {
super.onStart();
mapView.onStart();
}
@Override
public void onResume() {
super.onResume();
mapView.onResume();
}
@Override
public void onPause() {
super.onPause();
mapView.onPause();
}
@Override
public void onStop() {
super.onStop();
mapView.onStop();
}
@Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
@Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
}

You can learn how to set up an Android Studio project with the Maps SDK for Android in the First steps with the Mapbox Maps SDK for Android guide. Run your application, and you should see a map in the Mapbox Streets style centered on the Gulf of Mexico and Caribbean Sea.

Retrieve layers

Next, you'll use code from the Mapbox Maps SDK for Android to get the map's layers. Manipulating the layers is what leads to the visual changes.

Import classes

First, import the correct classes in the MainActivity.java file. The Layer class is for creating layer objects that will be manipulated later. The PropertyFactory class enables you to change the map layers' colors and label properties.

Add the following code within the public void onStyleLoaded(@NonNull final Style style) { ... } method of the MainActivity.java file:

MainActivity.java
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
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.Layer;
import com.mapbox.mapboxsdk.style.layers.PropertyFactory;
import android.graphics.Color;
import android.view.View;
public class MainActivity extends AppCompatActivity {
private MapView mapView;
private FloatingActionButton changeMapPropertiesFab;
private Layer waterLayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Mapbox.getInstance(this, getString(R.string.access_token));
setContentView(R.layout.activity_main);
mapView = findViewById(R.id.runtime_mapview);
mapView.onCreate(savedInstanceState);
changeMapPropertiesFab = findViewById(R.id.floatingActionButton);
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(@NonNull final MapboxMap mapboxMap) {
mapboxMap.setStyle(Style.MAPBOX_STREETS, new Style.OnStyleLoaded() {
@Override
public void onStyleLoaded(@NonNull final Style style) {
waterLayer = style.getLayer("water");
changeMapPropertiesFab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (waterLayer != null) {
waterLayer.setProperties(PropertyFactory.fillColor(Color.parseColor("#023689"))
);
}
for (Layer singleMapLayer : style.getLayers()) {
if (singleMapLayer.getId().contains("water-") && !singleMapLayer.getId().equals("water-shadow")) {
singleMapLayer.setProperties(
PropertyFactory.textHaloBlur(10f),
PropertyFactory.textSize(25f),
PropertyFactory.textColor(Color.parseColor("#00FF08")),
PropertyFactory.textOpacity(1f)
);
}
}
}
});
}
});
}
});
}
// Add the mapView's own lifecycle methods to the activity's lifecycle methods
@Override
public void onStart() {
super.onStart();
mapView.onStart();
}
@Override
public void onResume() {
super.onResume();
mapView.onResume();
}
@Override
public void onPause() {
super.onPause();
mapView.onPause();
}
@Override
public void onStop() {
super.onStop();
mapView.onStop();
}
@Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
@Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
}

Style the layers

Now you'll change the color of the water layer and several properties of marine labels text. In this tutorial, the changes will happen when the pink floating action button is clicked.

Import classes

First, import the correct classes in the MainActivity.java file. The View class is for the pink floating action button's click listener. The Color class lets you use hex color codes to set the new color of the map layers.

Change the water color

Start by adding the floating action button's click listener below the waterLayer Layer object. Then adjust the water color to a darker blue color inside of the onClick() method.

MainActivity.java
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
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.Layer;
import com.mapbox.mapboxsdk.style.layers.PropertyFactory;
import android.graphics.Color;
import android.view.View;
public class MainActivity extends AppCompatActivity {
private MapView mapView;
private FloatingActionButton changeMapPropertiesFab;
private Layer waterLayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Mapbox.getInstance(this, getString(R.string.access_token));
setContentView(R.layout.activity_main);
mapView = findViewById(R.id.runtime_mapview);
mapView.onCreate(savedInstanceState);
changeMapPropertiesFab = findViewById(R.id.floatingActionButton);
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(@NonNull final MapboxMap mapboxMap) {
mapboxMap.setStyle(Style.MAPBOX_STREETS, new Style.OnStyleLoaded() {
@Override
public void onStyleLoaded(@NonNull final Style style) {
waterLayer = style.getLayer("water");
changeMapPropertiesFab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (waterLayer != null) {
waterLayer.setProperties(PropertyFactory.fillColor(Color.parseColor("#023689"))
);
}
for (Layer singleMapLayer : style.getLayers()) {
if (singleMapLayer.getId().contains("water-") && !singleMapLayer.getId().equals("water-shadow")) {
singleMapLayer.setProperties(
PropertyFactory.textHaloBlur(10f),
PropertyFactory.textSize(25f),
PropertyFactory.textColor(Color.parseColor("#00FF08")),
PropertyFactory.textOpacity(1f)
);
}
}
}
});
}
});
}
});
}
// Add the mapView's own lifecycle methods to the activity's lifecycle methods
@Override
public void onStart() {
super.onStart();
mapView.onStart();
}
@Override
public void onResume() {
super.onResume();
mapView.onResume();
}
@Override
public void onPause() {
super.onPause();
mapView.onPause();
}
@Override
public void onStop() {
super.onStop();
mapView.onStop();
}
@Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
@Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
}

Rerun your application, click on the pink floating action button, and you will see all the earth's water turn to a darker blue color.

Change marine labels' text properties

Now specify the new properties of the water label text. In the floating action button's onClick() method, add the following code to adjust the labels' halo blur, size, color, and opacity.

The for (Layer singleMapLayer : mapboxMap.getLayers()) line is used to tell the app to check each map layer for the text water-. This is because the Mapbox Streets style has several different water-related layer IDs:

  • water-point-label
  • water-line-label

Using the for loop allows you to retrieve these layers instead of creating a separate layer object for each water label layer and then repeating the same text changes to each individual layer. With a for loop you can change the properties of several layers that have the same word in them. Examples of words that are commonly repeated in layer names are building, water, or bridge.

MainActivity.java
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
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.Layer;
import com.mapbox.mapboxsdk.style.layers.PropertyFactory;
import android.graphics.Color;
import android.view.View;
public class MainActivity extends AppCompatActivity {
private MapView mapView;
private FloatingActionButton changeMapPropertiesFab;
private Layer waterLayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Mapbox.getInstance(this, getString(R.string.access_token));
setContentView(R.layout.activity_main);
mapView = findViewById(R.id.runtime_mapview);
mapView.onCreate(savedInstanceState);
changeMapPropertiesFab = findViewById(R.id.floatingActionButton);
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(@NonNull final MapboxMap mapboxMap) {
mapboxMap.setStyle(Style.MAPBOX_STREETS, new Style.OnStyleLoaded() {
@Override
public void onStyleLoaded(@NonNull final Style style) {
waterLayer = style.getLayer("water");
changeMapPropertiesFab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (waterLayer != null) {
waterLayer.setProperties(PropertyFactory.fillColor(Color.parseColor("#023689"))
);
}
for (Layer singleMapLayer : style.getLayers()) {
if (singleMapLayer.getId().contains("water-") && !singleMapLayer.getId().equals("water-shadow")) {
singleMapLayer.setProperties(
PropertyFactory.textHaloBlur(10f),
PropertyFactory.textSize(25f),
PropertyFactory.textColor(Color.parseColor("#00FF08")),
PropertyFactory.textOpacity(1f)
);
}
}
}
});
}
});
}
});
}
// Add the mapView's own lifecycle methods to the activity's lifecycle methods
@Override
public void onStart() {
super.onStart();
mapView.onStart();
}
@Override
public void onResume() {
super.onResume();
mapView.onResume();
}
@Override
public void onPause() {
super.onPause();
mapView.onPause();
}
@Override
public void onStop() {
super.onStop();
mapView.onStop();
}
@Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
@Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
}
Layer IDs

You will need the name (i.e. ID) of a layer if you'd like to change the layer's style at runtime. You can find the layer IDs for any map style (including our Mapbox template styles) by opening the style in Mapbox Studio. Make sure that you're signed into your Mapbox account and then visit the Styles page. Find the style in your account's list of styles and click on its Edit button. In the Mapbox Studio style editor, you will see the map style's layers listed in the left-hand sidebar.

Alternatively, you can use the getLayers() method that is part of the MapboxMap class, to print the ID of each layer in your Android Studio Logcat console. Add the following for loop code within the onMapReady method of your mapView object to print the IDs:

for (Layer singleMapLayer : mapboxMap.getLayers()) {
  Log.d("MainActivity", "onMapReady: layer name = " + singleMapLayer.getId());
}

Now that you have the full list of layer IDs, you will know what layers you'll be able to style in runtime!

Rerun your application, click on the pink floating action button, and you will see the map's water layers' text become much larger, greener, less opaque, and have a blurrier halo.

Finished product

You've created an app that changes the water layer's fill color and changes several text properties of multiple map layers. These changes didn't happen because of a particular data set, the map style, or any other factor. They happened in runtime and on-the-fly!

MainActivity.java
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
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.Layer;
import com.mapbox.mapboxsdk.style.layers.PropertyFactory;
import android.graphics.Color;
import android.view.View;
public class MainActivity extends AppCompatActivity {
private MapView mapView;
private FloatingActionButton changeMapPropertiesFab;
private Layer waterLayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Mapbox.getInstance(this, getString(R.string.access_token));
setContentView(R.layout.activity_main);
mapView = findViewById(R.id.runtime_mapview);
mapView.onCreate(savedInstanceState);
changeMapPropertiesFab = findViewById(R.id.floatingActionButton);
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(@NonNull final MapboxMap mapboxMap) {
mapboxMap.setStyle(Style.MAPBOX_STREETS, new Style.OnStyleLoaded() {
@Override
public void onStyleLoaded(@NonNull final Style style) {
waterLayer = style.getLayer("water");
changeMapPropertiesFab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (waterLayer != null) {
waterLayer.setProperties(PropertyFactory.fillColor(Color.parseColor("#023689"))
);
}
for (Layer singleMapLayer : style.getLayers()) {
if (singleMapLayer.getId().contains("water-") && !singleMapLayer.getId().equals("water-shadow")) {
singleMapLayer.setProperties(
PropertyFactory.textHaloBlur(10f),
PropertyFactory.textSize(25f),
PropertyFactory.textColor(Color.parseColor("#00FF08")),
PropertyFactory.textOpacity(1f)
);
}
}
}
});
}
});
}
});
}
// Add the mapView's own lifecycle methods to the activity's lifecycle methods
@Override
public void onStart() {
super.onStart();
mapView.onStart();
}
@Override
public void onResume() {
super.onResume();
mapView.onResume();
}
@Override
public void onPause() {
super.onPause();
mapView.onPause();
}
@Override
public void onStop() {
super.onStop();
mapView.onStop();
}
@Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
@Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
}

Next steps

There are many possibilities when using runtime styling to create beautiful and informative maps for Android applications. Read more about runtime styling more generally in our Map design guide and dig into some runtime styling examples specifically for Android:

Was this page helpful?