Search code examples
javaandroidandroidplot

Dual axis labels


Is it possible to create XYPlot with two series (series have the same domain/x values, but range/y values are totally different)? I mean, first serie has it's own scale presented on left range axis, and second serie has it's own scale presented on right range axis.


Solution

  • EDIT: As of 1.3.1 NormedXYSeries has been added, making dual scale implementations a little simpler. DualScaleActivity in the DemoApp provides a full example.

    Starting with 1.x and later, this is possible with Androidplot - the f(x) example provides a basic example of how it's done.

    The two important pieces are creating a custom LineLabelRenderer to generate tick labels. This provided example is used to customize coloring and tick spacing:

        /**
         * Draws every other tick label and renders text in gray instead of white.
         */
        class MySecondaryLabelRenderer extends MyLineLabelRenderer {
    
    
            @Override
            public void drawLabel(Canvas canvas, XYGraphWidget.LineLabelStyle style,
                    Number val, float x, float y, boolean isOrigin) {
                if(val.doubleValue() % 2 == 0) {
                    final Paint paint = style.getPaint();
                    if(!isOrigin) {
                        paint.setColor(Color.GRAY);
                    }
                    super.drawLabel(canvas, style, val, x, y, isOrigin);
                }
            }
        }
    

    Then you'll need to attach attach the custom LineLabelRenderer to one of the edges of the plot:

    plot.getGraph().setLineLabelRenderer(XYGraphWidget.Edge.RIGHT, new MySecondaryLabelRenderer());
    

    If you dont need any fancy coloring, you can also just set a custom formatter:

          plot.getGraph().getLineLabelStyle(XYGraphWidget.Edge.RIGHT).setFormat(new Format() {
                @Override
                public StringBuffer format(Object seriesVal, StringBuffer stringBuffer,
                        FieldPosition fieldPosition) {
    
                    // do whatever you need to do here.
                    stringBuffer.append(((Number) seriesVal).doubleValue() + "bla");
                    return stringBuffer;
                }
    
                @Override
                public Object parseObject(String s, ParsePosition parsePosition) {
                    // nothing to do here
                    return null;
                }
            });
    

    This will give you an accurately labeled dual scale, but depending on how different the two scales are, you may wind up with one of your series being zoomed way out, or at least not ideally sized.

    The best way to solve this new issue is to normalize all series data to a range between 0 and 1 and then expand the normalized values back into their original values in a custom formatter.