This commit is contained in:
coco
2026-07-03 15:56:07 +08:00
commit caef23209c
5767 changed files with 1004268 additions and 0 deletions
+154
View File
@@ -0,0 +1,154 @@
# Advanced XY Series Types
Androidplot offers several specialized implementations of XYSeries providing various performance
or usability enhancements.
## EditableXYSeries
Enhances the standard XYSeries interface with edit methods.
## FixedSizeEditableXYSeries
An implementation of EditableXYSeries that supports modifying x/y values and has been optimized
for speed. FixedSizeEditableXYSeries is optimized for data whose samples may frequently change but
whose absolute size doesn't change, such as an ECG (ring buffer) or an animated plot.
## FastXYSeries
By default, Androidplot iterates over every element in each series every render cycle to
determine it's current min/max values. This is necessary in order to support dynamic plots where
data can change at any moment and invalidate the previously calculated min/max vals.
FastXYSeries allows the developer to provide a more efficient algorithm to obtain min/max XVals
and avoid the high overhead of this iteration. For example, if you know your XVals will always
be in strict ascending order then the first and last XVal of the series will always contain the
min/max XVals respectively.
XYSeries implementation supports a min/max algorithm implementation that is more efficient than doing
a comparison on each point of the series via iteration. It's a good idea to implement this interface
if your series will contain more than about 500 points
## OrderedXYSeries
If the XVals of your series are in ascending order, implementing this interface provides a hint to
the series renderer that allows it to avoid iterating over points that are outside the screen's
visible domain. For larger data sets, implementing this interface can mean the difference between
smooth animations and freezing.
## ScalingXYSeries
Wraps any other instance of XYSeries and provides a simple interface for dynamically
scaling x and/or y values. A popular use case for dynamic scaling is to create an animated intro
for your XYSeries where yVals increase (or decrease in the case of negative values) from 0 towards
their original value. The [AnimatedXYPlotActivity](../demoapp/src/main/java/com/androidplot/demos/AnimatedXYPlotActivity.java)
in the DemoApp is one example on how this can be accomplished.
## SampledXYSeries
SampledXYSeries is meant for use with extremely large datasets. Given a series, multiple sampled
series instances at stepped resolutions are generated for faster rendering.
Basic usage:
```java
XYSeries series = ... // instantiate an XYSeries however you want here
// wrap our series in a SampledXYSeries with a threshold of 1000:
SampledXYSeries sampledSeries =
new SampledXYSeries(series, OrderedXYSeries.XOrder.ASCENDING, 2,100);
// add the SampledXYSeries instance to the plot:
plot.addSeries(sampledSeries, formatter);
```
SampledXYSeries is meant to be used in conjunction with ZoomEstimator, which enables a plot to
automatically render using the resolution appropriate for the current screen boundaries, allowing
pan / zoom operations to perform with little or no degradation as series size increases.
To enable ZoomEstimator:
```java
// enable autoselect of sampling level based on visible boundaries:
plot.getRegistry().setEstimator(new ZoomEstimator());
```
[The Touch Zoom Example source code](../demoapp/src/main/java/com/androidplot/demos/TouchZoomExampleActivity.java) provides a functional reference implementation.
If you want to take advantage of the performance benefits of sampling but don't need pan/zoom support
check out the Sampling section below.
## NormedXYSeries
A convenience wrapper to simplify normalizing XYSeries data in the range of 0 to 1. Usage is straightforward:
```java
XYSeries normedSeries = new NormedXYSeries(series);
```
which is equivalent to:
```java
XYSeries normedSeries = new NormedXYSeries(series, null, new Norm(null, 0, false));
```
The first null argument pertains to the normalization being applied to the x axis values. While normalization
can certainly be applied on this axis, it's typically unused, which is why the single argument constructor
defaults to a null value here.
Both examples auto normalize the passed in series, maximizing the resolution of the
output result. Sometimes however, it's desirable to control the output resolution for the purpose
of visually shifting the result up or down in the graph. This can be done by using the `NormedXYSeries.Norm`
constructor's second and third arguments: `offset` and `useOffsetCompression`.
##### offset
This value is added to the original normalized value to effectively shift the series data up or down
within the normalized range. If the offset is large enough and offset compression is not used, this
can cause normalized values to exceed the norm range of 0 to 1.
##### useOffsetCompression
If you want to shift a normalized series up or down in the graph but do not want the shift to potentially
move values offscreen, you can set `useOffsetComression` to true. This tells `NormedXYSeries` to shrink
the scale of the associated series relative to the offset being used to ensure that normed values
stay within the range of 0 to 1. If set to true and you supply an offset <0 or >1, an
`IllegalArgumentException` will be thrown.
We've not talked about the first argument to the `NormedXYSeries.Norm` constructor: `minMax`. This is an
optional optimization value that reflects min/max values in the series data being passed in. If they
are known, you can pass these in to speed up normalization, otherwise just pass in null and they will
be auto calculated.
IMPORTANT: If your series data is dynamic and it's min/max values change at runtime, you'll need to invoke
`NormedXYSeries.normalize(Norm, Norm)` immediately following each change in order to maintain accuracy.
There's a [reference implementation](../demoapp/src/main/java/com/androidplot/demos/DualScaleActivity.java)
of a dual scale plot that demonstrates NormedXYSeries usage in the DemoApp.
# Sampling
Series size is generally the biggest factor when it comes to rendering performance. If you're plotting
very large datasets, its generally a good idea to sample the data to improve performance.
For example, if you have an XYSeries that consists of 10,000 points, we can sample that data into
the 200 points that most accurately represent the profile of the original series:
```java
// An instance of any implementation of XYSeries. Assume it's size is 10,000
XYSeries originalSeries = ...;
// Sampled series of size 200:
EditableXYSeries sampledSeries = new FixedSizeEditableXYSeries(
originalSeries.getTitle(), 200);
// an instance of an implementation of Sampler:
Sampler sampler = ...
// do the actual sampling:
new LTTBSampler().run(originalSeries, sampledSeries);
```
Currently LTTBSampler is the only available implementation.
# Storing series data in onSaveInstanceState
If your series data requires a non trivial amount of preprocessing (subsampling etc.) or your data comes
from a dynamic source, you'll likely want to persist your series data when your Activity saves its
instance state. There are a few caveats to be aware of:
* You can only persist about 1mb worth of data at a time so if your series data is much larger than that
you'll need to find a creative solution to the problem
* Due to [quirks in the way Android persists data](http://stackoverflow.com/questions/12300886/linkedlist-put-into-intent-extra-gets-recast-to-arraylist-when-retrieving-in-nex)
`XYSeries` implementations such as `SimpleXYSeries` that use `LinkedList` instances to store data cannot be serialized directly.
* Formatters generally cannot be persisted as they typically contain instances of `Paint` that cannot be serialized directly..
Due to these limitations we suggest storing `XYSeries` data into an array or `ArrayList` and using that to
instantiate your `XYSeries`. The DemoApp's [Time Series Example](../demoapp/src/main/java/com/androidplot/demos/TimeSeriesActivity.java)
contains a full source example.
+598
View File
@@ -0,0 +1,598 @@
_This documentation is auto generated from [attrs.xml](../androidplot-core/src/main/res/values/attrs.xml)._
# Androidplot XML Attributes
Attributes are broken down by element followed by either their type or list of accepted values.
Supported Elements:
* [Plot](#Plot)
* [XYPlot](#XYPlot)
* [PieChart](#PieChart)
## Plot
Plot's attrs are available in all Plot types.
### markupEnabled
__boolean__
### renderMode
* use_background_thread
* use_main_thread
### marginTop
__dimension__
### marginBottom
__dimension__
### marginLeft
__dimension__
### marginRight
__dimension__
### paddingTop
__dimension__
### paddingBottom
__dimension__
### paddingLeft
__dimension__
### paddingRight
__dimension__
### title
__dimension__
### titleTextSize
__string__
### titleTextColor
__dimension__
### backgroundColor
__color__
### borderColor
__color__
### borderThickness
__color__
## XYPlot
XML attributes for the [XYPlot](xyplot.md) class.
### previewMode
TODO
### domainStepMode
* subdivide
* increment_by_val
* increment_by_pixels
### domainStep
__dimension|float|integer__
### rangeStepMode
* subdivide
* increment_by_val
* increment_by_pixels
### rangeStep
__dimension|float|integer__
### domainTitle
__string__
### domainTitleTextColor
__color__
### domainTitleTextSize
__dimension__
### domainTitleHeightMode
* absolute
* relative
* fill
### domainTitleWidthMode
* absolute
* relative
* fill
### domainTitleHeight
__dimension|float|integer__
### domainTitleWidth
__dimension|float|integer__
### domainTitleHorizontalPositioning
* absolute_from_left
* absolute_from_right
* absolute_from_center
* relative_from_left
* relative_from_right
* relative_from_center
`HorizontalPositioning` component of the `HorizontalPosition` of the `TextLabelWidget`
that displays the domain title.
See [Positioning Widgets](plot_composition.md#positioning-widgets) documentation.
### domainTitleVerticalPositioning
* absolute_from_top
* absolute_from_bottom
* absolute_from_center
* relative_from_top
* relative_from_bottom
* relative_from_center
`VerticalPositioning` component of the `VerticalPosition` of the `TextLabelWidget`
that displays the domain title.
See [Positioning Widgets](plot_composition.md#positioning-widgets) documentation.
### domainTitleHorizontalPosition
__dimension|float|integer__
`float` value component of the `HorizontalPosition` of the `TextLabelWidget`
that displays the domain title.
See [Positioning Widgets](plot_composition.md#positioning-widgets) documentation.
### domainTitleVerticalPosition
__dimension|float|integer__
`float` value component of the `VerticalPosition` of the `TextLabelWidget`
that displays the domain title.
See [Positioning Widgets](plot_composition.md#positioning-widgets) documentation.
### domainTitleAnchor
* top_middle
* left_top
* left_middle
* left_bottom
* right_top
* right_middle
* right_bottom
* bottom_middle
* center
### domainTitleVisible
__boolean__
### rangeTitle
__string__
### rangeTitleTextColor
__color__
### rangeTitleTextSize
__dimension__
### rangeTitleHeightMode
* absolute
* relative
* fill
### rangeTitleWidthMode
* absolute
* relative
* fill
### rangeTitleHeight
__dimension|float|integer__
### rangeTitleWidth
__dimension|float|integer__
### rangeTitleHorizontalPositioning
* absolute_from_left
* absolute_from_right
* absolute_from_center
* relative_from_left
* relative_from_right
* relative_from_center
### rangeTitleVerticalPositioning
* absolute_from_top
* absolute_from_bottom
* absolute_from_center
* relative_from_top
* relative_from_bottom
* relative_from_center
### rangeTitleHorizontalPosition
__dimension|float|integer__
### rangeTitleVerticalPosition
__dimension|float|integer__
### rangeTitleAnchor
* top_middle
* left_top
* left_middle
* left_bottom
* right_top
* right_middle
* right_bottom
* bottom_middle
* center
### rangeTitleVisible
__boolean__
### drawGridOnTop
__boolean__
<br/>
(default is false) When set to true, grid lines are drawn on top of rendered series data
instead of underneath.
### graphHeightMode
* absolute
* relative
* fill
### graphWidthMode
* absolute
* relative
* fill
### graphHeight
__dimension|float|integer__
### graphWidth
__dimension|float|integer__
### graphRotation
* none
* ninety_degrees
* negative_ninety_degrees
* one_hundred_eighty_degrees
### graphHorizontalPositioning
* absolute_from_left
* absolute_from_right
* absolute_from_center
* relative_from_left
* relative_from_right
* relative_from_center
`HorizontalPositioning` component of the `HorizontalPosition` of the `XYGraphWidget`.
See [Positioning Widgets](plot_composition.md#positioning-widgets) documentation.
### graphVerticalPositioning
* absolute_from_top
* absolute_from_bottom
* absolute_from_center
* relative_from_top
* relative_from_bottom
* relative_from_center
`VerticalPositioning` component of the `VerticalPosition` of the `XYGraphWidget`.
See [Positioning Widgets](plot_composition.md#positioning-widgets) documentation.
### graphHorizontalPosition
__dimension|float|integer__
`float` value component of the `HorizontalPosition` of the `XYGraphWidget`.
See [Positioning Widgets](plot_composition.md#positioning-widgets) documentation.
### graphVerticalPosition
__dimension|float|integer__
`float` value component of the `VerticalPosition` of the `XYGraphWidget`.
See [Positioning Widgets](plot_composition.md#positioning-widgets) documentation.
### graphAnchor
* top_middle
* left_top
* left_middle
* left_bottom
* right_top
* right_middle
* right_bottom
* bottom_middle
* center
### graphVisible
__boolean__
### graphMarginTop
__dimension__
### graphMarginBottom
__dimension__
### graphMarginLeft
__dimension__
### graphMarginRight
__dimension__
### graphPaddingTop
__dimension__
### graphPaddingBottom
__dimension__
### graphPaddingLeft
__dimension__
### graphPaddinRight
__dimension__
### gridClippingEnabled
__boolean__
### gridInsetTop
__dimension__
### gridInsetBottom
__dimension__
### gridInsetLeft
__dimension__
### gridInsetRight
__dimension__
### lineLabelInsetTop
__dimension__
<br/>
Top edge of a rectangle relative to the XYGraphWidget
border upon which line labels will be anchored.
### lineLabelInsetBottom
__dimension__
<br/>
Bottom edge of a rectangle relative to the XYGraphWidget
border upon which line labels will be anchored.
### lineLabelInsetLeft
__dimension__
<br/>
Left edge of a rectangle relative to the XYGraphWidget
border upon which line labels will be anchored.
### lineLabelInsetRight
__dimension__
<br/>
Right edge of a rectangle relative to the XYGraphWidget
border upon which line labels will be anchored.
### lineLabels
* top
* bottom
* left
* right
Enable line labels on one or more edge of the graph. For example, to enable labels on the left
and bottom edges:
```
ap:lineLabels="left|bottom"
```
### lineLabelAlignTop
* left
* center
* right
Text alignment of line labels drawn on top edge of the plot. This alignment is applied relative
to the line label insets defined by `lineLabelInsetTop`.
### lineLabelAlignBottom
* left
* center
* right
Text alignment of line labels drawn on bottom edge of the plot. This alignment is applied relative
to the line label insets defined by `lineLabelInsetBottom`.
### lineLabelAlignLeft
* left
* center
* right
Text alignment of line labels drawn on left edge of the plot. This alignment is applied relative
to the line label insets defined by `lineLabelInsetLeft`.
### lineLabelAlignRight
* left
* center
* right
Text alignment of line labels drawn on right edge of the plot. This alignment is applied relative
to the line label insets defined by `lineLabelInsetRight`.
### lineLabelRotationTop
__float__
<br/>
Angle at which line labels on the plot's top edge are drawn.
### lineLabelRotationBottom
__float__
<br/>
Angle at which line labels on the plot's bottom edge are drawn.
### lineLabelRotationLet
__float__
<br/>
Angle at which line labels on the plot's left edge are drawn.
### lineLabelRotationRight
__float__
<br/>
Angle at which line labels on the plot's right edge are drawn.
### domainLineThickness
__dimension__
### rangeLineThickness
__dimension__
### domainLineColor
__color__
### rangeLineColor
__color__
### domainOriginLineThickness
__dimension__
### rangeOriginLineThickness
__dimension__
### domainOriginLineColor
__color__
### rangeOriginLineColor
__color__
### lineLabelTextSizeTop
__dimension__
### lineLabelTextSizeBottom
__dimension__
### lineLabelTextSizeLeft
__dimension__
### lineLabelTextSizeRight
__dimension__
### lineLabelTextColorTop
__color__
### lineLabelTextColorBottom
__color__
### lineLabelTextColorLeft
__color__
### lineLabelTextColorRight
__color__
### lineExtensionTop
__dimension__
### lineExtensionBottom
__dimension__
### lineExtensionLeft
__dimension__
### lineExtensionRight
__dimension__
### gridBackgroundColor
__color__
<br/>
background color of the grid portion of the XYGraphWidget
### graphBackgroundColor
__color__
<br/>
background color of the XYGraphWidget
### legendHeightMode
* absolute
* relative
* fill
### legendWidthMode
* absolute
* relative
* fill
### legendHeight
__dimension|float|integer__
### legendWidth
__dimension|float|integer__
### legendHorizontalPositioning
`HorizontalPositioning` component of the `HorizontalPosition` of the `XYLegendWidget`.
See [Positioning Widgets](plot_composition.md#positioning-widgets) documentation.
* absolute_from_left
* absolute_from_right
* absolute_from_center
* relative_from_left
* relative_from_right
* relative_from_center
### legendVerticalPositioning
`VerticalPositioning` component of the `VerticalPosition` of the `XYLegendWidget`.
See [Positioning Widgets](plot_composition.md#positioning-widgets) documentation.
* absolute_from_top
* absolute_from_bottom
* absolute_from_center
* relative_from_top
* relative_from_bottom
* relative_from_center
### legendHorizontalPosition
__dimension|float|integer__
`float` value component of the `HorizontalPosition` of the `XYLegendWidget`.
See [Positioning Widgets](plot_composition.md#positioning-widgets) documentation.
### legendVerticalPosition
__dimension|float|integer__
`float` value component of the `VerticalPosition` of the `XYLegendWidget`.
See [Positioning Widgets](plot_composition.md#positioning-widgets) documentation.
### legendAnchor
* top_middle
* left_top
* left_middle
* left_bottom
* right_top
* right_middle
* right_bottom
* bottom_middle
* center
### legendTextSize
__dimension__
### legendTextColor
__color__
### legendIconHeightMode
* absolute
* relative
* fill
### legendIconWidthMode
* absolute
* relative
* fill
### legendIconHeight
__dimension|float|integer__
### legendIconWidth
__dimension|float|integer__
### legendVisible
__boolean__
### pieBorderThickness
__dimension__
<br/>
Determines how far beyond the graph's edge each domain grid line will extend.
### pieBorderThickness
__dimension__
<br/>
Determines how far beyond the graph's edge each range grid line will extend.
## PieChart
TODO
### pieBorderColor
__color__
### pieBorderThickness
__dimension__
+132
View File
@@ -0,0 +1,132 @@
# Bar Charts
Bar Charts are drawn with `BarRenderer` by converting xy values of an `XYSeries` into
vertical bars. The height of the vertical bar is calculated from the yVal and the base
of the bar is calculated from the plot's range origin value.
![image](images/screens/bar_horiz.png)
# Basic Usage
Rendering an `XYSeries` as a bar chart is as simple as adding the series to an `XYPlot` with an
instance of `BarFormatter`:
```java
// create a bar formatter with a red fill color and a white outline:
BarFormatter bf = new BarFormatter(Color.RED, Color.WHITE);
plot.addSeries(series, bf);
```
# BarRenderer
Unlike most other renderers, much of the visual configuration of a bar chart is configured through it's `BarRenderer`.
`BarRenderer` provides methods for setting how the width of each bar should be calculated, the space between each `Bar`
and what style of visual grouping to use.
##### Getting the BarRenderer Instance
Many of the topics below require access to the `BarRenderer` instance to be set. Each instance of
`XYPlot` contains it's own unique `Renderer` instances. To retrieve the `BarRenderer`
instance from an `XYPlot`:
```java
BarRenderer renderer = plot.getRenderer(BarRenderer.class);
```
# BarGroup
A `BarGroup` is automatically generated to group values from one or more series by their index values.
All `Bar` instances belong to a `BarGroup`, even if there is only a single `XYSeries` that exists.
A common use case of bar charts is to represent a group of two or more values for a given interval as
individual bars, for example the number of wins and losses for a baseball team for every month over the course
of a year.
To model this in Androidplot, create two instances of `XYSeries`; one for wins and one for losses, each
with exactly 12 elements (one for each day of the month):
```java
XYSeries wins = new SimpleXYSeries(SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "wins", 3, 4, 5, 3, 2, 3, 5, 6, 2, 1, 3, 1);
XYSeries losses = new SimpleXYSeries(SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "losses", 0, 1, 1, 0, 1, 0, 0, 0, 2, 1, 0, 1);
```
Each series is then added to the plot with it's own formatter:
```java
// draw wins bars with a green fill:
BarFormatter winsFormatter = new BarFormatter(Color.GREEN, Color.BLACK);
plot.addSeries(wins, winsFormatter);
// draw losses bars with a red fill:
BarFormatter lossesFormatter = new BarFormatter(Color.RED, Color.BLACK);
plot.addSeries(losses, lossesFormatter);
```
The pairs of wins/losses bars are then drawn side-by-side for each of the 12 indexes. Androidplot
knows to do this because `BarRenderer` (the renderer used to draw series associated with a `BarFormatter`)
extends [GroupRenderer](grouprenderer.md).
# BarRenderer Styles
`BarRenderer` provides three grouping styles that may be used when rendering two or more XYSeries:
`OVERLAID`, `STACKED`, `SIDE_BY_SIDE`
##### OVERLAID (default)
Bars in the same grouping are overlaid on each-other, the bars being drawn by yVal in descending order.
![image](images/bargroup_overlaid.png)
To use:
```java
barRenderer.setBarOrientation(BarRenderer.BarOrientation.OVERLAID);
```
##### STACKED
Bars in the same group are stacked on top of each-other. Limitations:
* Range Origin must be set to 0.
* All `XYSeries` must contain no negative values.
![image](images/bargroup_stacked.png)
To use:
```java
barRenderer.setBarOrientation(BarRenderer.BarOrientation.STACKED);
```
##### SIDE_BY_SIDE
Bars in the same group are drawn next to one another.
![image](images/bargroup_side_by_side.png)
To use:
```java
barRenderer.setBarOrientation(BarRenderer.BarOrientation.SIDE_BY_SIDE);
```
By default there is no spacing between bars in the same `BarGroup` in this mode. You can add spacing
by setting a left and right margin on your BarFormatter instances:
```java
barFormatter.setMarginLeft(PixelUtils.dpToPix(1));
barFormatter.setMarginRight(PixelUtils.dpToPix(1));
```
# BarGroup Widths & Spacing
When configuring BarGroup widths and spacing, there are two mutually exclusive methods that can be used;
`FIXED_WIDTH` and `FIXED_GAP`.
![image](images/bargroup_spacing.png)
##### FIXED_WIDTH
The exact size of the `BarGroup` is specified in pixels and the space between each `BarGroup`
is dynamically calculated based on that size.
```java
barRenderer.setBarGroupWidth(BarRenderer.BarGroupWidthMode.FIXED_WIDTH, PixelUtils.dpToPix(25));
```
##### FIXED_GAP
The exact size of the "gap" between each `BarGroup` is specified in pixels and the size of each `BarGroup`
is dynamically calculated based on that spacing.
```java
barRenderer.setBarGroupWidth(BarRenderer.BarGroupWidthMode.FIXED_GAP, PixelUtils.dpToPix(5));
```
# Example
See the DemoApp's [bar chart example source](../demoapp/src/main/java/com/androidplot/demos/BarPlotExampleActivity.java).
+53
View File
@@ -0,0 +1,53 @@
# Bubble Charts
A `BubbleChart` is a two dimensional representation of three dimensional data
on an `XYPlot`, where the xy values are drawn as a circle with a radius representing the zVal.
![image](images/screens/bubble_horiz.png)
# Basic Usage
The first step in creating a `BubbleChart` is to define the data to be plotted. We'll start
by creating an instance of `BubbleSeries`:
Using the implicit iVal for x:
```java
BubbleSeries series1 = new BubbleSeries(
Arrays.asList(new Number[]{3, 5, 2, 3, 6}), // xCoordinate
Arrays.asList(new Number[]{1, 5, 2, 2, 3}), "s1"); // yCoordinate
```
Or it's equivalent four argument counterpart:
```java
BubbleSeries bubbleSeries = new BubbleSeries(
Arrays.asList(new Number[]{0, 1, 2, 3, 4}), // xCoordinate
Arrays.asList(new Number[]{3, 5, 2, 3, 6}), // yCoordinate
Arrays.asList(new Number[]{1, 5, 2, 2, 3}), // zVal (corresponds to radius)
"s1");
```
Next, create a `Formatter` defining the fill and outline colors of the bubbles:
```java
// draw bubbles with a green fill and white outline:
BubbleFormatter formatter = new BubbleFormatter(Color.GREEN, Color.WHITE)
```
Finally, add the `BubbleSeries` to our plot as you would any other `XYSeries` instance:
```java
plot.addSeries(bubbleSeries, formatter);
```
# BubbleScaleMode
By default, `BubbleRenderer` scales each rendered bubble radius using the square root of it's corresponding
zVal, preventing apparent size differences in each bubble radius from being visually misleading. See: https://en.wikipedia.org/wiki/Bubble_chart#Choosing_bubble_sizes_correctly
If you'd prefer to use a linear scale:
```java
plot.getRenderer(BubbleRenderer.class).setBubbleScaleMode(BubbleRenderer.BubbleScaleMode.LINEAR);
```
# Example
Check out the [bubble chart example source](../demoapp/src/main/java/com/androidplot/demos/BubbleChartActivity.java) for a full source example of a bubble chart.
+77
View File
@@ -0,0 +1,77 @@
# Candlestick Charts
Candlestick charts are a special kind of `XYPlot` usually used for rendering financial information.
Each "candlestick" is composed of 4 values: open, close, high and low.
![image](images/screens/candlestick_horiz.png)
Androidplot's implementation of candlestick charts uses four separate `XYSeries` instances to represent
the candlestick values; each series corresponds to one of the four values that composes each "candlestick".
For example:
```java
Number[] highVals = new Number[] {12, 10, 15, 8, 7};
Number[] lowVals = new Number[] {3, 1, 5, 0, 2};
Number[] openVals = new Number[] {5, 2, 7, 5, 3};
Number[] closeVals = new Number[] {7, 9, 6, 0, 4};
```
Using the values above, the first candlestick model would be `{high=12, low=3, open=5, close=7}`,
the second would be `{high=10, low=1, open=2, close=7}` and so on.
There are a few constraints that must be satisfied before we can create XYSeries from the data above and add them to Androidplot:
* Each series must be of the same size.
* Each series must have the same x(i); getX(i) must return the same value for each series.
* There must be exactly four series, added in the precise order: high, low, open, close.
* The series data must make sense; each candlestick's high must be equal to or greater than all other
values in the candlestick, and each candlestick's low must be less than or equal to all other values.
* Each candlestick series belonging to the same candlestick chart must use the same `CandlestickFormatter`
instance when being added to the XYPlot.
While it's possible to satisfy these constraints by creating each of the four `XYSeries` required by a
candlestick chart, adding them in order using the same formatter, many projects will find it simpler
to interact with the data as though it were a single series of candlestick values. To makr this easier,
two convenience classes: `CandlestickSeries` and `CandlestickMaker`. Here's how they are used together to add a candlestick chart to an `XYPlot`:
```java
CandlestickSeries candlestickSeries = new CandlestickSeries(
new CandlestickSeries.Item(1, 10, 2, 9),
new CandlestickSeries.Item(4, 18, 6, 5),
new CandlestickSeries.Item(3, 11, 5, 10),
new CandlestickSeries.Item(2, 17, 2, 15),
new CandlestickSeries.Item(6, 11, 11, 7),
new CandlestickSeries.Item(8, 16, 10, 15));
CandlestickFormatter formatter = new CandlestickFormatter();
// add the candlestick series data to the plot:
CandlestickMaker.make(plot, formatter, candlestickSeries);
```
It's necessary to use `CandlestickMaker` to add the `CandlestickSeries` (via "make") to the
`XYPlot` as `CandlestickSeries` does not and cannot implement the `XYSeries` interface.
[A full source example is available here](../demoapp/src/main/java/com/androidplot/demos/CandlestickChartActivity.java).
+56
View File
@@ -0,0 +1,56 @@
# Contributing Soure Code
Contributions are always welcome! If you've got an idea for a specific feature and would like feedback
on implementation or if you want to contribute but don't know where to start, feel free to [
start a topic in the Forums](https://groups.google.com/forum/#!forum/androidplot). If you already have
a feature or bugfix to submit, take a look at the code style notes below and then submit a Pull Request.
## Code Style
For familiarity and simplicity, Androidplot uses [Android's code style guide](https://source.android.com/source/code-style.html).
In particular:
* Indentations are 4 spaces each - no tab characters.
* Braces do not go on their own line; they go on the same line as the code before them.
* Line length should be a max of 100 characters.
* Don't catch generic Exception and don't ignore caught exceptions.
* Classes and methods should be documented with standard Javadoc comments.
## Pull Requests
If you have code changes you'd like to share with the project, issue a [Pull Request](https://help.github.com/articles/about-pull-requests/) against the master
branch of Androidplot. Please make sure that:
* Difficult to follow logical statements, etc. are reasonably documented.
* Commented out lines, unused methods / dead code blocks have been removed.
* Unit Tests have have been refactored appropriately and all pass.
* A clear description of the change is included in the Pull Request.
Adding Unit Tests for new methods / functionality is highly encouraged.
# Git Newcomers
If you're new to Git, this section will show you the basics of checking out a local copy of the project and building it.
## Clone
Androidplot is hosted on Github. If you've got a git client installed, you can either
[fork](https://help.github.com/articles/fork-a-repo/) the project into your own
github account, or you can clone the project to your workstation using this command:
```
git clone https://github.com/halfhp/androidplot.git
```
You can also [download an ZIP](https://github.com/halfhp/androidplot/archive/master.zip) of the master branch of Androidplot.
## Build
Androidplot is a Gradle project, which makes building incredibly easy. To perform a full build (runs tests,
generates javadocs and builds the library and demo app) from the command line:
```
./gradlew test assemble
```
You can also of course use the IDE of your choice instead. [Android Studio](https://developer.android.com/studio/index.html) is highly recommended.
## Useful Links
If you're new to Git these links will get you started:
* [Setting up Git](https://help.github.com/articles/set-up-git/) Start here if you don't have a Git client installed.
* [Pro Git (free eBook)](https://git-scm.com/book/en/v2) - _The_ book to read when it comes to learning Git
* [Git Reference](https://git-scm.com/docs) - Good set of links for general reference
+101
View File
@@ -0,0 +1,101 @@
# Custom Renderers
Androidplot renderers can be extended with extra functionality in the form of custom renderers.
For the example below we'll look at creating a custom implementation of BarRenderer that will
draw bars with rounded edges. While this example demonstrates an `XYPlot`, the steps apply to
`PieChart` etc. as well.
## Create the Formatter
The first step to creating a custom renderer is defining it's `Formatter`. Aside from providing
the visual configuration used by the `Renderer` to draw series data, it's also used by Androidplot
to map a series to a specific renderer type and if necessary, obtain a new instance of that renderer.
Here's the most basic implementation:
```java
class RoundedBarFormatter extends BarFormatter {
{
// for now we'll hardcode some formatting values.
// a real implementation would probably provide a constructor instead
getBorderPaint().setColor(Color.WHITE);
getFillPaint().setColor(Color.RED);
}
@Override
public Class<RoundedBarRenderer> getRendererClass() {
return RoundedBarRenderer.class;
}
@Override
public RoundedBarRenderer doGetRendererInstance(XYPlot xyPlot) {
return new RoundedBarRenderer(xyPlot);
}
}
```
## Create the Renderer
All we want to do here is tweak the way `BarRenderer` behaves a little so we'll use it as our base class:
```java
class RoundedBarRenderer extends BarRenderer<RoundedBarFormatter> {
public RoundedBarRenderer(XYPlot plot) {
super(plot);
}
@Override
protected void drawBar(Canvas canvas, Bar<RoundedBarFormatter> bar, RectF rect) {
// TODO this is where we'll add our custom behavior
}
}
```
In the case of our RoundedBarFormatter, the behavior we need to change all exits within the
`drawBar(Canvas, Bar, RectF)` method. The existing implementation uses `Canvas.drawRect(...)` to
draw bars. We could use a stroke with rounded edges, but this would cause the edges on both the top
and the bottom to be rounded, which won't look right. We'll draw using a path instead:
```java
@Override
protected void drawBar(Canvas canvas, Bar<RoundedBarFormatter> bar, RectF rect) {
// skip nulls:
if(bar.getY() == null) {
return;
}
RoundedBarFormatter formatter = getFormatter(bar.i, bar.series);
if(formatter == null) {
formatter = bar.formatter;
}
// don't need to draw if the bar lacks height or width:
if (rect.height() > 0 && rect.width() > 0) {
final Path path = new Path();
final float arcHeightPx = 20;
final float adjustedTop = rect.top + arcHeightPx;
final RectF cap = new RectF(rect.left, rect.top, rect.right,rect.top + 2 * arcHeightPx);
// start at bottom-left and move clock-wise around the shape:
path.moveTo(rect.left, rect.bottom);
path.lineTo(rect.left, adjustedTop);
path.arcTo(cap, 180, 180, false);
path.lineTo(rect.right, rect.bottom);
path.close();
canvas.drawPath(path, formatter.getFillPaint());
canvas.drawPath(path, formatter.getBorderPaint());
}
}
```
## Use It
Using the new custom renderer is as easy as adding a series to a plot using `RoundedBarFormatter`:
```java
plot.addSeries(series1, new RoundedBarFormatter(Color.RED));
plot.addSeries(series2, new RoundedBarFormatter(Color.BLUE));
```
Here's what this would look like used in the demo app's `SimpleXYPlotActivity`:
![image](images/screens/rounded_bar_renderer.png)
+88
View File
@@ -0,0 +1,88 @@
# Plotting Dynamic Data
One of Androidplot's core focuses is on plotting dynamically changing data in real-time. Special care has
been put into the design of the library to provide solutions for various scenarios and synchronization concerns.
# Background Rendering
By default, Androidplot does all of it's rendering on the UI thread. While this is fine for most applications,
it can cause performance issues when rendering larger datasets or when continuously redrawing dynamic data.
To resolve this issue, Androidplot provides a background rendering mode that can be enabled programmatically in
your Java source or via XML:
**Programmatically:**
```java
plot.setRenderMode(Plot.RenderMode.USE_BACKGROUND_THREAD);
```
**XML:**
```xml
ap:renderMode="use_background_thread"
```
In general, if your plot is continuously redrawing the plot, you should use background rendering.
#Rendering Dynamic Data
There are two general approaches to dynamically rendering data: event driven and render loops. Each has
pros and cons and often times, the application might force you to use one approach over the other, however
90% of the time using a render loop is the better approach.
In both cases the redraw is triggered by invoking `Plot.redraw().`
## Render Loops
A render loop is basically a continuous loop of calls to a render routine. This loop might
include logic for updating the data being plotted, or it might simply focus on maintaining a stable refresh rate.
Androidplot provides a convenience utility, Redrawer which provides a basic implementation of a render loop
running on a fixed frequency. Check out the [ECG demo source](../demoapp/src/main/java/com/androidplot/demos/ECGExample.java) for a working example.
## Event Driven Redraws
Sometimes it's more efficient to only redraw the plot as a result of an event such as a GPS update, button click, etc.
Event driven updates are as simple as invoking `Plot.redraw()` from the callback handling the event of interest,
after the data being plotted has been updated. While it's possible for the event triggering the redraw
to fire at a faster rate than the plot is capable of redrawing, no special care needs to be taken as Androidplot
ignores subsequent invocations of redraw() when a previous invocation is already active.
# Synchronization
A major challenge of plotting dynamic data is the need to render an accurate representation of the data
as it existed at a specific point in time. This is not a trivial problem and the right solution depends
greatly on the specific details of the project. Presented here are a list of items to keep in mind
along with general best practices and approaches to solving more common problems.
The most common scenario is when your data model is being dynamically updated by a background thread.
Regardless of whether or not you use background rendering here, invoking plot.redraw() is asynchronous
and your background thread can easily loop around and start altering values while your plot is half way through
rendering the previous set of values. The result of a race condition is indeterminate; if the size of your data
changed the app could crash from an IndexOutOfBoundsException, or a bogus representation of your data might
be rendered to the display.
To prevent the race condition, changes to the data model must be synchronized with Androidplot's
rendering loop. A PlotListener can be used to detect when a Plot begins and ends a redraw and a read-write
can be used to protect the model (typically any series data being drawn):
```java
plot.addListener(new PlotListener() {
@Override
public void onBeforeDraw(Plot source, Canvas canvas) {
// write-lock each active series for writes
}
@Override
public void onAfterDraw(Plot source, Canvas canvas) {
// unlock any locked series
}
});
```
Note that if your custom series implements the PlotListener interface, it's onBeforeDraw and onAfterDraw
methods will be automatically called when the series is rendered, without the need for adding it
as a listener to the plot. SimpleXYSeries provides a reference implementation of this synchronization
technique.
In-depth usage of thread synchronization and read-write locks is beyond scope for this doc but you can
check out the source code of the SimpleXYSeries class for a functional example of ReentrantReadWriteLock
being used to synchronize an XYSeries implementation.
# Examples
Androidplot provides a few full-source examples of how to dynamically plot data as demonstrated
in the [demo app](https://play.google.com/store/apps/details?id=com.androidplot.demos&hl=en).
* [Plotting Realtime Orientation Sensor Data](../demoapp/src/main/java/com/androidplot/demos/OrientationSensorExampleActivity.java)
* [An ECG Example](../demoapp/src/main/java/com/androidplot/demos/ECGExample.java)
* [A simple dynamic XYPlot](../demoapp/src/main/java/com/androidplot/demos/DynamicXYPlotActivity.java)
+18
View File
@@ -0,0 +1,18 @@
# GroupRenderer
`GroupRenderer` is a special implementation of `XYSeriesRenderer` that combines multiple instances
of `XYSeries` into a single virtual series of s higher dimension. Examples of `GroupRenderer` are:
* BarRenderer
* CandlestickRenderer
Let's take a quick look at `CandlestickRenderer`. A candlestick chart is basically a two two dimensional
representation of a complex value; for every value of x a single candlestick is drawn, but there are
four values (dimensions) that make up each candlestick:
* open
* close
* high
* low
When a series is added to the plot with a formatter associated with a renderer that extends `GroupRenderer`,
all series added with formatters of the same type are automatically "grouped" together during rendering.
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 KiB

@@ -0,0 +1,7 @@
# About Androidplot Screenshots
Screenshots should be added to this directory for each new plot type. These screens should
conform to the following guidelines:
* Screenshots framed in Nexus5x, no glare, no shadows
* Horizontal screen dimensions: 950w * 484h
* Vertical screen dimensions 484w * 950h
Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

+52
View File
@@ -0,0 +1,52 @@
# Androidplot Documentation
When working with Androidplot, your best resource
is the [DemoApp's example source code](../demoapp) as it's kept up to date for every release.
If you can't find an answer feel free to ask a question on Stack Overflow using the
[Androidplot tag](http://stackoverflow.com/questions/tagged/androidplot).
# Tutorials
These tutorials are roughly in the order that they should be read. Quickstart will get your app
up and running with a basic xy plot. Plot Composition explains the common anatomy of all plots
in the Androidplot library and lays the foundation for future topics. The tutorials discuss
specific plot types explaining styling and other advanced topics.
* [Quickstart](quickstart.md) :star:
* [Quickstart (YouTube Video)](https://www.youtube.com/watch?v=wEFkzQY_wWI) :movie_camera:
* [Plot Composition](plot_composition.md)
* [The Legend](legend.md)
* [XY Plots](xyplot.md)
* [Bar Charts](barchart.md)
* [Candlestick Charts](candlestick.md)
* [Pie Charts](piechart.md)
* [Bubble Charts](bubblechart.md)
* [Dynamic Plots](dynamicdata.md)
* [Advanced XY Plot Topics](advanced_xy_plot.md)
* [Custom Renderers](custom_renderer.md)
# Examples
Source code examples of the various plot types.
* [An XYPlot](../demoapp/src/main/java/com/androidplot/demos/SimpleXYPlotActivity.java)
* [A dynamic XYPlot](../demoapp/src/main/java/com/androidplot/demos/DynamicXYPlotActivity.java)
* [Panning & Zooming](../demoapp/src/main/java/com/androidplot/demos/TouchZoomExampleActivity.java)
* [Step Chart](../demoapp/src/main/java/com/androidplot/demos/StepChartExampleActivity.java)
* [Candlestick Chart](../demoapp/src/main/java/com/androidplot/demos/CandlestickChartActivity.java)
* [Scatter Plot](../demoapp/src/main/java/com/androidplot/demos/ScatterPlotActivity.java)
* [Bar Chart](../demoapp/src/main/java/com/androidplot/demos/BarPlotExampleActivity.java)
* [Pie Chart](../demoapp/src/main/java/com/androidplot/demos/SimplePieChartActivity.java)
* [An ECG Example](../demoapp/src/main/java/com/androidplot/demos/ECGExample.java)
* [f(x) Plot](../demoapp/src/main/java/com/androidplot/demos/FXPlotExampleActivity.java)
# XML Attributes
A complete list of XML attributes is [available here](attrs.md).
# Javadoc
The latest Javadocs are [available here](https://javadoc.io/doc/com.androidplot/androidplot-core).
# Release Notes
Full release notes are [available here](release_notes.md)
# Contributing
_If you see something that isn't right or want to contribute, please [make a pull-request](https://help.github.com/articles/creating-a-pull-request/) - these docs
live the main repo in the top-level `/docs` directory. For more info, see the [Contributing Source Code](docs/contributing.md) doc.
+63
View File
@@ -0,0 +1,63 @@
# <a name="legend"></a> The Legend
For `Plot` types that support it, the legend displays a list of elements in the plot along with
a color coded icon. The color coded icon is automatically generated using the colors and line styles
used to render the associated item. In the case of a `Series`, this is the `Formatter` you associated
with the `Series` when you added it to your `Plot`.
# Showing / Hiding the Legend
Depending on the `Plot` type(s) you are using, the legend may or may not be visible by default. To
can enable / disable the legend:
```java
plot.getLegend().setVisible(true|false);
```
# Hiding Series Items
You can tell Androidplot not to generate a legend item for a `Series` by configuring it's associated
`Formatter`:
```java
formatter.setLegendIconEnabled(false);
```
## The TableModel
The `TableModel` controls how and where each item in the legend is drawn. Androidplot provides two
default implementations; `DynamicTableModel` and `FixedTableModel` (detailed below). All `TableModel` implementations
organize elements into a grid. This grid is populated with items based on the order which it's corresponding
series was added to the plot. This ordering can be further controlled by setting the `TableModel`'s
`TableOrder` param to either [ROW_MAJOR](https://en.wikipedia.org/wiki/Row-major_order) (items are added left-to-right, top-down)
or `COLUMN_MAJOR` (items are added top-down, left-to-right).
### DynamicTableModel
The `DynamicTableModel` takes a desired of numbered rows and columns and evenly subdivides the `LegendWidget`'s
visible space into cells. For example, A 2x2 legend using `ROW_MAJOR` ordering:
```java
plot.getLegend().setTableModel(new DynamicTableModel(2, 2, TableOrder.ROW_MAJOR));
```
### FixedTableModel
The `FixedTableModel` takes a desired size of each cell in pixels and adds cells using the specified `TableOrder`.
It automatically wraps to the next row or column (based on `TableOrder`) when the cell being added
exceeds the legend's available space on a given axis. For example, A `FixedTableModel` using 300w*100h cells and
a TableOrder of `COLUMN_MAJOR`:
```java
plot.getLegend().setTableModel(new FixedTableModel(PixelUtils.dpToPix(300),
PixelUtils.dpToPix(100), TableOrder.COLUMN_MAJOR));
```
# Sorting Legend Entries
You can control the order of Legend entries by setting a custom `Comparator` on the legend:
```java
Comparator<...> myComparator = ...
plot.getLegend().setLegendItemComparator(myComparator);
```
Using a custom `Comparator` in conjunction with `ROW_MAJOR` and `COLUMN_MAJOR` properties on the `TableModel`
(show above) gives you full control over the display ordering of your legend entries.
# Positioning and Resizing
The legend is just an implementation of a Widget and is positioned and resized in the same ways
that all Widget instances are positioned. See the [Plot Composition](plot_composition.md) doc for details.
+104
View File
@@ -0,0 +1,104 @@
# Pie Charts
Like all other primary chart and graph classes in Androidplot, `PieChart` is simply a composition
of widgets along with convenience methods. The real work is actually done by `PieWidget`.
![image](images/screens/pie_horiz.png)
# Basic Usage
Pie charts are composed of one or more values called Segments which combines a vaue and a label:
```java
Segment segment = new Segment("my segment", 10);
```
Displaying a `Segment` to a pie chart also requires a `SegmentFormatter`. `SegmentFormatter` is what defines
the visual characteristics of the associated `Segment`; color, border thickness,
segment offset, label text style (if any) etc. The below code instantiates a new `SegmentFormatter`
with a segment color of red:
```java
SegmentFormatter formatter = new SegmentFormatter(Color.RED);
```
Finally, the `Segment` must be added to the pie chart along with it's `SegmentFormatter`:
```java
pie.addSegment(segment, formatter);
```
# Donut Size
By default, `PieChart` draws it's self as a ring. The amount of empty space in the middle of
the pie is called the `donutSize`. Setting a `donutSize` of 0 causes the `PieChart` to draw it's
self as a full circle.
![image](images/pie_donut_size.png)
Setting `donutSize` programmatically:
```java
// draw pie with an empty inner space radius equal to 50% of the pie's radius.
pieRenderer.setDonutSize(0.5, PieRenderer.DonutMode.PERCENT);
// donut size may also be expressed in pixels:
pieRenderer.setDonutSize(PixelUtils.dpToPix(30), PieRenderer.DonutMode.PIXELS);
```
# Segment Orientation
Each `Segment` is drawn in the order it was added to the `PieChart` clockwise from `startDegs`
which by default is 0.
![image](images/pie_orientation.png)
Using the image above, to move Segment0 to the Northeast quadrant
of the pie (currently occupied by Segment3) `startDeg` should be set to 90:
```java
pie.getRenderer(PieRenderer.class).setStartDegs(90);
```
# Extent
The `extentDegs` value determines the number of degrees running clockwise from `startDegs` that
represent 100% of the pie chart. The formal description is a little obtuse but is easy to understand
through example. You could for instance use `extentDegs` in conjunction with `startDegs` to represent
the full pie as a gauge:
```java
// start the first segment at 5 degrees;
pie.getRenderer(PieRenderer.class).setStartDegs(165);
pie.getRenderer(PieRenderer.class).setExtentDegs(150);
```
Which produces a pie chart profile like this:
![image](images/pie_extent.png)
# SegmentFormatter
As mentioned above, `SegmentFormatter` defines how a `Segment` is visually represented in a `PieChart`.
In addition to basic color / border line styling params, there are several parameters available
to visually distinguish a segment:
## Offset
Value in pixels used to "shift" the position of the segment outward relative to the center of the `PieChart`.
The visual effect is equivalent to cutting up a pie and dragging one piece out and away from the rest of the pie:
![image](images/pie_offset.png)
## RadialInset
Value in degrees used to shrink the radial edges of the segment inward. The visual effect is equivalent
to shaving the edges off of a slice of pie, resulting in a narrower slice of pie:
![image](images/pie_radial_inset.png)
## InnerInset
Value in pixels used to shrink the inner section of the pie. The visual effect is equivalent to
cutting the tip off of a slice of pie:
![image](images/pie_inner_inset.png)
## OuterInset
Value in pixels used to shrink the outer edge of the pie. The visual effect is equivalent to evenly
shaving the crust off of a slice of pie:
![image](images/pie_outer_inset.png)
See the DemoApp's
[pie chart example source](../demoapp/src/main/java/com/androidplot/demos/SimplePieChartActivity.java) for a comprehensive usage example.
+289
View File
@@ -0,0 +1,289 @@
# Plot Composition
All plots in Androidplot inherit from the abstract base class `Plot` which provides common behaviors
for all `Plot` implementations.
# Widgets
Plots are composed of one or more Widgets. A `Widget` is an abstraction of a visual
component that may be positioned and scaled within the visible area of a Plot. For example,
an `XYPlot` is typically composed of these 5 `Widgets`:
* Title
* Graph
* Domain Label
* Range Label
* [Legend](legend.md)
All implementations of `Plot` will contain at least one default `Widget` providing the core
behavior encapsulated by that `Plot`. In addition to moving and scaling a `Widget`, developers may
also extend them and replace the `Plot` instance's default instance with the derived implementation in order to
get custom behavior.
# The LayoutManager
The `LayoutManager` provides the logic for visually positioning and scaling Widgets; all `Plot` implementations
contain an instance of `LayoutManager` that can be retrieved via `Plot.getLayoutManager()`.
## Z-Indexing
Z-indexing is a 2D drawing concept which associates each drawable entity with a value that determines
which elements get drawn onto the screen first, producing the visual effect that certain elements appear
on top of others.
While Androidplot uses the term "z-index" it's implemented internally as a linked list to prevent the possibility
of duplicate index values and therefore ensuring that `Widget` drawing order is always explicit.
The [Layerable](../androidplot-core/src/main/java/com/androidplot/util/Layerable.java) interface
defines methods used for manipulating the z-index of a `Widget`.
## Adding & Removing Widgets
New `Widget` instances can be added either to the front or back of the z-index using these methods:
* `LayoutManager.addToTop(Widget)`
* `LayoutManager.addToBottom(Widget)`
## Positioning Widgets
Once a Widget has been added to the LayoutManager, it's position within the Plot can be adjusted via
`Widget.position(...)`. This method takes a layout style and value for the x and y dimension and an
optional anchor position from which the layout modes will be applied.
### HorizontalPosition & VerticalPosition
These define the part of the Plot from which Widget will be positioned using relative or absolute units.
* XPosition - Supports positioning in relation to the Plot's left edge, right edge or horizontal center.
* YPosition - Supports positioning in relation to the Plot's top edge, bottom edge or vertical center.
#### Absolute Positioning
Absolute positioning means that positions are expressed as an absolute pixel offset from the specified
edge or center point.
#### Relative Positioning
Relative positions are expressed as a ratio of the total size of the Plot along the given axis. This
ration must fall within the range of -1 to 1.
**Example #1: A relative XPosition of 1 and YPosition of 0.5 in a 100Hx200W pixel Plot**
x = 1 * 200 = 200
y = 0.5 * 100 = 50
**Example #2: An absolute XPosition of 50 and an absolute YPosition of 25 in a 100Hx200W pixel Plot**
x = 50
y = 25
#### Anchors
The Anchor param specifies the point on the Widget from which the XPosition and YPosition calculations will be applied.
Using example #1 above, an Anchor value of `Anchor.LEFT_TOP` means that the top left corner of the Widget
would be positioned at the screen coordinate [200, 50].
#### Examples
The examples below illustrate positioning an `XYGraphWidget` of an `XYPlot`.
xml:
```xml
ap:graphAnchor="right_bottom"
ap:graphHorizontalPositioning="absolute_from_right"
ap:graphHorizontalPosition="0dp"
ap:graphVerticalPositioning="relative_from_bottom"
ap:graphVerticalPosition="0dp"
```
java:
```java
plot.getGraph().position(
0, HorizontalPositioning.ABSOLUTE_FROM_RIGHT,
0, VerticalPositioning.RELATIVE_TO_BOTTOM);
```
![alt text](images/positioning/bottom-right-absolute.png "bottom-right-absolute")
***
xml:
```xml
ap:graphAnchor="right_bottom"
ap:graphHorizontalPositioning="absolute_from_right"
ap:graphHorizontalPosition="10dp"
ap:graphVerticalPositioning="relative_from_bottom"
ap:graphVerticalPosition="10dp"
```
java:
```java
plot.getGraph().position(
PixelUtils.dpToPix(10), HorizontalPositioning.ABSOLUTE_FROM_RIGHT,
PixelUtils.dpToPix(10), VerticalPositioning.RELATIVE_TO_BOTTOM);
```
![alt text](images/positioning/bottom-right-absolute-with-offset.png "bottom-right-absolute-with-offset")
***
xml:
```xml
ap:graphAnchor="right_bottom"
ap:graphHorizontalPositioning="absolute_from_left"
ap:graphHorizontalPosition="0dp"
ap:graphVerticalPositioning="relative_from_top"
ap:graphVerticalPosition="0dp"
```
java:
```java
plot.getGraph().position(
0, HorizontalPositioning.ABSOLUTE_FROM_LEFT,
0, VerticalPositioning.RELATIVE_TO_TOP);
```
![alt text](image``s/positioning/top-left-absolute.png "top-left-absolute")
***
xml:
```xml
ap:graphAnchor="center"
ap:graphHorizontalPositioning="absolute_from_center"
ap:graphHorizontalPosition="0dp"
ap:graphVerticalPositioning="absolute_from_center"
ap:graphVerticalPosition="0dp"
```
java:
```java
plot.getGraph().position(
0, HorizontalPositioning.ABSOLUTE_FROM_CENTER,
0, VerticalPositioning.ABSOLUTE_FROM_CENTER);
```
![alt text](images/positioning/center-absolute.png "center-absolute")
## Sizing Widgets
The size and shape of a `Widget` is controlled by it's `setSize(Size)` method.
### Size
The `Size` parameter of `Widget.setSize(Size)` defines the height and width of the associated Widget.
It is composed of two `SizeMetric` instances; one for height and one for width.
`Size` provides two constructors: `Size(SizeMetric, SizeMetric)` or
`Size(int, SizeMode, int, SizeMode)`. Constructor params represent height and width respectively.
### SizeMetric
A `SizeMetric` is composed of a `SizeMode` and a float value. There are three kinds of `SizeMode`:
* ABSOLUTE - float value defines the size metric as an absolute value in pixels.
* RELATIVE - float value defines the size of the metric relative to the size of the containing Plot along
the associated axis, in the range of 0.0 to 1.0.
* FILL - float value defines an absolute value in pixels to subtract from the size of the containing
Plot along the associated axis and the SizeMetric "fills" the difference.
#### Examples
The examples below illustrate positioning an `XYGraphWidget` of an `XYPlot` and
assume centered positioning is applied as described in the [Positioning Widgets](#positioning-widgets) section above.
xml:
```xml
ap:graphHeightMode="absolute"
ap:graphHeight="100dp"
ap:graphWidthMode="absolute"
ap:graphWidth="100dp"
```
java:
```java
plot.getGraph().setSize(new Size(
PixelUtils.dpToPix(100), SizeMode.ABSOLUTE,
PixelUtils.dpToPix(100), SizeMode.ABSOLUTE));
```
![alt text](images/sizing/abs100x-abs100y.png "x=100|ABSOLUTE, y=100|ABSOLUTE")
***
xml:
```xml
ap:graphHeightMode="absolute"
ap:graphHeight="150dp"
ap:graphWidthMode="absolute"
ap:graphWidth="100dp"
```
java:
```java
plot.getGraph().setSize(new Size(
PixelUtils.dpToPix(150), SizeMode.ABSOLUTE,
PixelUtils.dpToPix(100), SizeMode.ABSOLUTE));
```
![alt text](images/sizing/abs100x-abs150y.png "x=100|ABSOLUTE, y=150|ABSOLUTE")
***
xml:
```xml
ap:graphHeightMode="relative"
ap:graphHeight="1.0"
ap:graphWidthMode="absolute"
ap:graphWidth="100dp"
```
java:
```java
plot.getGraph().setSize(new Size(
1.0f, SizeMode.RELATIVE,
PixelUtils.dpToPix(100), SizeMode.ABSOLUTE));
```
![alt text](images/sizing/abs100x-rel1y.png "x=100|ABSOLUTE, y=1.0|RELATIVE")
***
xml:
```xml
ap:graphHeightMode="absolute"
ap:graphHeight="100dp"
ap:graphWidthMode="relative"
ap:graphWidth="0.75"
```
java:
```java
plot.getGraph().setSize(new Size(
PixelUtils.dpToPix(100), SizeMode.ABSOLUTE,
0.75f, SizeMode.RELATIVE));
```
![alt text](images/sizing/rel075x-abs100y.png "x=0.75|RELATIVE, y=100|ABSOLUTE")
***
xml:
```xml
ap:graphHeightMode="fill"
ap:graphHeight="50dp"
ap:graphWidthMode="fill"
ap:graphWidth="50dp"
```
java:
```java
plot.getGraph().setSize(new Size(
PixelUtils.dpToPix(50), SizeMode.FILL,
PixelUtils.dpToPix(50), SizeMode.FILL));
```
![alt text](images/sizing/fil50x-fil50y.png "x=50|FILL, y=50|FILL")
## Margins and Padding
Every widget has a margin, padding and an optional border that can be drawn around it. These params behave
very similarly to those defined in the [CSS Box Model](http://www.w3schools.com/css/css_boxmodel.asp).
## Markup Mode
If you're having trouble visualizing the effects of tweaking margins and padding, you can enable
markup mode which will highlight these spaces on each widget, as well as draw a green line around it's
absolute border.
To turn markup mode on for a plot programmatically:
```java
plot.setMarkupEnabled(true);
```
Or via XML:
```xml
ap:markupEnabled="true"
```
This is what it looks like:
![alt text](images/markup_mode.jpg "markup enabled")
# Formatters, Renderers and Series Data
Each Plot specifies the type of Series it supports; XYPlots support XYSeries, PieCharts support Segment, etc.
In all cases, the Series encapsulates the numeric model of the data being represented by the Plot.
## Formatters
The Plot keeps a mapping between Series
data and the Formatter instance provided by the user that is to be used to render that data. It is the
Formatter that tells Androiplot which Renderer to use to draw the Series along with which colors, line thicknesses,
text style, etc. to apply while drawing.
## Renderers
The Renderer is what renders Series data onto a Plot. Users can provide their own custom rendering behavior
by writing their own Renderer implementation along with a custom Formatter telling Androidplot about the
Renderer via the `Formatter.getRendererClass()` method. See [Custom Renderer](custom_rnderer.md) documentation.
# XML Styling
Androidplot supports an increasing number of XML attributes. The two best resources for learning about
these attributes is the [demo app source code](../demoapp) and [attrs.xml](../androidplot-core/src/main/res/values/attrs.xml) file which
contains the exhaustive list of available attributes.
+205
View File
@@ -0,0 +1,205 @@
# Quickstart
![image](images/simple_xy_plot.png)
This tutorial will walk you through Adding Androidplot as a dependency and displaying an XYPlot with
two data series. You can view this sample on your own device or emulator by installing the
[Demo App](https://play.google.com/store/apps/details?id=com.androidplot.demos) on your device and
clicking on the Simple XY Plot Example. Full source [available here](../demoapp/src/main/java/com/androidplot/demos/SimpleXYPlotActivity.java).
You can also watch this tutorial in video form on [Youtube](https://youtu.be/wEFkzQY_wWI).
# Add the Dependency
To use the library in your gradle project add the following to your build.gradle:
```groovy
dependencies {
implementation "com.androidplot:androidplot-core:1.5.10"
}
```
*NOTE: As of version 1.5.8 Androidplot has migrated over from the Android Support Libraries to androidx.
If you have a very old project and experience issues, may need to stay on version 1.5.7*
If youre using Proguard obfuscation (Projects created by Android Studio do by default) youll also
want add this to your proguard-rules.pro file:
`-keep class com.androidplot.** { *; }`
# Create your Activity XML Layout
Once youve got an Android project skeleton created, create **res/layout/simple_xy_plot_example.xml**
and add an XYPlot view:
```xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ap="http://schemas.android.com/apk/res-auto"
android:layout_height="match_parent"
android:layout_width="match_parent">
<com.androidplot.xy.XYPlot
style="@style/APDefacto.Dark"
android:id="@+id/plot"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
ap:title="A Simple XY Plot"
ap:rangeTitle="range"
ap:domainTitle="domain"
ap:lineLabels="left|bottom"
ap:lineLabelRotationBottom="-45"/>
</LinearLayout>
```
This example uses a default style to decorate the plot. The full list of XML styleable attributes is
[available here](attrs.md). While new attributes are added regularly,
not all configurable properties are yet available.
If something you need is missing, use [Fig Syntax](https://github.com/halfhp/fig)
directly within your Plot's XML, prefixing each property with "androidPlot". Example:
```xml
androidPlot.title="My Plot"
```
Add these files to your **/res/xml** directory:
#### /res/xml/line_point_formatter_with_labels.xml
```xml
<?xml version="1.0" encoding="utf-8"?>
<config
linePaint.strokeWidth="5dp"
linePaint.color="#00AA00"
vertexPaint.color="#007700"
vertexPaint.strokeWidth="20dp"
fillPaint.color="#00000000"
pointLabelFormatter.textPaint.color="#CCCCCC"/>
```
#### /res/xml/line_point_formatter_with_labels_2.xml
```xml
<?xml version="1.0" encoding="utf-8"?>
<config
linePaint.strokeWidth="5dp"
linePaint.color="#0000AA"
vertexPaint.strokeWidth="20dp"
vertexPaint.color="#000099"
fillPaint.color="#00000000"
pointLabelFormatter.textPaint.color="#CCCCCC"/>
```
# Create an Activity
Now let's create an Activity to display the XYPlot we just defined in `simple_xy_plot_example.xml`.
The basic steps are:
1. Create an instance of Series and populate it with data to be displayed.
2. Register one or more series with the plot instance along with a Formatter to describing how the series should look when drawn.
3. Draw the Plot
Since we're working with XY data, well use XYPlot, SimpleXYSeries (which is an
implementation of the XYSeries interface) and LineAndPointFormatter:
```java
import android.app.Activity;
import android.graphics.*;
import android.os.Bundle;
import com.androidplot.util.PixelUtils;
import com.androidplot.xy.SimpleXYSeries;
import com.androidplot.xy.XYSeries;
import com.androidplot.xy.*;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParsePosition;
import java.util.*;
/**
* 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 LineAndPointFormatter(this, R.xml.line_point_formatter_with_labels);
LineAndPointFormatter series2Format =
new LineAndPointFormatter(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));
// just for fun, 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, StringBuffer toAppendTo, FieldPosition pos) {
int i = Math.round(((Number) obj).floatValue());
return toAppendTo.append(domainLabels[i]);
}
@Override
public Object parseObject(String source, ParsePosition pos) {
return null;
}
});
}
}
```
One potentially confusing section of the code above are the initializations of LineAndPointFormatter
You probably noticed that they take a mysterious reference to an xml resource file. This is actually
using [Fig](https://github.com/halfhp/fig) to configure the instance properties from XML.
If you'd prefer to avoid the XML and keep everything in Java, just replace the code:
```java
LineAndPointFormatter series1Format =
new LineAndPointFormatter(this, R.xml.line_point_formatter_with_labels);
```
with:
```java
LineAndPointFormatter series1Format = new LineAndPointFormatter(Color.RED, Color.GREEN, Color.BLUE, null);
```
In general XML configuration should be used over programmatic configuration when possible as it produces
more flexibility in terms of defining properties by screen density etc.. For more details on how to
programmatically configure Formatters etc. consult the latest Javadocs.
# Whats Next?
Learn about [Plot Composition](plot_composition.md) or continue with [XYPlots](xyplot.md).
+173
View File
@@ -0,0 +1,173 @@
# Androidplot Versioning
For details on what to expect in general when updating to a new version of Androiplot, check out the
[versioning doc](versioning.md).
# 1.5.10
* Update project to use latest gradle / build tools
* (#114) fix `setLinesPerRangeLabel` & `setLinesPerDomainLabel`
# 1.5.9
* (#107) Fix ambiguous ordinal for render mode attributes
* (#104) Fix issue with background rendering in RecyclerView.
* Adds a RecyclerView example to demo app.
# 1.5.8
* Maintenance release - update dependences, get off jcenter, etc.
# 1.5.7
* (#94) Potential fix / better error logging for a crash caused by a buffered canvas resize with illegal arguments.
* (#93) Fix Android 9 compiler warnings.
* (#83) Fix NPE when attempting to recycle an already null buffered canvas instance.
* Remove unused `PlotRenderException`.
* Added `IN_ORDER` BarRenderer mode.
# 1.5.6
* Adds convenience methods for saving / restoring `PanZoom` state.
* (#80) Targets SDK 28, fixing compatibility issues.
# 1.5.5
* (#76) Fixed a bug that could cause a deadlock when grid steps are much larger than actual plot range.
* (#78) Fixed a bug where setting insets on XYGraphWidget would have no effect after the plot was drawn.
* XYGraphWidget.drawMarkerText is now marked `protected`.
# 1.5.4
* (#69) Fixed a bug in `SimpleXYPlot` preventing the resizing of `Y_VALS_ONLY` formatted series.
* (#73) Fixed a bug where dynamically resizing a Plot (by marking a sibling view as `GONE`, etc.) would not resize the graph widget.
# 1.5.3
* Minor cleanup of Widget example source.
* (#67) Fixed Javadoc link
# 1.5.2
_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._
* Added [sizing documentation](plot_composition.md#sizing-widgets)
* Added [custom renderer documentation](custom_renderer.md)
* Fixed (#61) Bug in `XYGraphWidget.screenToSeriesY(...)`.
* Fixed (#63) Fixed compatibility issue with Gradle 3.x.x that caused issues with XML parsing.
# 1.5.1
* (#52) Fixed minor NPE issue
* (#55) Fixed bug with `PieRenderer.getContainingSegment` not working for very large segments.
# 1.5.0
_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](legend.md) for implementation details._
* Added [legend doc](legend.md)
* Added legend support to `PieChart`
* Added configurable legend item sorting
* (#45) Auto range boundaries calculation fix for when using a fixed domain range and a `FastXYSeries`
* Minor Performance Optimizations
# 1.4.3
* (#39) `FastLineAndPointRenderer` now renders vertices for legend items.
* Added [XML Attrs reference doc](attrs.md).
# 1.4.2
* (#32) New step mode: `INCREMENT_BY_FIT`.
* (#33) `PanZoom` support for `INCREMENT_BY_FIT`.
* (#34) Removed examples and documentation for serializing `SeriesRegistry` to preserve state.
# 1.4.1
* (#26) Fixed an NPE issue when drawing null values with a `PointLabeler`.
* Fixed a broken link in Quickstart doc.
# 1.4.0
* Moderate refactor of `PieRenderer`. [Documentation](piechart.md) has been updated to reflect these changes.
* Major refactor of `BarRenderer`. [Documentation](barchart.md) has been updated to reflect these changes.
* Added `ScalingXYSeries` which wraps other instances of `XYSeries` to be dynamically scaled. This is
particularly useful for creating animated intros using `XYSeries` data.
* Added [AnimatedXYPlotActivity](../demoapp/src/main/java/com/androidplot/demos/AnimatedXYPlotActivity.java)
demonstrating the use of `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.
* Domain and range cursors are now disabled by default. To enable, set a valid cursor position using
`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(...)`.
# 1.3.1
* Added [NormedXYSeries](advanced_xy_plot.md#normedxyseries) wrapper to simplify the process of normalizing xy series data.
* Added [DualScaleActivity](../demoapp/src/main/java/com/androidplot/demos/DualScaleActivity.java)
demonstrating `NormedXYSeries` usage to present dual range scales.
* LineAndPointRenderer options for cases where two or mode series' of different size have been added.
* Fixed a bug causing points scrolled off-screen to occasionally accumulate and render along the left edge of the graph.
* Fixed a bug that could cause render jitter when extreme zoom levels were applied.
* Fixed a bug that prevented `PanZoom` from working properly on plots with an undefined outer limit.
# 1.3.0
* Added sampling support. See the [Advanced XY Plot](advanced_xy_plot.md) doc for details.
* PanZoom performance enhancements & bug fixes. If you're currently using PanZoom you'll likely need to
update your code as the interface has slightly changed.
* Added leakcanary to DemoApp for debug builds.
* More unit test coverage
* Fixed a bug that prevented an instance of a given series from being added more than once, even
when a unique formatter is supplied.
* Added `Formatter.getLegendIconEnabled()` and `Formatter.setLegendIconEnabled(boolean)`, used to enable / disable drawing legend items for individual
series / formatter pairs.
* Added `XYGraphWidget.Edge.NONE` to be used with `XYGraphWidget.setLineLabelEdges(Edge...)` to disable all edges.
# 1.2.2
* BarRenderer / BarFormatter cleanup
* More documentation!
* Bounds and XYBounds have been merged into Region and RectRegion respectively.
* ValPixConverter has been removed and it's functionality migrated to Region and RectRegion.
* Added Region.transform(...) and RectRegion.transform(...)
* Added Region.ratio(...) and RectRegion.ratio(...)
* XYPlot.getCalculatedMinX(), XYPlot.getCalculatedMaxX(), XYPlot.getCalculatedMinY() and XYPlot.getCalculatedMinY()
have been replaced with XYPlot.getBounds().
* Configurator has become it's own library - [Fig!](https://github.com/halfhp/fig)
* New constructors have been added to Formatters to simplify XML configuration via Fig.
* Added Jacoco code coverage reporting
# 1.2.1
### Pie Chart Enhancements
Pie chart has been updated with new methods and format attributes to improve segment
selection and highlighting functionality:
* Added `offset`, `radialInset`, `innerInset` and `outerInset` properties to SegmentFormatter.
See the [pie chart documentation](piechart.md) for usage details.
* Updated PieRenderer to support the new SegmentFormatter properties.
* SimplePieChartActivity has been updated to provide an interactive demo of some of these new features.
### Misc
* Added `Plot.getListeners()` method.
* Added FastLineAndPointRenderer, updated OrientationSensorExampleActivity to use it.
* Updated to target SDK 24, removed sdkmanager dependency, and other misc. updates to project deps etc.
* Lots of additions and updates to documentation
* Added CircleCI support
# 1.1.0
* Added drawGridOnTop param to XYGraphWidget; when set to true, grid lines will be drawn on top of rendered series data. (default is false)
* Added PanZoom class providing one-line configuration of pan/zoom behavior for instances of XYPlot. See TouchZoomExampleActivity for a usage example.
* Removed InteractiveXYPlot as PanZoom makes it obsolete.
# 1.0.0
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.
* Major refactor of XYGraphWidget
* Tick renamed to Line
* Extensible Label Formatters
* getXXXWidget methods renamed to simply getXXX
* Per-edge tick extensions
* Moved documentation into vcs. Docs from 1.0 forward will be maintained here. (TODO)
* plot label xml param renamed to title
+15
View File
@@ -0,0 +1,15 @@
# Versioning
Androidplot's versioning scheme is Major.Minor.Rev. For example, in the version 1.3.2 the Major is 1
the Minor is 3 and the Rev is 2.
Revisional releases (releases where only the Rev value has increased from the previous release, ie 1.3.1 -> 1.3.2)
may include new features and bug fixes but will be fully backwards compatible with releases of the same minor version.
**Updating to the latest revisional release should require no code changes in your app.**
Minor Releases (releases where the Minor value has increased but the Major value has not) may contain
new features, bug fixes, removed methods deprecated in the previous minor release
and other moderate refactoring. **Updating to a new minor release may require code changes in your app.**
Major releases (releases where the Major value has increased) are typically complete rewrites of core
elements of the library. Unless otherwise specified in the release notes, **updating to a new major release
will require significant code changes to your app.**
+391
View File
@@ -0,0 +1,391 @@
# XYPlot
An `XYPlot` renders one or more XYSeries onto an instance of XYGraphWidget. It also includes two instances
of `TextLabelWidget` used to display an optional title for the domain and range axis, and an instance
of [XYLegendWidget](#legend) which by default will automatically display legend items for each `XYSeries` added to the plot.
![image](images/plot_anatomy.png)
# XYSeries
`XYSeries` is the interface Androidplot uses to retrieve your numeric data. You may either create your own
implementation of `XYSeries` or use `SimpleXYSeries` if you don't have tight performance requirements or
if your numeric data is easily accessed as an array or list of values.
## SimpleXYSeries
As a convenience, Androidplot provides an all-purpose implementation of the `XYSeries` interface called
`SimpleXYSeries`. `SimpleXYSeries` is used to wrap your raw data with an implementation of the `XYSeries` interface.
You can supply your data in several ways:
As a list of y-vals (x = i for each supplied y-val):
```java
Number[] yVals = {1, 4, 2, 8, 4, 16, 8, 32, 16, 64};
XYSeries series1 = new SimpleXYSeries(
Arrays.asList(yVals), SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "my series");
```
An interleaved list of x/y value pairs (x[0] = 1, y[0] = 4, x[1] = 2, y[1] = 8, ...):
```java
Number[] yVals = {1, 4, 2, 8, 4, 16, 8, 32, 16, 64};
XYSeries series1 = new SimpleXYSeries(
Arrays.asList(yVals), SimpleXYSeries.ArrayFormat.XY_VALS_INTERLEAVED, "my series");
```
Separate lists of x-vals and y-vals:
```java
Number[] xVals = {1, 4, 2, 8, 4, 16, 8, 32, 16, 64};
Number[] yVals = {5, 2, 10, 5, 20, 10, 40, 20, 80, 40};
XYSeries series = new SimpleXYSeries(xVals, yVals, "my series");
```
Keep in mind that `SimpleXYSeries` is designed to be easy to use for a broad number of applications; it's not
optimized for any specific scenario; if you are dynamically displaying data that needs to be refreshed more than several
times a second, consider building your own implementation of `XYSeries` designed for your app's
specific needs.
# The Graph
`XYGraphWidget` encapsulates XYPlot's graphing functionality. Given an instance of `XYPlot`, a reference
to `XYGraphWidget` can be retrieve via `XYPlot.getGraph()`.
## Domain & Range Boundaries
By default, Androidplot will analyze all `XYSeries` instances registered with the `XYPlot`, determine the
min/max values for domain and range and adjust the `XYPlot` boundaries to match those values. If your
plot contains dynamic data, especially if your plot can periodically contain either no series data
or data with no resolution on one or both axis (all identical values for either x or y) then you may
want to manually set your `XYPlot`'s domain and range boundaries.
To set your plot's boundaries use:
* `XYPlot.setDomainBoundaries(Number value, BoundaryMode mode)`
* `XYPlot.setRangeBoundaries(Number value, BoundaryMode mode)`
Note that the value argument is only used when setting `BoundaryMode.FIXED`. For all other
modes, pass in null.
### BoundaryMode
Androidplot provides four boundary modes:
#### FIXED
The plot's boundaries on the specified axis are fixed to user defined values.
#### AUTO (default)
The plot's boundaries auto adjust to the min/max values for the defined axis.
#### GROW
The plot's boundaries automatically increase to the max value encountered by the plot. The initial
determines the starting boundaries from which the `BoundaryMode.GROW` behavior will be based.
#### SHRINK
The plot's boundaries automatically shrink to the min value encountered by the plot. The initial
determines the starting boundaries from which the shrink behavior will be based.
## Domain & Range Lines
These are the horizontal lines drawn on a graph. These lines are configured via:
* `XYPlot.setDomainStep(StepMode mode, Number value)`
* `XYPlot.setRangeStep(StepMode, Number value)`
Androidplot provides these step modes:
### Subdivide
When using `StepMode.SUBDIVIDE`, the graph is subdivided into the specified number of sections.
### IncrementByValue
`StepMode.INCREMENT_BY_VALUE` instructs Androidplot draw grid lines at the specified interval. This
is the most commonly used modes as is produces an easy to read result.
### IncrementByPixels
`StepMode.INCREMENT_BY_PIXELS` behaves identically to `StepMode.INCREMENT_BY_VALUE` except that
the increment quantity is expressed in pixels.
## Domain & Range Labels
Androidplot supports labeling domain values on either or both the top and bottom graph edges
and range values on either or both the left and right graph edges. Most default styles show labels
only on the left and bottom edges.
### Line Label Insets
Insets are used to control where line labels are drawn in relation to the graph space. The Insets instance
can be obtained via `XYPlot.getGraph().getLineLabelInsets()`. For example, to
move the range labels on the left of the graph further to the left by 5dp:
xml
```xml
ap:lineLabelInsetLeft="-5dp"
```
java
```java
plot.getGraph().getLineLabelInsets().setLeft(PixelUtils.dpToPix(-5));
```
### Dual Axis Labels
Androidplot provides methods for enabling / disabling axis labels along all edges of the graph. By
default, only the left and bottom edge labels are enabled. To enable labels on the right edge:
xml
```xml
ap:lineLabels="left|bottom|right"
```
java
```java
plot.getGraph().setLineLabelEdges(
XYGraphWidget.Edge.BOTTOM,
XYGraphWidget.Edge.LEFT,
XYGraphWidget.Edge.RIGHT);
```
Once the edge has been enabled, text formatting can be controlled by enabling a custom formatter
for the desired edge:
```java
plot.getGraph().getLineLabelStyle(XYGraphWidget.Edge.RIGHT).setFormat(new Format() {
@Override
public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
// obj contains the raw Number value representing the position of the label being drawn.
// customize the labeling however you want here:
int i = Math.round(((Number) obj).floatValue());
return toAppendTo.append(i + " thingies");
}
@Override
public Object parseObject(String source, ParsePosition pos) {
// unused
return null;
}
});
```
You're likely also going to need to normalize your series data in order to get it to display properly. To help simplify
this step, Androidplot provides [NormedXYSeries](advanced_xy_plot.md#normedxyseries)
which can be used to wrap any `XYSeries` to provide the normalized representation of it's data.
There's a full [reference implementation](../demoapp/src/main/java/com/androidplot/demos/DualScaleActivity.java)
of a dual scale plot using a custom `Formatter` and `NormedXYSeries` in the DemoApp.
### Line Label Interval
Androidplot allows you to configure the interval at which labels are rendered for domain and range lines:
* `XYPlot.getGraph().setLinesPerDomainLabel(int interval)`
* `XYPlot.getGraph().setLinesPerRangeLabel(int interval)`
### LineLabelStyle
The styling of the line labels drawn on each edge of the graph is controlled by it's associated style.
this style contains params that control:
* Paint used to draw labels (determines, color, font size, etc.)
* Format used to draw text (can be NumberFormat, etc.)
* Rotation of the label text
To get the style used to draw the left edge (range) labels:
```java
plot.getGraph().getLineLabelStyle(XYGraphWidget.Edge.LEFT);
```
### LineLabelRenderer
If you need to implement special rendering behavior for your line labels, such as drawing graphics, symbols, etc.
you can create a custom renderer by extending LineLabelRenderer and injecting it into the graph:
```java
plot.getGraph().setLineLabelRenderer(XYGraphWidget.Edge.LEFT, customLineLabelRenderer);
```
[f(x) plot example source](../demoapp/src/main/java/com/androidplot/demos/FXPlotExampleActivity.java)
provides an example of this kind of customization.
# Pan & Zoom
You can enable pan/zoom behavior on any instance of XYPlot using the PanZoom class like this:
```java
PanZoom.attach(plot);
```
The default behavior is to enable horizontal and vertical panning an to zoom using uniform scaling.
If you want to override this behavior use the three argument form of `PanZoom.attach(Plot)`. For example,
to enable pan and zoom (stretch) on the horizontal axis only:
```java
PanZoom.attach(plot, PanZoom.Pan.HORIZONTAL, PanZoom.Zoom.STRETCH_HORIZONTAL);
```
Pan and zoom operations abide by your plot's defined outer limits limits. If no such limits have been set
then the plot will pan and zoom on both axes infinitely. To set the plot's outer limits:
```java
// cap pan/zoom limits for panning and zooming to a 100x100 space:
plot.getOuterLimits().set(0, 100, 0, 100);
```
# Saving & Restoring PanZoom State
The `PanZoom` class provides convenience methods for saving and restoring state from your `Activity`:
```java
// save the current pan/zoom state
@Override
public void onSaveInstanceState(Bundle bundle) {
bundle.putSerializable("pan-zoom-state", panZoom.getState());
}
// restore the previously saved pan/zoom state
@Override
public void onRestoreInstanceState(Bundle bundle) {
PanZoom.State state = (PanZoom.State) bundle.getSerializable("pan-zoom-state");
panZoom.setState(state);
plot.redraw();
}
```
For a more detailed look at pan & zoom behavior, check out the [Touch Zoom Example source code](../demoapp/src/main/java/com/androidplot/demos/TouchZoomExampleActivity.java).
# Series Renderers
There are several renderers available for XYPlots:
* LineAndPointRenderer
* BarRenderer
* CandlestickRenderer
When you add a new series to your plot, you tell Androidplot how to render it by passing
in a subclass of `XYSeriesFormatter` that corresponds to the desired renderer. For example, to use
`LineAndPointRenderer`, you'd register your series with an instance of `LineAndPointFormatter`:
```java
LineAndPointFormatter format = new LineAndPointFormatter();
plot.addSeries(series, format);
```
If you need special behavior not provided by an existing renderer you can create
your own by either extending `XYSeriesRenderer` or one of the above implementations. You'll also need
to create a matching implementation of `XYSeriesFormatter` that returns your renderer's class
from it's `getRendererClass()` method.
## LineAndPointRenderer
`LineAndPointRenderer` is the go-to renderer when it comes to `XYSeries`. It provides the most robust
feature set of any `XYSeriesRenderer` and has been the most carefully optimized for performance. Having
said that, `LineAndPointRenderer` isn't always the best tool for the job. Androidplot includes two
additional variations of `LineAndPointRenderer`:
* **FastLineAndPointRenderer** - intended for use in apps displaying large amounts of dynamic data where
fast refresh rates are important.
* **AdvancedLineAndPointRenderer** - provides capabilities for dynamically coloring individual line
segments, etc. See the [ECGExample source](../demoapp/src/main/java/com/androidplot/demos/ECGExample.java)
in the demo app for a complete example.
### Labeling Points
Most implementations of `XYSeriesRenderer` support labeling rendered points with text. This functionality
is activated by setting the `PointLabelFormatter` property in the associated `XYSeriesFormatter`. For
example, to enable point labels for a `LineAndPointFormatter`:
// create a new `PointLabelFormatter` with a text size of 12sp and a color of red:
```java
PointLabelFormatter plf = new PointLabelFormatter();
plf.getTextPaint().setTextSize(PixelUtils.spToPix(12));
plf.getTextPaint().setColor(Color.RED);
lineAndPointFormatter.setPointLabelFormatter(plf);
```
By default this will enable labels for all points using a string representation of the yVal of each
point. This behavior can be customized by setting a custom instance of `PointLabeler` on the `XYSeriesFormatter`:
```java
formatter.setPointLabeler(new PointLabeler() {
@Override
public String getLabel(XYSeries series, int index) {
// draw labels on even indexes only:
if(index % 2 == 0) {
return "Y=" + series.getY(index).doubleValue();
}
return null;
}
});
```
## BarRenderer
See the [barcharts documentation](barchart.md).
## CandlestickRenderer
See the [candlestick documentation](candlestick.md)
# Drawing Smooth Lines
Smooth lines can be created by applying the
[Catmull-Rom interpolator](http://androidplot.com/smooth-curves-and-androidplot/) to your series' Format.
# The Legend <a name="legend"></a>
By default, Androidplot will automatically produce a legend for your `XYPlot`. See [the legend](legend.md) doc
for usage details.
# Graph Rotation
Androidplot provides the `Widget.setRotation(Widget.Rotation)` method for controlling the orientation
of Widgets. For example, if you wanted to create a bar graph where the bars extended across the screen
from left to right:
xml
```xml
ap:graphRotation="ninety_degrees"
```
java
```java
plot.getGraph().setRotation(Widget.Rotation.NINETY_DEGREES);
```
# Optimization Tips
Here are a few suggestions to improve performance when plotting dynamic data:
* Create your own implementation of `XYSeries` to work with your data in it's rawest form.
* Use `FastLineAndPointRenderer` instead of `LineAndPointRenderer`:
```java
plot.addSeries(azimuthHistorySeries,
new FastLineAndPointRenderer.Formatter(
Color.rgb(100, 100, 200), null, null, null));
```
* Consider averaging or subsampling very large datasets before rendering. If you have the time and
inclination, [the LTTB algorithm](http://skemman.is/stream/get/1946/15343/37285/3/SS_MSthesis.pdf) is
particularly well suited for downsampling `XYSeries` data.
* If possible, avoid rendering vertices (points).
* Disable anti-aliasing on your `XYSeriesFormatter`'s paint values:
```java
LineAndPointFormatter format = new LineAndPointFormatter(...);
format.getLinePaint().setAntiAlias(false);
```
# Converting Values
Because the coordinate system used by your `XYSeries` data is almost always different than the screen
coordinate system upon which the data is rendered, you'll often need to convert from one system to
the other. `XYPlot` provides convenience methods for this purpose:
## Screen to Series Conversion
```java
// x
float screenX = ...
Number x = plot.screenToSeriesX(screenX);
// y
float screenY = ...
Number y = plot.screenToSeriesY(screenY);
// x and y
PointF screenCoords = ...
XYCoords xy = plot.screenToSeries(screenCoords)
```
## Series to Screen Conversion
```java
// x
Number x = ...
float screenX = plot.seriesToScreenX(x);
// y
Number y = ...
float screenY = plot.seriesToScreenY(y);
// x and y
XYCoords xy = ...
PointF screenCoords = plot.series.Screen(xy);
```
# Whats Next?
Explore [Advanced XYPlot Topics](advanced_xy_plot.md)