Search code examples
androidscichart

SciChart TradeChartAxisLabelProvider custom formatting to show days, months


How can I customize the X axis to show days (or months or years, based on the selected range) where a new day/month/year begins? I am using CategoryDateAxis (CreateMultiPaneStockChartsFragment example).

What I want:

Larger ranges:

enter image description here

Smaller ranges (easily see where new day begins):

enter image description here

What I have:

Right now I am using default label provider and it is hard to see when new day/month/year begins. E.g. for 7 day range:

enter image description here

Axis is construced like this:

final CategoryDateAxis xAxis = sciChartBuilder.newCategoryDateAxis()
                .withVisibility(isMainPane ? View.VISIBLE : View.GONE)
                .withVisibleRange(sharedXRange)
                //.withLabelProvider(new TradeChartAxisLabelProviderDateTime())
                .withGrowBy(0.01d, 0.01d)
                .build();

How do I achieve this?

public static class TradeChartAxisLabelProviderDateTime extends TradeChartAxisLabelProvider {
    public TradeChartAxisLabelProviderDateTime() {
        super();
    }

    @Override
    public String formatLabel(Comparable dataValue) {
        if(currentRange == RANGE_1_YEAR) {

        } else if(currentRange == RANGE_1_MONTH) {

        } else if(currentRange == RANGE_1_DAY) {

        }
        String text = super.formatLabel(dataValue).toString();
        return text;
    }
}

Solution

  • To implement selection of label based on VisibleRange you can use code like this:

    public static class TradeChartAxisLabelProviderDateTime extends TradeChartAxisLabelProvider {
        public TradeChartAxisLabelProviderDateTime() {
            super(new TradeChartAxisLabelFormatterDateTime());
        }
    
        private static class TradeChartAxisLabelFormatterDateTime implements ILabelFormatter<CategoryDateAxis> {
            private final SimpleDateFormat labelFormat, cursorLabelFormat;
    
            private TradeChartAxisLabelFormatterDateTime() {
                labelFormat = new SimpleDateFormat(CategoryDateAxis.DEFAULT_TEXT_FORMATTING, Locale.getDefault());
                cursorLabelFormat = new SimpleDateFormat(CategoryDateAxis.DEFAULT_TEXT_FORMATTING, Locale.getDefault());
            }
    
            @Override
            public void update(CategoryDateAxis axis) {
                final ICategoryLabelProvider labelProvider = Guard.instanceOfAndNotNull(axis.getLabelProvider(), ICategoryLabelProvider.class);
    
                // this is range of indices which are drawn by CategoryDateAxis
                final IRange<Double> visibleRange = axis.getVisibleRange();
    
                // convert indicies to range of dates
                final DateRange dateRange = new DateRange(
                        ComparableUtil.toDate(labelProvider.transformIndexToData((int) NumberUtil.constrain(Math.floor(visibleRange.getMin()), 0, Integer.MAX_VALUE))),
                        ComparableUtil.toDate(labelProvider.transformIndexToData((int) NumberUtil.constrain(Math.ceil(visibleRange.getMax()), 0, Integer.MAX_VALUE))));
    
                if (dateRange.getIsDefined()) {
                    long ticksInViewport = dateRange.getDiff().getTime();
    
                    // select formatting based on diff in time between Min and Max
                    if (ticksInViewport > DateIntervalUtil.fromYears(1)) {
                        // apply year formatting
                        labelFormat.applyPattern("");
                        cursorLabelFormat.applyPattern("");
                    } else if (ticksInViewport > DateIntervalUtil.fromMonths(1)) {
                        // apply month formatting
                        labelFormat.applyPattern("");
                        cursorLabelFormat.applyPattern("");
                    } else if (ticksInViewport > DateIntervalUtil.fromMonths(1)) {
                        // apply day formatting
                        labelFormat.applyPattern("");
                        cursorLabelFormat.applyPattern("");
                    }
                }
            }
    
            @Override
            public CharSequence formatLabel(Comparable dataValue) {
                final Date valueToFormat = ComparableUtil.toDate(dataValue);
                return labelFormat.format(valueToFormat);
            }
    
            @Override
            public CharSequence formatCursorLabel(Comparable dataValue) {
                final Date valueToFormat = ComparableUtil.toDate(dataValue);
                return cursorLabelFormat.format(valueToFormat);
            }
        }
    }
    

    In update() you can get access to VisibleRange of axis and based on it select label formatting and then use SimpleDateFormat to format Dates.

    But as I understand your case is more complex than this because you can't get labels which allow to see when new day/month/year begins based on current VisibleRange. For this case you'll need to select format string based on previously formatted values and track when day/month/year changes.