如何旋转图形上的文本值
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。
我有一个使用 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。