SeriesValueModifier
The SeriesValueModifier is a custom ChartModifier that places a SeriesValueModifier.SeriesValueMarkerAnnotation on the YAxis for each RenderableSeries in the chart, showing the current IRenderableSeries latest Y-value.
![Series Value Modifier](../images/series-value-modifier.mp4">
Note
Examples of the SeriesValueModifier usage can be found in the SciChart Android Examples Suite as well as on GitHub:
Adding the SeriesValueModifier to a Chart
Any Chart Modifier can be added to a SciChartSurface via the chartModifiers property and SeriesValueModifier is no different.
// Assume a surface has been created and configured somewhere
// Create a modifier
SeriesValueModifier seriesValueModifier = new SeriesValueModifier();
// Add the modifier to the surface
surface.getChartModifiers().add(seriesValueModifier);
Note
To learn more about features available, Chart Modifier APIs article.
Excluding a Series from the SeriesValueModifier
By default, all RenderableSeries from renderableSeries collection are included in the SeriesValueModifier and thus will have an axis marker placed on YAxis. This behavior can be controlled and to do so you’ll need to create a SeriesValueModifier.DefaultSeriesValueMarkerFactory instance with a predicate to decide which series would you like to include or exclude.
Imagine you have three IRenderableSeries and you want to show marker only for a series with the name "Blue Series". Please see the code below, which shows how to do just that:
// Assume renderable series with name "Blue Series" has been created and configured somewhere
// Create a factory with a predicate
final SeriesValueModifier.DefaultSeriesValueMarkerFactory factory = new SeriesValueModifier.DefaultSeriesValueMarkerFactory(series ->
series.getDataSeries().getSeriesName() == "Blue Series"
);
// Create seriesValueModifier with this factory
final SeriesValueModifier seriesValueModifier = new SeriesValueModifier(factory);
// Add the modifier to the surface
surface.getChartModifiers().add(seriesValueModifier);
The result is that only the blue series has a marker:
Styling the SeriesValueModifier Axis Markers
By default, SeriesValueModifier uses SeriesValueModifier.DefaultSeriesValueMarker to create SeriesValueModifier.SeriesValueMarkerAnnotation. You can customize a marker annotation style or provide some complete custom annotation.
Note
To learn more about AxisMarkerAnnotation API, follow AxisMarkerAnnotation API article.
As an example of such customization, let’s place a HorizontalLineAnnotation with an axis label. You will need to write few classes with some implementations to inject the desired marker into the SeriesValueModifier pipeline. Those are:
- Subclass HorizontalLineAnnotation with SeriesValueModifier.SeriesValueMarkerAnnotationHelper as a initializer parameter to call tryUpdateAnnotation(TAnnotation annotation) method when needed:
class HorizontalLineSeriesValueMarkerAnnotation extends HorizontalLineAnnotation {
final SeriesValueModifier.DefaultSeriesValueMarkerAnnotationHelper<HorizontalLineSeriesValueMarkerAnnotation> seriesValueHelper;
public HorizontalLineSeriesValueMarkerAnnotation(
Context context,
SeriesValueModifier.DefaultSeriesValueMarkerAnnotationHelper<HorizontalLineSeriesValueMarkerAnnotation> seriesValueHelper
) {
super(context);
this.seriesValueHelper = seriesValueHelper;
}
@Override
public void attachTo(IServiceContainer services) {
final IRenderableSeries renderableSeries = seriesValueHelper.renderableSeries;
setXAxisId(renderableSeries.getXAxisId());
setYAxisId(renderableSeries.getYAxisId());
super.attachTo(services);
}
@Override
protected void update(ICoordinateCalculator xCalc, ICoordinateCalculator yCalc) {
seriesValueHelper.tryUpdateAnnotation(this);
super.update(xCalc, yCalc);
}
}
- Subclass SeriesValueModifier.SeriesValueMarkerAnnotationHelper where we will style our annotation and annotation labels:
class HorizontalLineSeriesValueMarkerAnnotationHelper extends SeriesValueModifier.DefaultSeriesValueMarkerAnnotationHelper<HorizontalLineSeriesValueMarkerAnnotation> {
private float lineThickness = convertValueToDp(1);
private float[] dashPattern = new float[] { convertValueToDp(4), convertValueToDp(4)};
public HorizontalLineSeriesValueMarkerAnnotationHelper(IRenderableSeries renderableSeries, Predicate<IRenderableSeries> isValidRenderableSeriesPredicate) {
super(renderableSeries, isValidRenderableSeriesPredicate);
}
@Override
protected void updateAnnotation(HorizontalLineSeriesValueMarkerAnnotation annotation, Comparable<?> lastValue, int lastColor) {
super.updateAnnotation(annotation, lastValue, lastColor);
Dispatcher.postOnUiThread(new Runnable() {
@Override
public void run() {
annotation.setStroke(new SolidPenStyle(lastColor, false, lineThickness, dashPattern));
int count = annotation.annotationLabels.size();
for (int i = 0; i < count; i++) {
final AnnotationLabel label = annotation.annotationLabels.get(i);
label.setBackground(new PaintDrawable(lastColor));
label.setFontStyle(new FontStyle(convertValueToDp(12), ColorUtil.getInvertedColor(lastColor)));
}
}
});
}
}
- Subclass SeriesValueModifier.SeriesValueMarkerBase, where we will create, destroy, add ot remove our custom markerAnnotation when needed:
class HorizontalLineSeriesValueMarker extends SeriesValueModifier.SeriesValueMarkerBase {
private HorizontalLineSeriesValueMarkerAnnotation markerAnnotation;
protected HorizontalLineSeriesValueMarker(
IRenderableSeries renderableSeries,
Predicate<IRenderableSeries> isValidRenderableSeriesPredicate
) {
super(renderableSeries, isValidRenderableSeriesPredicate);
}
@Override
protected void tryRemoveMarkerAnnotation(ISciChartSurface parentSurface) {
parentSurface.getAnnotations().remove(markerAnnotation);
}
@Override
protected void tryAddMarkerAnnotation(ISciChartSurface parentSurface) {
ListUtil.safeAddExact(parentSurface.getAnnotations(), markerAnnotation);
}
@Override
protected void createMarkerAnnotation(Context context) {
markerAnnotation = new HorizontalLineSeriesValueMarkerAnnotation(
context,
new HorizontalLineSeriesValueMarkerAnnotationHelper(
renderableSeries,
isValidRenderableSeriesPredicate
)
);
final AnnotationLabel label = new AnnotationLabel(context);
label.setLabelPlacement(LabelPlacement.Axis);
markerAnnotation.annotationLabels.add(label);
}
@Override
protected void destroyMarkerAnnotation() {
markerAnnotation = null;
}
}
- Create a custom SeriesValueModifier.ISeriesValueMarkerFactory and implement createMarkerFor(renderableSeries.IRenderableSeries renderableSeries) which will return our custom marker:
class HorizontalLineSeriesValueMarkerFactory implements SeriesValueModifier.ISeriesValueMarkerFactory {
private Predicate<IRenderableSeries> isValidSeriesPredicate;
protected HorizontalLineSeriesValueMarkerFactory(Predicate<IRenderableSeries> isValidSeriesPredicate) {
super();
this.isValidSeriesPredicate = isValidSeriesPredicate;
}
@Override
public SeriesValueModifier.ISeriesValueMarker createMarkerFor(IRenderableSeries renderableSeries) {
return new HorizontalLineSeriesValueMarker(renderableSeries, isValidSeriesPredicate);
}
}
- Create a SeriesValueModifier with your custom factory and add it to the surface:
final SeriesValueModifier seriesValueModifier = new SeriesValueModifier(
new HorizontalLineSeriesValueMarkerFactory(item -> true)
);
surface.getChartModifiers().add(seriesValueModifier);
This produces the following output: