Charts and plots for Android
For details on what to expect in general when updating to a new version of Androiplot, check out the versioning doc.
Unreleased.
A maintenance release focused on correctness and modernisation. Highlights:
androidx.annotation; the XML configuration engine
(formerly the separate Fig library) lives in androidplot-core.Full list:
androidx.annotation.NonNull / Nullable) across the whole public API of
androidplot-core: every reference-typed return value and parameter of every public or protected
method and constructor is annotated, so Kotlin callers see real nullable / non-null types instead
of platform types. Kotlin code may see new compile errors where it passed null to a parameter
that is now @NonNull, or used a now-@Nullable return (for example XYSeries.getX() /
getY(), Region.getMin() / getMax(), XYPlot.getDomainOrigin() / getRangeOrigin(),
Plot.getRenderer() and formatter paint getters that return null when disabled) without a null
check. A test (NullabilityAnnotationsTest) keeps the annotations complete.Redrawer occasionally never exiting (or never resuming) when finish(), pause() or
start() raced with its thread: the flag was set before the thread started, or a notify slipped
in between the thread checking its state and waiting.XYPlot.seriesToScreenY(Number) previously returned a y pixel flipped relative to
seriesToScreen(XYCoords) and screenToSeriesY (the largest series value landed on the bottom
edge of the grid). It now agrees with them, so code that compensated for the inversion should
drop the compensation.SimpleLineLabelFormatter, RenderBundle and
APTrace. None were referenced by the library, the demo app or the docs.Redrawer now tolerates plots that have been garbage
collected instead of crashing, and exits on its own once none remain.StepMode.INCREMENT_BY_VAL or INCREMENT_BY_FIT.androidPlot. attribute references a color, dimension
or integer resource. Negative integer values are now accepted as well.LineAndPointRenderer throwing IndexOutOfBoundsException on every frame for an empty
series with XOrder.ASCENDING; SeriesUtils.iBounds no longer yields a negative index for an
empty series.LineAndPointRenderer throwing on every frame when interpolation is enabled on a series with
fewer than 3 points or a null value; such series now fall back to straight line segments.GROW / SHRINK boundary modes latching the placeholder [-1, 1] bounds when the first
frame is drawn before any series data exists, which left GROW stuck at -1 and gave SHRINK an
inverted axis.XYConstraints.contains(RectRegion) comparing the region’s minimum y value against the domain
instead of its minimum x value.FillDirection.RANGE_ORIGIN fills being closed at the wrong screen coordinate; the range
origin was transformed through the domain region instead of the range region.BarOrientation.STACKED bars being measured from the bottom of the plot instead of from the
range origin, which produced incorrect stacks whenever the range lower boundary was not the origin.
Negative values now stack downward from the origin.BubbleRenderer producing infinite radii when any z-val is zero or negative, and NaN radii (no
bubble drawn) when all z-vals are equal or the series has a single bubble.Region.transform now
maps a zero-length region onto the center of the target range, and XYPlot pads a calculated axis
whose min equals max so the data is centered with a visible grid.BarFormatter.setFillPaint(null) / setBorderPaint(null) not disabling the fill / border;
the formatter shadowed the inherited paint fields so hasFillPaint() / hasLinePaint() kept
returning true and BarRenderer drew with a null Paint.NormedXYSeries returning NaN for a flat or single-point series, and drifting outside of the
[0, 1] range when the wrapped series changes after construction. Auto-calculated bounds are now
refreshed before each draw (NormedXYSeries implements PlotListener) and can be refreshed
manually via the new normalize() method.TableModel can hold. Untitled items now draw without
text, an over-full legend draws what fits, and DynamicTableModel rounds its dynamic axis up and
sizes cells by the computed row/column count so a table such as DynamicTableModel(0, 2) fills
its width. XYSeriesRenderer.getUniqueRegionFormatters() now returns a Map (a
LinkedHashMap) instead of a Hashtable.Widget.position(...) re-adding the widget to the LayoutManager on every call, which drew
the widget again on top of everything positioned before it (e.g. the graph covering the legend
and titles after plot.getGraph().position(...)).FixedTableModel wrapping immediately (or laying out nothing) for any table whose rect does
not start at the plot origin.domainTitle* and rangeTitle* size, position, anchor and visibility XML attributes
being applied to the graph widget instead of the domain and range title widgets, so e.g.
ap:domainTitleVisible="false" hid the entire graph.com.halfhp.fig:figlib. See the new
XML Configuration doc.PanZoom.State that was captured before any
pan or zoom gesture (eg. saving getState() in onSaveInstanceState and rotating the device).
getState() now snapshots the plot’s actual boundaries and modes for both axes, and applying a
state skips any axis edge it holds no mode for. A State serialized by an earlier version
still deserializes (its serialVersionUID is pinned) and restores as a no-op. XYPlot gains
public getters for its four boundary modes.SampledXYSeries crashing the render thread on its first draw: the series had no active
data until a zoom factor was applied, and had no bounds when its data was too small to produce
any sampled zoom level. ZoomEstimator also tolerates a series without bounds.ConcurrentModificationException (and silently skipped listeners) when a PlotListener
or series removes itself from the plot from within onBeforeDraw / onAfterDraw. The
listener list is now a CopyOnWriteArrayList; Plot.getListeners() returns a List.centerOnRangeOrigin(...) with BoundaryMode.FIXED, GROW or SHRINK throwing
UnsupportedOperationException on every frame; the range axis now supports the same origin
boundary modes as the domain axis.NullPointerException when a RectRegion with a null (unbounded) edge is tested for
intersection, eg. a fill region added to a LineAndPointFormatter. Null now means infinity as
documented, and the Region(v1, v2) constructor no longer swaps a null value to the wrong edge.
LineAndPointRenderer draws such a region clipped to the plot’s visible bounds.ConcurrentModificationException on the render thread when XYPlot value markers are
added or removed while the plot is drawing; the marker lists are now CopyOnWriteArrayLists.NullPointerException from SimpleXYSeries.setModel(...), resize(...) and setXY(...)
after useImplicitXVals(). setModel and resize keep the x-vals implicit, setXY sets
only the y value, and setX throws an IllegalStateException explaining why.LayerListOrganizer.moveBeneath(...) losing the moved element (and throwing
IndexOutOfBoundsException) and moveAbove(...) silently moving it to the bottom when the
reference element is not in the list; both now throw IllegalArgumentException and leave the
list unchanged.PanZoom clamping a horizontal pan to the range (y) outer limits when only minY/maxY
outer limits are set, which snapped the data away on the first drag.PanZoom pan and zoom losing precision on large-magnitude axes such as epoch milliseconds;
small drags did not move the window and zoom quantized. The value-space math is now done in
double. PanZoom.calculateZoom(RectF, float, boolean) is deprecated in favor of a RectRegion
overload.PieRenderer.setDonutSize(0, DonutMode.PIXELS) making the pie invisible; a donut size of 0
pixels now means no hole.Plot.removeSeries(series, rendererClass) returning false when a series that is not a
PlotListener was removed, and unregistering a PlotListener series (ex. SimpleXYSeries,
which relies on it for draw-time locking) while the series was still registered with another
renderer.Plot.clear() and SeriesRegistry.add()/clear() mutating the series registry without
synchronizing against the render thread, which could throw a ConcurrentModificationException
mid-render.Behavior changes for RenderMode.USE_BACKGROUND_THREAD:
redraw() requests are no longer dropped when the render thread is busy drawing. Requests
made during a render are coalesced into one additional render pass, so the latest data is
always drawn. Apps that issue redraw() faster than the plot can draw will see one extra
frame per burst compared to previous versions.redraw() call.setLinesPerRangeLabel & setLinesPerDomainLabelPlotRenderException.IN_ORDER BarRenderer mode.PanZoom state.protected.SimpleXYPlot preventing the resizing of Y_VALS_ONLY formatted series.GONE, etc.) would not resize the graph widget.This version is pickier than it’s predecessors about proper XML configuration. Where
previous versions would silently ignore illegal XML attrs, a RuntimeException will be thrown.
XYGraphWidget.screenToSeriesY(...).PieRenderer.getContainingSegment not working for very large segments.Updates to legend functionality in this version may result in changes to the display order
of legend items in some cases. A custom Comparator can be used to resolve this if necessary;
see the legend doc for implementation details.
PieChartFastXYSeriesFastLineAndPointRenderer now renders vertices for legend items.INCREMENT_BY_FIT.PanZoom support for INCREMENT_BY_FIT.SeriesRegistry to preserve state.PointLabeler.PieRenderer. Documentation has been updated to reflect these changes.BarRenderer. Documentation has been updated to reflect these changes.ScalingXYSeries which wraps other instances of XYSeries to be dynamically scaled. This is
particularly useful for creating animated intros using XYSeries data.ScalingXYSeries to create an animated intro.XYPlot.getXVal(..) and XYPlot.getYVal(...) methods have been deprecated and will be removed in 1.5.0.
XYPlot.screenToSeries(...) and XYPlot.seriesToScreen(...) should be used instead.XYGraphWidget.setCursorPosition(float, float). Cursor position values are expressed in screen coordinates;
you can convert between screen and series values using XYPlot.screenToSeries(...) and XYPlot.seriesToScreen(...).NormedXYSeries usage to present dual range scales.PanZoom from working properly on plots with an undefined outer limit.Formatter.getLegendIconEnabled() and Formatter.setLegendIconEnabled(boolean), used to enable / disable drawing legend items for individual
series / formatter pairs.XYGraphWidget.Edge.NONE to be used with XYGraphWidget.setLineLabelEdges(Edge...) to disable all edges.Pie chart has been updated with new methods and format attributes to improve segment selection and highlighting functionality:
offset, radialInset, innerInset and outerInset properties to SegmentFormatter.Plot.getListeners() method.This is a factor of several core elements of the Androidplot lib. The general theme was to make class and method names more intuitive and to make xml styling more powerful.