Search Results for

    Show / Hide Table of Contents

    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:

    • Native Example
    • Xamarin Example

    Adding the SeriesValueModifier to a Chart

    Any Chart Modifier can be added to a SciChartSurface via the chartModifiers property and SeriesValueModifier is no different.

    • Java
    • Java with Builders API
    • Kotlin
    // 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);
    
    // Assume a surface has been created and configured somewhere
    // Create a modifier
    ModifierGroup seriesValueModifierGroup = sciChartBuilder.newModifierGroup()
            .withSeriesValueModifier()
            .build()
            .build();
    
    // Add the modifier to the surface
    surface.getChartModifiers().add(seriesValueModifierGroup);
    
    // Assume a surface has been created and configured somewhere
    // Create a modifier
    var seriesValueModifier = SeriesValueModifier()
    
    // Add the modifier to the surface
    surface.chartModifiers.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:

    • Java
    • Java with Builders API
    • Kotlin
    // 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);
    
    // Assume renderable series with name "Blue Series" has been created and configured somewhere
    // Create a factory with a predicate
    ModifierGroup seriesValueModifierGroup = sciChartBuilder.newModifierGroup()
            .withSeriesValueModifier(series ->
                    series.getDataSeries().getSeriesName() == "Blue Series"
            )
            .build()
            .build();
    
    // Add the modifier to the surface
    surface.getChartModifiers().add(seriesValueModifierGroup);
    
    // Assume renderable series with name "Blue Series" has been created and configured somewhere
    // Create a factory with a predicate
    val factory = DefaultSeriesValueMarkerFactory { series: IRenderableSeries ->
        series.dataSeries.seriesName === "Blue Series"
    }
    
    // Create seriesValueModifier with this factory
    val seriesValueModifier = SeriesValueModifier(factory)
    
    // Add the modifier to the surface
    surface.chartModifiers.add(seriesValueModifier)
    

    The result is that only the blue series has a marker:

    Series Value Modifier

    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:
    • Java
    • Java with Builders API
    • Kotlin
    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);
        }
    }
    
    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);
        }
    }
    
    class HorizontalLineSeriesValueMarkerAnnotation(
        context: Context,
        val seriesValueHelper: DefaultSeriesValueMarkerAnnotationHelper<HorizontalLineSeriesValueMarkerAnnotation>
    ) : HorizontalLineAnnotation(context) {
        override fun attachTo(services: IServiceContainer) {
            val renderableSeries = seriesValueHelper.renderableSeries
            xAxisId = renderableSeries.xAxisId
            yAxisId = renderableSeries.yAxisId
    
            super.attachTo(services)
        }
    
        override fun update(xCalc: ICoordinateCalculator, yCalc: ICoordinateCalculator) {
            seriesValueHelper.tryUpdateAnnotation(this)
    
            super.update(xCalc, yCalc)
        }
    }
    
    • Subclass SeriesValueModifier.SeriesValueMarkerAnnotationHelper where we will style our annotation and annotation labels:
    • Java
    • Java with Builders API
    • Kotlin
    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)));
                    }
                }
            });
        }
    }
    
    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)));
                    }
                }
            });
        }
    }
    
    class HorizontalLineSeriesValueMarkerAnnotationHelper(
        renderableSeries: IRenderableSeries,
        isValidRenderableSeriesPredicate: Predicate<IRenderableSeries?>
    ) : DefaultSeriesValueMarkerAnnotationHelper<HorizontalLineSeriesValueMarkerAnnotation>(
            renderableSeries,
            isValidRenderableSeriesPredicate
        ) {
        private val lineThickness = convertValueToDp(1f)
        private val dashPattern = floatArrayOf(convertValueToDp(4f), convertValueToDp(4f))
    
        override fun updateAnnotation(
            annotation: HorizontalLineSeriesValueMarkerAnnotation,
            lastValue: Comparable<*>,
            lastColor: Int
        ) {
            super.updateAnnotation(annotation, lastValue, lastColor)
    
            Dispatcher.postOnUiThread {
                annotation.stroke = SolidPenStyle(lastColor, false, lineThickness, dashPattern)
                val count = annotation.annotationLabels.size
                for (i in 0 until count) {
                    val label = annotation.annotationLabels[i]
                    label.background = PaintDrawable(lastColor)
                    label.fontStyle = FontStyle(convertValueToDp(12f), ColorUtil.getInvertedColor(lastColor))
                }
            }
        }
    }
    
    • Subclass SeriesValueModifier.SeriesValueMarkerBase, where we will create, destroy, add ot remove our custom markerAnnotation when needed:
    • Java
    • Java with Builders API
    • Kotlin
    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;
        }
    }
    
    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;
        }
    }
    
    class HorizontalLineSeriesValueMarker(
        renderableSeries: IRenderableSeries,
        isValidRenderableSeriesPredicate: Predicate<IRenderableSeries?>
    ) : SeriesValueMarkerBase(renderableSeries, isValidRenderableSeriesPredicate) {
        private var markerAnnotation: HorizontalLineSeriesValueMarkerAnnotation? = null
    
        override fun tryRemoveMarkerAnnotation(parentSurface: ISciChartSurface) {
            parentSurface.annotations.remove(markerAnnotation)
        }
    
        override fun tryAddMarkerAnnotation(parentSurface: ISciChartSurface) {
            ListUtil.safeAddExact(parentSurface.annotations, markerAnnotation)
        }
    
        override fun createMarkerAnnotation(context: Context) {
            markerAnnotation = HorizontalLineSeriesValueMarkerAnnotation(
                context,
                HorizontalLineSeriesValueMarkerAnnotationHelper(
                    renderableSeries,
                    isValidRenderableSeriesPredicate
                )
            )
            val label = AnnotationLabel(context)
            label.labelPlacement = LabelPlacement.Axis
            markerAnnotation?.annotationLabels?.add(label)
        }
    
        override fun destroyMarkerAnnotation() {
            markerAnnotation = null
        }
    }
    
    • Create a custom SeriesValueModifier.ISeriesValueMarkerFactory and implement createMarkerFor(renderableSeries.IRenderableSeries renderableSeries) which will return our custom marker:
    • Java
    • Java with Builders API
    • Kotlin
    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);
        }
    }
    
    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);
        }
    }
    
    class HorizontalLineSeriesValueMarkerFactory(private val isValidSeriesPredicate: Predicate<IRenderableSeries?>) :
        ISeriesValueMarkerFactory {
        override fun createMarkerFor(renderableSeries: IRenderableSeries): ISeriesValueMarker {
            return HorizontalLineSeriesValueMarker(renderableSeries, isValidSeriesPredicate)
        }
    }
    
    • Create a SeriesValueModifier with your custom factory and add it to the surface:
    • Java
    • Java with Builders API
    • Kotlin
    final SeriesValueModifier seriesValueModifier = new SeriesValueModifier(
            new HorizontalLineSeriesValueMarkerFactory(item -> true)
    );
    surface.getChartModifiers().add(seriesValueModifier);
    
    final SeriesValueModifier seriesValueModifier = new SeriesValueModifier(
            new HorizontalLineSeriesValueMarkerFactory(item -> true)
    );
    surface.getChartModifiers().add(seriesValueModifier);
    
    val seriesValueModifier = SeriesValueModifier(
        HorizontalLineSeriesValueMarkerFactory { item: IRenderableSeries? -> true }
    )
    surface.chartModifiers.add(seriesValueModifier)
    

    This produces the following output:

    Series Value Modifier Custom Marker

    Back to top © 2011-2025 SciChart. All rights reserved. | sitemap.xml