Customize Drop-In UI

In the current iteration of Drop-In UI you can customize the default UI components in two mutually exclusive ways:

Style UI Components

To use a different set of colors and text properties, you can customize them in NavigationView at runtime.

Add your custom style to styles.xml or themes.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="MyCustomSpeedLimitStyle">
        <item name="speedLimitTextColor">#FFFFFF</item>
        <item name="speedLimitBackgroundColor">@color/primary</item>
        <item name="speedLimitMutcdBorderColor">@color/secondary</item>
        <item name="speedLimitViennaBorderColor">@color/secondary</item>
    </style>

    <style name="MyCustomSpeedLimitTextAppearance" parent="TextAppearance.AppCompat">
        <item name="android:textSize">10sp</item>
        <item name="android:textColor">#FFFFFF</item>
    </style>
</resources>

In your Activity or Fragment invoke the function to change the appearance anytime

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.your_layout)
    findViewById(R.id.navigationView).customizeViewStyles {
        speedLimitStyle = R.style.MyCustomSpeedLimitStyle
        speedLimitTextAppearance = R.style.MyCustomSpeedLimitTextAppearance
    }
}

The snippet above shows how to customize speed limit UI component. To customize all the standalone UI components refer to the example below.

example
Custom runtime styling with NavigationView

Render custom runtime styling of default UI components to be used with the NavigationView

NavigationView exposes options that can be used to define the behavior of the components associated with it.

To change the appearance of route arrows, define RouteArrowOptions and apply it to NavigationView

class MyActivity: AppCompatActivity() {
    private val routeArrowOptions by lazy {
        RouteArrowOptions.Builder(this)
            .withAboveLayerId(RouteLayerConstants.TOP_LEVEL_ROUTE_LINE_LAYER_ID)
            .withArrowColor(Color.RED)
            .build()
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.your_activity_layout)
        findViewById(R.id.navigationView).customizeViewOptions {
            routeArrowOptions = this@MyActivity.routeArrowOptions
        }
    }
}
Runtime support for options

NavigationViewOptions only allows for defining RouteLine, RouteArrow, and selection of predefined map day and night styles as options. Support for a wide selection of customization options when using the NavigationView is coming soon.

example
Define options used by NavigationView

Apply NavigationViewOptions at runtime.

Use custom views

To use a custom designed view with NavigationView instead of the default supported view

Define the layout for custom view

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    tools:parentTag="android.widget.FrameLayout">

    <TextView
        android:id="@+id/speedLimit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:textSize="18sp"
        android:textColor="@android:color/black"
        android:gravity="center"
        android:background="@android:color/white"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintEnd_toStartOf="parent"/>
</merge>
Merge into a FrameLayout

Drop-In UI uses FrameLayout as a container to display views on top of the MapView. All default and custom views are merged into the parent FrameLayout each of which has a specific bound set on the screen. For a better understanding of the bounds take a look at mapbox_navigation_view_layout

Create a UIBinder

UIBinder is an interface responsible for transitioning view(s) into ViewGroup

@OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
private class MySpeedLimitViewBinder : UIBinder {
    override fun bind(viewGroup: ViewGroup): MapboxNavigationObserver {
        val scene = Scene.getSceneForLayout(
            viewGroup,
            R.layout.my_speed_limit_layout,
            viewGroup.context
        )
        TransitionManager.go(scene, Fade())

        val binding = MySpeedLimitLayoutBinding.bind(viewGroup)
        return MySpeedLimitComponent(binding)
    }
}

The function bind defines the transition of speed limit view into the viewGroup using a Fade transition. You can choose a different transition based on app requirements.

Create a UIComponent

UIComponent is an interface that gives you access to MapboxNavigation and coroutineScope. onAttached and onDetached are invoked when the UIComponent is attached to and detached from the binder. All coroutines that you launch in onAttached are cancelled automatically when the observer is detached. Hence, you don't have to explicitly override onDetached in your custom UIComponent

class MySpeedLimitComponent(
    private val speedLimit: MySpeedLimitLayoutBinding
) : UIComponent() {

    override fun onAttached(mapboxNavigation: MapboxNavigation) {
        super.onAttached(mapboxNavigation)
        mapboxNavigation.flowLocationMatcherResult().collect {
            val postedSpeedLimit = it.speedLimit?.speedKmph

            textView.text = when {
                postedSpeedLimit != null -> "Speed Limit\n $postedSpeedLimit"
                else -> "--"
            }
        }
    }
}

Finally, apply your custom view binder to NavigationView

class MyActivity: AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.your_activity_layout)
        findViewById(R.id.navigationView).customizeViewBinders {
            speedLimitBinder = MySpeedLimitViewBinder()
        }
    }
}
Default speed limitCustom speed limit
example
Custom speed limit with NavigationView

Render custom speed limit view to be used with the NavigationView