如何旋转图形上的文本值

How to rotate a text value on a graph

我有一个使用 androidplot 构建的图表。我需要水平查看每个值(旋转 90 度)。在屏幕截图中,我展示了我的意思。怎么做?

没有内置的配置选项来执行此操作,但在 custom renderer. Here's the quickstart example 修改后相对容易做到:

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Bundle;
import android.support.annotation.NonNull;

import com.androidplot.ui.SeriesRenderer;
import com.androidplot.util.PixelUtils;
import com.androidplot.xy.CatmullRomInterpolator;
import com.androidplot.xy.LineAndPointFormatter;
import com.androidplot.xy.LineAndPointRenderer;
import com.androidplot.xy.PointLabelFormatter;
import com.androidplot.xy.PointLabeler;
import com.androidplot.xy.SimpleXYSeries;
import com.androidplot.xy.XYGraphWidget;
import com.androidplot.xy.XYPlot;
import com.androidplot.xy.XYSeries;

import java.text.FieldPosition;
import java.text.Format;
import java.text.ParsePosition;
import java.util.Arrays;
import java.util.List;

/**
 * A simple XYPlot
 */
public class SimpleXYPlotActivity extends Activity {

    private XYPlot plot;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.simple_xy_plot_example);

        // initialize our XYPlot reference:
        plot = (XYPlot) findViewById(R.id.plot);

        // create a couple arrays of y-values to plot:
        final Number[] domainLabels = {1, 2, 3, 6, 7, 8, 9, 10, 13, 14};
        Number[] series1Numbers = {1, 4, 2, 8, 4, 16, 8, 32, 16, 64};
        Number[] series2Numbers = {5, 2, 10, 5, 20, 10, 40, 20, 80, 40};

        // turn the above arrays into XYSeries':
        // (Y_VALS_ONLY means use the element index as the x value)
        XYSeries series1 = new SimpleXYSeries(
                Arrays.asList(series1Numbers), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Series1");
        XYSeries series2 = new SimpleXYSeries(
                Arrays.asList(series2Numbers), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "Series2");

        // create formatters to use for drawing a series using LineAndPointRenderer
        // and configure them from xml:
        LineAndPointFormatter series1Format =
                new MyLineAndPointFormatter(this, R.xml.line_point_formatter_with_labels);

        LineAndPointFormatter series2Format =
                new MyLineAndPointFormatter(this, R.xml.line_point_formatter_with_labels_2);

        // add an "dash" effect to the series2 line:
        series2Format.getLinePaint().setPathEffect(new DashPathEffect(new float[] {

                // always use DP when specifying pixel sizes, to keep things consistent across devices:
                PixelUtils.dpToPix(20),
                PixelUtils.dpToPix(15)}, 0));

        // (optional) add some smoothing to the lines:
        // see: http://androidplot.com/smooth-curves-and-androidplot/
        series1Format.setInterpolationParams(
                new CatmullRomInterpolator.Params(10, CatmullRomInterpolator.Type.Centripetal));

        series2Format.setInterpolationParams(
                new CatmullRomInterpolator.Params(10, CatmullRomInterpolator.Type.Centripetal));

        // add a new series' to the xyplot:
        plot.addSeries(series1, series1Format);
        plot.addSeries(series2, series2Format);

        plot.getGraph().getLineLabelStyle(XYGraphWidget.Edge.BOTTOM).setFormat(new Format() {
            @Override
            public StringBuffer format(Object obj, @NonNull StringBuffer toAppendTo, @NonNull FieldPosition pos) {
                int i = Math.round(((Number) obj).floatValue());
                return toAppendTo.append(domainLabels[i]);
            }
            @Override
            public Object parseObject(String source, @NonNull ParsePosition pos) {
                return null;
            }
        });
    }

    /**
     * A LineAndPointRenderer that rotates it's point labels -90 degrees.
     */
    static class MyLineAndPointRenderer extends LineAndPointRenderer<MyLineAndPointFormatter> {

        public MyLineAndPointRenderer(XYPlot plot) {
            super(plot);
        }

        // Basically just copy the entire renderPoints implementation and add a rotation as shown below
        @Override
        protected void renderPoints(Canvas canvas, RectF plotArea, XYSeries series, int iStart, int iEnd, List<PointF> points,
                                    LineAndPointFormatter formatter) {
            if (formatter.hasVertexPaint() || formatter.hasPointLabelFormatter()) {
                final Paint vertexPaint = formatter.hasVertexPaint() ? formatter.getVertexPaint() : null;
                final boolean hasPointLabelFormatter = formatter.hasPointLabelFormatter();
                final PointLabelFormatter plf = hasPointLabelFormatter ? formatter.getPointLabelFormatter() : null;
                final PointLabeler pointLabeler = hasPointLabelFormatter ? formatter.getPointLabeler() : null;
                for(int i = iStart; i < iEnd; i++) {
                    PointF p = points.get(i);
                    if(p != null) {

                        if (vertexPaint != null) {
                            canvas.drawPoint(p.x, p.y, vertexPaint);
                        }

                        if (pointLabeler != null) {
                            // this is where we rotate the text:
                            final int canvasState = canvas.save();
                            try {
                                canvas.rotate(-90, p.x, p.y);
                                canvas.drawText(pointLabeler.getLabel(series, i),
                                        p.x + plf.hOffset, p.y + plf.vOffset, plf.getTextPaint());
                            } finally {
                                canvas.restoreToCount(canvasState);
                            }
                        }
                    }
                }
            }
        }

    }

    static class MyLineAndPointFormatter extends LineAndPointFormatter {

        // if you dont use configurator you can omit this constructor.  this example uses it
        // tho so here it is.
        public MyLineAndPointFormatter(Context context, int xmlCfgId) {
            super(context, xmlCfgId);
        }

        @Override
        public Class<? extends SeriesRenderer> getRendererClass() {
            return MyLineAndPointRenderer.class;
        }

        @Override
        public SeriesRenderer doGetRendererInstance(XYPlot plot) {
            return new MyLineAndPointRenderer(plot);
        }

    }
}

重要的东西在 MyLineAndPointRenderer 的底部。基本上你只是扩展 LineAndPointRenderer 并覆盖 renderPoints(...) 以在绘制文本标签之前旋转 canvas -90 度,然后在之后立即恢复 canvas。