1

Background

I'm developing an app for Android that plots data as a line graph using AndroidPlot. Because of the nature of the data, it's important that it be pannable and zoomable. I'm using AndroidPlot's sample code on bitbucket for panning and zooming, modified to allow panning and zooming in both X and Y directions.

Everything works as desired except that there are no X and Y axis lines. It is very disorienting to look at the data without them. The grid helps, but there's no guarantee that grid lines will actually fall on the axis.

To remedy this I have tried adding two series, one that falls on just the X axis and the other on the Y. The problem with this is that if one zooms out too far the axis simply end, and it becomes apparent that I have applied a 'hack'.

Question

Is it possible to add X and Y axis lines to AndroidPlot? Or will my sad hack have to do?

EDIT Added tags

Nathan Vance
  • 605
  • 1
  • 5
  • 21

1 Answers1

0

I figured it out. It wasn't trivial, took a joint effort with a collaborator, and sucked up many hours of our time.

Starting with the sample mentioned in my question, I had to extend XYPlot (which I called GraphView) and override the onPreInit method. Note that I have two PointF's, minXY and maxXY, that are defined in my overridden XYPlot and manipulated when I zoom or scroll.

@Override
protected void onPreInit() {
    super.onPreInit();

    final Paint axisPaint = new Paint();
    axisPaint.setColor(getResources().getColor(R.color.MY_AXIS_COLOR));
    axisPaint.setStrokeWidth(3); //or whatever stroke width you want

    XYGraphWidget oldWidget = getGraphWidget();
    XYGraphWidget widget = new XYGraphWidget(getLayoutManager(),
            this,
            new SizeMetrics(
                    oldWidget.getHeightMetric(),
                    oldWidget.getWidthMetric())) {
        //We now override XYGraphWidget methods
        RectF mGridRect;
        @Override
        protected void doOnDraw(Canvas canvas, RectF widgetRect)
                throws PlotRenderException {
            //In order to draw the x axis, we must obtain gridRect. I believe this is the only
            //way to do so as the more convenient routes have private rather than protected access.
            mGridRect = new RectF(widgetRect.left + ((isRangeAxisLeft())?getRangeLabelWidth():1),
                    widgetRect.top + ((isDomainAxisBottom())?1:getDomainLabelWidth()),
                    widgetRect.right - ((isRangeAxisLeft())?1:getRangeLabelWidth()),
                    widgetRect.bottom - ((isDomainAxisBottom())?getDomainLabelWidth():1));
            super.doOnDraw(canvas, widgetRect);
        }
        @Override
        protected void drawGrid(Canvas canvas) {
            super.drawGrid(canvas);
            if(mGridRect == null) return;
            //minXY and maxXY are PointF's defined elsewhere. See my comment in the answer.
            if(minXY.y <= 0 && maxXY.y >= 0) { //Draw the x axis
                RectF paddedGridRect = getGridRect();
                //Note: GraphView.this is the extended XYPlot instance.
                XYStep rangeStep = XYStepCalculator.getStep(GraphView.this, XYAxisType.RANGE,
                        paddedGridRect, getCalculatedMinY().doubleValue(),
                        getCalculatedMaxY().doubleValue());
                double rangeOriginF = paddedGridRect.bottom;
                float yPix = (float) (rangeOriginF + getRangeOrigin().doubleValue() * rangeStep.getStepPix() /
                        rangeStep.getStepVal());
                //Keep things consistent with drawing y axis even though drawRangeTick is public
                //drawRangeTick(canvas, yPix, 0, getRangeLabelPaint(), axisPaint, true);
                canvas.drawLine(mGridRect.left, yPix, mGridRect.right, yPix, axisPaint);
            }
            if(minXY.x <= 0 && maxXY.x >= 0) { //Draw the y axis
                RectF paddedGridRect = getGridRect();
                XYStep domianStep = XYStepCalculator.getStep(GraphView.this, XYAxisType.DOMAIN,
                        paddedGridRect, getCalculatedMinX().doubleValue(),
                        getCalculatedMaxX().doubleValue());
                double domainOriginF = paddedGridRect.left;
                float xPix = (float) (domainOriginF - getDomainOrigin().doubleValue() * domianStep.getStepPix() /
                        domianStep.getStepVal());
                //Unfortunately, drawDomainTick has private access in XYGraphWidget
                canvas.drawLine(xPix, mGridRect.top, xPix, mGridRect.bottom, axisPaint);
            }
        }
    };
    widget.setBackgroundPaint(oldWidget.getBackgroundPaint());
    widget.setMarginTop(oldWidget.getMarginTop());
    widget.setMarginRight(oldWidget.getMarginRight());
    widget.setPositionMetrics(oldWidget.getPositionMetrics());
    getLayoutManager().remove(oldWidget);
    getLayoutManager().addToTop(widget);
    setGraphWidget(widget);
    //More customizations can go here
}

And that was that. I sure wish this was built into AndroidPlot; it'll be nasty trying to fix this when it breaks in an AndroidPlot update...

Nathan Vance
  • 605
  • 1
  • 5
  • 21
  • I'm currently facing the exact same problem. Is this the solution you went with in the end or did you solve it in another way? – Joakim Nov 27 '15 at 09:56
  • Unfortunately, this is what I went with. If you find anything better I'd love to know! – Nathan Vance Nov 28 '15 at 15:10