How To: Display a Crosshair

This how-to guide shows you how to display the crosshair on your chart and the various customization options available. The crosshair is drawn as a circle over the current data point with optional lines extending to the X and Y axes. On top of this is a ‘tooltip’ - a rectangular box displaying the current data point’s X and Y values. For CandleStick and OHLC series types (a premium feature), rather than displaying simple X and Y values the tooltip displays X, open, high, low and close values.

Multi Value tooltip

The Crosshair can only be enabled on CartesianSeries.

In order to display the crosshair you need to do the following:

  1. Enable the crosshair on the series in your chart
  2. Long-press on the plot area
  3. Style the crosshair and listen for callbacks (optional)

The code in this guide is taken from the Crosshair sample app. In this app we create three series: two column series for the number of regional downloads of our (fictional) MySpiffyApp and a line series for the company revenue.

Enabling the Crosshair

By default the Crosshair is not enabled on any Series. You have to enable it on each Series in your ShinobiChart in order for it to be displayed. To do this simply call setCrosshairEnabled(true) on each Series. You can of course disable the Crosshair for a series by calling the same method with false.

Showing the Crosshair

To make the Crosshair appear do a long-press on the plot area. For LineSeries, by default the Crosshair will lock on to the nearest Series to where you touched. It will also interpolate values along the line between your actual data points. For ColumnSeries and BarSeries the Crosshair will only be displayed if you long-press on the actual bar or column. It will not interpolate between data points but instead snap between them.

Default Crosshair interpolating a LineSeries

Crosshair Callbacks

The ShinobiChart interface defines several listener interfaces which allow you to modify the way in which the crosshair and tooltip behave.

  • The OnTrackingInfoChangedForCrosshairListener offers the callback method onTrackingInfoChanged, which notifies you of a change in the current series and point tracking information as a result of a user gesture, such as a long press to activate the crosshair or a swipe while the crosshair is active to move it. This method allows you to respond to these changes and take certain actions. For example you may wish to modify the focus point of the crosshair.

  • The OnTrackingInfoChangedForTooltipListener also has a single callback method, onTrackingInfoChanged. Similarly this method informs you of tracking information changes and again allows you to leverage this to make some interesting overrides to the default behavior for the tooltip. For example you could move its position or modify the text which is displayed on it.

  • The OnCrosshairDrawListener offers a single method, onDrawCrosshair which notifies you when a crosshair is about to be drawn and is useful if you wish to change the way in which this happens.

  • Finally, the OnCrosshairActivationStateChangedListener again has a single callback method, onCrosshairActivationStateChanged. This callback gives a convenient notification that the activation state of the crosshair has changed. For example, this can be used if you wish to perform some action, dependent upon if the crosshair is shown on screen, or hidden.

In our sample app we are going to turn the lights down when the Crosshair is made active. First we make our main Activity (in this case CrosshairActivity) implement the OnCrosshairActivationStateChangedListener interface:

Java

public class CrosshairActivity extends Activity implements ShinobiChart.OnCrosshairActivationStateChangedListener {

C#

public class MainActivity : Activity, IShinobiChartOnCrosshairActivationStateChangedListener

This means we have to implement the single callback method, onCrosshairActivationStateChanged to darken the color of the plot area background when the Crosshair is active and lighten it again when the Crosshair is inactive. While we are here we need to also remember to modify the color of the legend text appropriately. A black text color is difficult to see once we have darkened the background of the plot area:

Java

@Override
public void onCrosshairActivationStateChanged(ShinobiChart chart) {

if (chart.getCrosshair().isActive()) {
    chart.getStyle().setPlotAreaBackgroundColor(CrosshairActivity.CROSSHAIR_ACTIVE_COLOR);
    chart.getLegend().getStyle().setTextColor(Color.WHITE);
}
else {
    chart.getStyle().setPlotAreaBackgroundColor(CrosshairActivity.CROSSHAIR_INACTIVE_COLOR);
    chart.getLegend().getStyle().setTextColor(Color.BLACK);
}

// Remember to redraw the chart to make the color change visible
chart.redrawChart();
}

C#

public void OnCrosshairActivationStateChanged(IShinobiChart chart)
    {
       // Set the plot area background color depending on the crosshair's
       // activation state
       if (chart.Crosshair.IsActive) {
         chart.Style.PlotAreaBackgroundColor = CROSSHAIR_ACTIVE_COLOR;
         chart.Legend.Style.TextColor = Color.White;
       }
       else {
         chart.Style.PlotAreaBackgroundColor = CROSSHAIR_INACTIVE_COLOR;
         chart.Legend.Style.TextColor = Color.Black;
       }

       // Remember to redraw the chart to make the color change visible
       chart.RedrawChart();
    }

The two colors for the plot area background are defined as constants in our Activity but they can be any colors you wish. To keep things consistent, especially if we were to stray from the default plot area color, we’ll want to set the plot area to the CROSSHAIR_INACTIVE_COLOR when we create our chart as well (i.e. in the onCreate method).

In both cases, remember to call chart.redrawChart() for the styling changes to take effect.

We thought we would take the opportunity to smarten up the tooltip by displaying some more meaningful text. As we want the tooltip text to display different text depending upon which series it is tracking (Company Revenue or Downloads) we start by creating some useful constants, along with a StringBuilder object which will come in useful shortly:

Java

private static final String DOWNLOADS = "\nDownloads:\n";
private static final String COMPANY_REVENUE = "\nRevenue ($M):\n";
private final StringBuilder stringBuilder = new StringBuilder();

C#

private const string DOWNLOADS = "\nDownloads:\n";
    private const string COMPANY_REVENUE = "\nRevenue ($M):\n";
    private static readonly StringBuilder stringBuilder = new StringBuilder();

Next we modify our activity class definition to add an implementation of the OnTrackingInfoChangedForTooltipListener to our Activity:

Java

public class CrosshairActivity extends Activity implements
    ShinobiChart.OnCrosshairActivationStateChangedListener,
    ShinobiChart.OnTrackingInfoChangedForTooltipListener {

C#

public class MainActivity : Activity, IShinobiChartOnCrosshairActivationStateChangedListener,
                                IShinobiChartOnTrackingInfoChangedForTooltipListener

We must then implement the onTrackingInfoChanged listener method to allow us to respond to user gestures. Specifically in this listener method we set the center of the tooltip to position it at the appropriate place on the screen. If we are tracking a LineSeries (which will be the Company Revenue series) then we want to use the interpolated data point, provided by the callback method. If on the other hand we are tracking a ColumnSeries (the Downloads series) then we want to use the data point position. This listener method then goes on to call the populateTooltipText method, to populate the text which will be displayed by the tooltip. This will be done in a custom way, again depending upon the series being tracked:

Java

public void onTrackingInfoChanged(Tooltip tooltip,
        DataPoint<?, ?> dataPoint,
        DataPoint<?, ?> dataPointPosition,
        DataPoint<?, ?> interpolatedDataPointPosition) {

    if (interpolatedDataPointPosition != null) {
        // Update tooltip position and text for LineSeries (Company Revenue)
        tooltip.setCenter(interpolatedDataPointPosition);
        populateTooltipText(tooltip, interpolatedDataPointPosition, COMPANY_REVENUE);

    } else {
        // Update tooltip position and text for ColumnSeries (downloads)
        tooltip.setCenter(dataPointPosition);
        populateTooltipText(tooltip, dataPoint, DOWNLOADS);
    }
}

C#

public void OnTrackingInfoChanged(Tooltip tooltip, DataPoint dataPoint, DataPoint dataPointPosition, DataPoint interpolatedDataPointPosition)
    {
       if (interpolatedDataPointPosition != null) {
         // Update tooltip position and text for LineSeries (Company Revenue)
         tooltip.Center = interpolatedDataPointPosition;
         PopulateTooltipText(tooltip, interpolatedDataPointPosition, COMPANY_REVENUE);

       } else {
         // Update tooltip position and text for ColumnSeries (downloads)
         tooltip.Center = dataPointPosition;
         PopulateTooltipText(tooltip, dataPoint, DOWNLOADS);
       }
    }

Next we need to create the populateTooltipText method, whose responsibility is to correctly populate the text which will be displayed on the tooltip:

Java

private void populateTooltipText(Tooltip tooltip, DataPoint<?, ?> dataPoint,
        String descriptiveText) {
    // Set the text to be the data or interpolated values depending upon the
    // series type - we know our axes types so we can safely cast

    // Start by getting hold of the tool tip view which we know is of type
    // DefaultTooltipView
    DefaultTooltipView tooltipView = (DefaultTooltipView) tooltip.getView();
    // Next get the tracked series
    CartesianSeries<?> trackedSeries = tooltip.getTrackedSeries();
    // Now get the x and y axes and cast to the correct type, which we know
    CategoryAxis xAxis = (CategoryAxis) trackedSeries.getXAxis();
    NumberAxis yAxis = (NumberAxis) trackedSeries.getYAxis();
    // Let's build a string for our tooltip text - clear down the builder
    // first
    stringBuilder.setLength(0);
    // x value
    stringBuilder.append(xAxis.getFormattedString((Double) dataPoint.getX()));
    // now the text in the middle of the x and y values
    stringBuilder.append(descriptiveText);
    // now the y value
    stringBuilder.append(yAxis.getFormattedString((Double) dataPoint.getY()));
    // Now we can populate the TextView within the tooltip
    tooltipView.setText(stringBuilder.toString());
}

C#

private void PopulateTooltipText(Tooltip tooltip, DataPoint dataPoint, String descriptiveText)
{
    // Set the text to be the data or interpolated values depending upon the
    // series type - we know our axes types so we can safely cast

    // Start by getting hold of the tool tip view which we know is of type
    // DefaultTooltipView
    DefaultTooltipView tooltipView = (DefaultTooltipView) tooltip.View;
    // Next get the tracked series
    CartesianSeries trackedSeries = tooltip.TrackedSeries;
    // Now get the x and y axes and cast to the correct type, which we know
    CategoryAxis xAxis = (CategoryAxis) trackedSeries.XAxis;
    NumberAxis yAxis = (NumberAxis) trackedSeries.YAxis;
    // Let's build a string for our tooltip text - clear down the builder
    // first
    stringBuilder.Clear();
    // x value
    stringBuilder.Append(xAxis.GetFormattedString((Java.Lang.Double) dataPoint.X));
    // now the text in the middle of the x and y values
    stringBuilder.Append(descriptiveText);
    // now the y value
    stringBuilder.Append(yAxis.GetFormattedString((Java.Lang.Double) dataPoint.Y));
    // Now we can populate the TextView within the tooltip
    tooltipView.SetText(stringBuilder.ToString());
}

Finally, we need to set the Activity as the listeners on the chart. Do this when creating the chart in the onCreate method.

Java

shinobiChart.setOnCrosshairActivationStateChangedListener(this);
shinobiChart.setOnTrackingInfoChangedForTooltipListener(this);

C#

shinobiChart.SetOnCrosshairActivationStateChangedListener (this);
shinobiChart.SetOnTrackingInfoChangedForTooltipListener (this);

Default Crosshair using callback methods to darken plot area

Styling the Crosshair

That looks ok but we probably want to make the Crosshair lines stand out a bit more. There are a number of styling options available for the Crosshair and its Tooltip but for this sample app we’re just going to make the lines white:

Java

shinobiChart.getCrosshair().getStyle().setLineColor(Color.WHITE);

C#

shinobiChart.Crosshair.Style.LineColor = Color.White;

The CrosshairStyle class contains the methods you need to style it as you want. Again, you want to do this when setting up your chart in the onCreate method.

Default Crosshair with some custom styling

And that’s it! Try running the sample code and displaying the Crosshair. Swipe along the plot area when the Crosshair is locked to the line series and when it’s locked to the column series to see the difference in behavior. Enable gestures on the axes and watch what happens when you zoom in and out with the Crosshair displayed.

See related code sample: Crosshair Sample, in the samples/crosshair folder of your product download (Xamarin.Android/samples/Crosshair if you’re using Xamarin.Android).