init: init proj

This commit is contained in:
2025-11-10 18:21:19 +08:00
commit b65b28ec9e
1796 changed files with 187617 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.github.mikephil.charting">
<!-- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
</application> -->
</manifest>

View File

@@ -0,0 +1,207 @@
package com.github.mikephil.charting.animation;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import androidx.annotation.RequiresApi;
import com.github.mikephil.charting.animation.Easing.EasingFunction;
/**
* Object responsible for all animations in the Chart. Animations require API level 11.
*
* @author Philipp Jahoda
* @author Mick Ashton
*/
public class ChartAnimator {
/** object that is updated upon animation update */
private AnimatorUpdateListener mListener;
/** The phase of drawn values on the y-axis. 0 - 1 */
@SuppressWarnings("WeakerAccess")
protected float mPhaseY = 1f;
/** The phase of drawn values on the x-axis. 0 - 1 */
@SuppressWarnings("WeakerAccess")
protected float mPhaseX = 1f;
public ChartAnimator() { }
@RequiresApi(11)
public ChartAnimator(AnimatorUpdateListener listener) {
mListener = listener;
}
@RequiresApi(11)
private ObjectAnimator xAnimator(int duration, EasingFunction easing) {
ObjectAnimator animatorX = ObjectAnimator.ofFloat(this, "phaseX", 0f, 1f);
animatorX.setInterpolator(easing);
animatorX.setDuration(duration);
return animatorX;
}
@RequiresApi(11)
private ObjectAnimator yAnimator(int duration, EasingFunction easing) {
ObjectAnimator animatorY = ObjectAnimator.ofFloat(this, "phaseY", 0f, 1f);
animatorY.setInterpolator(easing);
animatorY.setDuration(duration);
return animatorY;
}
/**
* Animates values along the X axis, in a linear fashion.
*
* @param durationMillis animation duration
*/
@RequiresApi(11)
public void animateX(int durationMillis) {
animateX(durationMillis, Easing.Linear);
}
/**
* Animates values along the X axis.
*
* @param durationMillis animation duration
* @param easing EasingFunction
*/
@RequiresApi(11)
public void animateX(int durationMillis, EasingFunction easing) {
ObjectAnimator animatorX = xAnimator(durationMillis, easing);
animatorX.addUpdateListener(mListener);
animatorX.start();
}
/**
* Animates values along both the X and Y axes, in a linear fashion.
*
* @param durationMillisX animation duration along the X axis
* @param durationMillisY animation duration along the Y axis
*/
@RequiresApi(11)
public void animateXY(int durationMillisX, int durationMillisY) {
animateXY(durationMillisX, durationMillisY, Easing.Linear, Easing.Linear);
}
/**
* Animates values along both the X and Y axes.
*
* @param durationMillisX animation duration along the X axis
* @param durationMillisY animation duration along the Y axis
* @param easing EasingFunction for both axes
*/
@RequiresApi(11)
public void animateXY(int durationMillisX, int durationMillisY, EasingFunction easing) {
ObjectAnimator xAnimator = xAnimator(durationMillisX, easing);
ObjectAnimator yAnimator = yAnimator(durationMillisY, easing);
if (durationMillisX > durationMillisY) {
xAnimator.addUpdateListener(mListener);
} else {
yAnimator.addUpdateListener(mListener);
}
xAnimator.start();
yAnimator.start();
}
/**
* Animates values along both the X and Y axes.
*
* @param durationMillisX animation duration along the X axis
* @param durationMillisY animation duration along the Y axis
* @param easingX EasingFunction for the X axis
* @param easingY EasingFunction for the Y axis
*/
@RequiresApi(11)
public void animateXY(int durationMillisX, int durationMillisY, EasingFunction easingX,
EasingFunction easingY) {
ObjectAnimator xAnimator = xAnimator(durationMillisX, easingX);
ObjectAnimator yAnimator = yAnimator(durationMillisY, easingY);
if (durationMillisX > durationMillisY) {
xAnimator.addUpdateListener(mListener);
} else {
yAnimator.addUpdateListener(mListener);
}
xAnimator.start();
yAnimator.start();
}
/**
* Animates values along the Y axis, in a linear fashion.
*
* @param durationMillis animation duration
*/
@RequiresApi(11)
public void animateY(int durationMillis) {
animateY(durationMillis, Easing.Linear);
}
/**
* Animates values along the Y axis.
*
* @param durationMillis animation duration
* @param easing EasingFunction
*/
@RequiresApi(11)
public void animateY(int durationMillis, EasingFunction easing) {
ObjectAnimator animatorY = yAnimator(durationMillis, easing);
animatorY.addUpdateListener(mListener);
animatorY.start();
}
/**
* Gets the Y axis phase of the animation.
*
* @return float value of {@link #mPhaseY}
*/
public float getPhaseY() {
return mPhaseY;
}
/**
* Sets the Y axis phase of the animation.
*
* @param phase float value between 0 - 1
*/
public void setPhaseY(float phase) {
if (phase > 1f) {
phase = 1f;
} else if (phase < 0f) {
phase = 0f;
}
mPhaseY = phase;
}
/**
* Gets the X axis phase of the animation.
*
* @return float value of {@link #mPhaseX}
*/
public float getPhaseX() {
return mPhaseX;
}
/**
* Sets the X axis phase of the animation.
*
* @param phase float value between 0 - 1
*/
public void setPhaseX(float phase) {
if (phase > 1f) {
phase = 1f;
} else if (phase < 0f) {
phase = 0f;
}
mPhaseX = phase;
}
}

View File

@@ -0,0 +1,309 @@
package com.github.mikephil.charting.animation;
import android.animation.TimeInterpolator;
import androidx.annotation.RequiresApi;
/**
* Easing options.
*
* @author Daniel Cohen Gindi
* @author Mick Ashton
*/
@SuppressWarnings("WeakerAccess")
@RequiresApi(11)
public class Easing {
public interface EasingFunction extends TimeInterpolator {
@Override
float getInterpolation(float input);
}
private static final float DOUBLE_PI = 2f * (float) Math.PI;
@SuppressWarnings("unused")
public static final EasingFunction Linear = new EasingFunction() {
public float getInterpolation(float input) {
return input;
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInQuad = new EasingFunction() {
public float getInterpolation(float input) {
return input * input;
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseOutQuad = new EasingFunction() {
public float getInterpolation(float input) {
return -input * (input - 2f);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInOutQuad = new EasingFunction() {
public float getInterpolation(float input) {
input *= 2f;
if (input < 1f) {
return 0.5f * input * input;
}
return -0.5f * ((--input) * (input - 2f) - 1f);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInCubic = new EasingFunction() {
public float getInterpolation(float input) {
return (float) Math.pow(input, 3);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseOutCubic = new EasingFunction() {
public float getInterpolation(float input) {
input--;
return (float) Math.pow(input, 3) + 1f;
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInOutCubic = new EasingFunction() {
public float getInterpolation(float input) {
input *= 2f;
if (input < 1f) {
return 0.5f * (float) Math.pow(input, 3);
}
input -= 2f;
return 0.5f * ((float) Math.pow(input, 3) + 2f);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInQuart = new EasingFunction() {
public float getInterpolation(float input) {
return (float) Math.pow(input, 4);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseOutQuart = new EasingFunction() {
public float getInterpolation(float input) {
input--;
return -((float) Math.pow(input, 4) - 1f);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInOutQuart = new EasingFunction() {
public float getInterpolation(float input) {
input *= 2f;
if (input < 1f) {
return 0.5f * (float) Math.pow(input, 4);
}
input -= 2f;
return -0.5f * ((float) Math.pow(input, 4) - 2f);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInSine = new EasingFunction() {
public float getInterpolation(float input) {
return -(float) Math.cos(input * (Math.PI / 2f)) + 1f;
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseOutSine = new EasingFunction() {
public float getInterpolation(float input) {
return (float) Math.sin(input * (Math.PI / 2f));
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInOutSine = new EasingFunction() {
public float getInterpolation(float input) {
return -0.5f * ((float) Math.cos(Math.PI * input) - 1f);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInExpo = new EasingFunction() {
public float getInterpolation(float input) {
return (input == 0) ? 0f : (float) Math.pow(2f, 10f * (input - 1f));
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseOutExpo = new EasingFunction() {
public float getInterpolation(float input) {
return (input == 1f) ? 1f : (-(float) Math.pow(2f, -10f * (input + 1f)));
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInOutExpo = new EasingFunction() {
public float getInterpolation(float input) {
if (input == 0) {
return 0f;
} else if (input == 1f) {
return 1f;
}
input *= 2f;
if (input < 1f) {
return 0.5f * (float) Math.pow(2f, 10f * (input - 1f));
}
return 0.5f * (-(float) Math.pow(2f, -10f * --input) + 2f);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInCirc = new EasingFunction() {
public float getInterpolation(float input) {
return -((float) Math.sqrt(1f - input * input) - 1f);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseOutCirc = new EasingFunction() {
public float getInterpolation(float input) {
input--;
return (float) Math.sqrt(1f - input * input);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInOutCirc = new EasingFunction() {
public float getInterpolation(float input) {
input *= 2f;
if (input < 1f) {
return -0.5f * ((float) Math.sqrt(1f - input * input) - 1f);
}
return 0.5f * ((float) Math.sqrt(1f - (input -= 2f) * input) + 1f);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInElastic = new EasingFunction() {
public float getInterpolation(float input) {
if (input == 0) {
return 0f;
} else if (input == 1) {
return 1f;
}
float p = 0.3f;
float s = p / DOUBLE_PI * (float) Math.asin(1f);
return -((float) Math.pow(2f, 10f * (input -= 1f))
*(float) Math.sin((input - s) * DOUBLE_PI / p));
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseOutElastic = new EasingFunction() {
public float getInterpolation(float input) {
if (input == 0) {
return 0f;
} else if (input == 1) {
return 1f;
}
float p = 0.3f;
float s = p / DOUBLE_PI * (float) Math.asin(1f);
return 1f
+ (float) Math.pow(2f, -10f * input)
* (float) Math.sin((input - s) * DOUBLE_PI / p);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInOutElastic = new EasingFunction() {
public float getInterpolation(float input) {
if (input == 0) {
return 0f;
}
input *= 2f;
if (input == 2) {
return 1f;
}
float p = 1f / 0.45f;
float s = 0.45f / DOUBLE_PI * (float) Math.asin(1f);
if (input < 1f) {
return -0.5f
* ((float) Math.pow(2f, 10f * (input -= 1f))
* (float) Math.sin((input * 1f - s) * DOUBLE_PI * p));
}
return 1f + 0.5f
* (float) Math.pow(2f, -10f * (input -= 1f))
* (float) Math.sin((input * 1f - s) * DOUBLE_PI * p);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInBack = new EasingFunction() {
public float getInterpolation(float input) {
final float s = 1.70158f;
return input * input * ((s + 1f) * input - s);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseOutBack = new EasingFunction() {
public float getInterpolation(float input) {
final float s = 1.70158f;
input--;
return (input * input * ((s + 1f) * input + s) + 1f);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInOutBack = new EasingFunction() {
public float getInterpolation(float input) {
float s = 1.70158f;
input *= 2f;
if (input < 1f) {
return 0.5f * (input * input * (((s *= (1.525f)) + 1f) * input - s));
}
return 0.5f * ((input -= 2f) * input * (((s *= (1.525f)) + 1f) * input + s) + 2f);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInBounce = new EasingFunction() {
public float getInterpolation(float input) {
return 1f - EaseOutBounce.getInterpolation(1f - input);
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseOutBounce = new EasingFunction() {
public float getInterpolation(float input) {
float s = 7.5625f;
if (input < (1f / 2.75f)) {
return s * input * input;
} else if (input < (2f / 2.75f)) {
return s * (input -= (1.5f / 2.75f)) * input + 0.75f;
} else if (input < (2.5f / 2.75f)) {
return s * (input -= (2.25f / 2.75f)) * input + 0.9375f;
}
return s * (input -= (2.625f / 2.75f)) * input + 0.984375f;
}
};
@SuppressWarnings("unused")
public static final EasingFunction EaseInOutBounce = new EasingFunction() {
public float getInterpolation(float input) {
if (input < 0.5f) {
return EaseInBounce.getInterpolation(input * 2f) * 0.5f;
}
return EaseOutBounce.getInterpolation(input * 2f - 1f) * 0.5f + 0.5f;
}
};
}

View File

@@ -0,0 +1,91 @@
package com.github.mikephil.charting.buffer;
import java.util.List;
/**
* Buffer class to boost performance while drawing. Concept: Replace instead of
* recreate.
*
* @author Philipp Jahoda
* @param <T> The data the buffer accepts to be fed with.
*/
public abstract class AbstractBuffer<T> {
/** index in the buffer */
protected int index = 0;
/** float-buffer that holds the data points to draw, order: x,y,x,y,... */
public final float[] buffer;
/** animation phase x-axis */
protected float phaseX = 1f;
/** animation phase y-axis */
protected float phaseY = 1f;
/** indicates from which x-index the visible data begins */
protected int mFrom = 0;
/** indicates to which x-index the visible data ranges */
protected int mTo = 0;
/**
* Initialization with buffer-size.
*
* @param size
*/
public AbstractBuffer(int size) {
index = 0;
buffer = new float[size];
}
/** limits the drawing on the x-axis */
public void limitFrom(int from) {
if (from < 0)
from = 0;
mFrom = from;
}
/** limits the drawing on the x-axis */
public void limitTo(int to) {
if (to < 0)
to = 0;
mTo = to;
}
/**
* Resets the buffer index to 0 and makes the buffer reusable.
*/
public void reset() {
index = 0;
}
/**
* Returns the size (length) of the buffer array.
*
* @return
*/
public int size() {
return buffer.length;
}
/**
* Set the phases used for animations.
*
* @param phaseX
* @param phaseY
*/
public void setPhases(float phaseX, float phaseY) {
this.phaseX = phaseX;
this.phaseY = phaseY;
}
/**
* Builds up the buffer with the provided data and resets the buffer-index
* after feed-completion. This needs to run FAST.
*
* @param data
*/
public abstract void feed(T data);
}

View File

@@ -0,0 +1,130 @@
package com.github.mikephil.charting.buffer;
import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
public class BarBuffer extends AbstractBuffer<IBarDataSet> {
protected int mDataSetIndex = 0;
protected int mDataSetCount = 1;
protected boolean mContainsStacks = false;
protected boolean mInverted = false;
/** width of the bar on the x-axis, in values (not pixels) */
protected float mBarWidth = 1f;
public BarBuffer(int size, int dataSetCount, boolean containsStacks) {
super(size);
this.mDataSetCount = dataSetCount;
this.mContainsStacks = containsStacks;
}
public void setBarWidth(float barWidth) {
this.mBarWidth = barWidth;
}
public void setDataSet(int index) {
this.mDataSetIndex = index;
}
public void setInverted(boolean inverted) {
this.mInverted = inverted;
}
protected void addBar(float left, float top, float right, float bottom) {
buffer[index++] = left;
buffer[index++] = top;
buffer[index++] = right;
buffer[index++] = bottom;
}
@Override
public void feed(IBarDataSet data) {
float size = data.getEntryCount() * phaseX;
float barWidthHalf = mBarWidth / 2f;
for (int i = 0; i < size; i++) {
BarEntry e = data.getEntryForIndex(i);
if(e == null)
continue;
float x = e.getX();
float y = e.getY();
float[] vals = e.getYVals();
if (!mContainsStacks || vals == null) {
float left = x - barWidthHalf;
float right = x + barWidthHalf;
float bottom, top;
if (mInverted) {
bottom = y >= 0 ? y : 0;
top = y <= 0 ? y : 0;
} else {
top = y >= 0 ? y : 0;
bottom = y <= 0 ? y : 0;
}
// multiply the height of the rect with the phase
if (top > 0)
top *= phaseY;
else
bottom *= phaseY;
addBar(left, top, right, bottom);
} else {
float posY = 0f;
float negY = -e.getNegativeSum();
float yStart = 0f;
// fill the stack
for (int k = 0; k < vals.length; k++) {
float value = vals[k];
if (value == 0.0f && (posY == 0.0f || negY == 0.0f)) {
// Take care of the situation of a 0.0 value, which overlaps a non-zero bar
y = value;
yStart = y;
} else if (value >= 0.0f) {
y = posY;
yStart = posY + value;
posY = yStart;
} else {
y = negY;
yStart = negY + Math.abs(value);
negY += Math.abs(value);
}
float left = x - barWidthHalf;
float right = x + barWidthHalf;
float bottom, top;
if (mInverted) {
bottom = y >= yStart ? y : yStart;
top = y <= yStart ? y : yStart;
} else {
top = y >= yStart ? y : yStart;
bottom = y <= yStart ? y : yStart;
}
// multiply the height of the rect with the phase
top *= phaseY;
bottom *= phaseY;
addBar(left, top, right, bottom);
}
}
}
reset();
}
}

View File

@@ -0,0 +1,94 @@
package com.github.mikephil.charting.buffer;
import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
public class HorizontalBarBuffer extends BarBuffer {
public HorizontalBarBuffer(int size, int dataSetCount, boolean containsStacks) {
super(size, dataSetCount, containsStacks);
}
@Override
public void feed(IBarDataSet data) {
float size = data.getEntryCount() * phaseX;
float barWidthHalf = mBarWidth / 2f;
for (int i = 0; i < size; i++) {
BarEntry e = data.getEntryForIndex(i);
if(e == null)
continue;
float x = e.getX();
float y = e.getY();
float[] vals = e.getYVals();
if (!mContainsStacks || vals == null) {
float bottom = x - barWidthHalf;
float top = x + barWidthHalf;
float left, right;
if (mInverted) {
left = y >= 0 ? y : 0;
right = y <= 0 ? y : 0;
} else {
right = y >= 0 ? y : 0;
left = y <= 0 ? y : 0;
}
// multiply the height of the rect with the phase
if (right > 0)
right *= phaseY;
else
left *= phaseY;
addBar(left, top, right, bottom);
} else {
float posY = 0f;
float negY = -e.getNegativeSum();
float yStart = 0f;
// fill the stack
for (int k = 0; k < vals.length; k++) {
float value = vals[k];
if (value >= 0f) {
y = posY;
yStart = posY + value;
posY = yStart;
} else {
y = negY;
yStart = negY + Math.abs(value);
negY += Math.abs(value);
}
float bottom = x - barWidthHalf;
float top = x + barWidthHalf;
float left, right;
if (mInverted) {
left = y >= yStart ? y : yStart;
right = y <= yStart ? y : yStart;
} else {
right = y >= yStart ? y : yStart;
left = y <= yStart ? y : yStart;
}
// multiply the height of the rect with the phase
right *= phaseY;
left *= phaseY;
addBar(left, top, right, bottom);
}
}
}
reset();
}
}

View File

@@ -0,0 +1,258 @@
package com.github.mikephil.charting.charts;
import android.content.Context;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.BarData;
import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.highlight.BarHighlighter;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider;
import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
import com.github.mikephil.charting.renderer.BarChartRenderer;
/**
* Chart that draws bars.
*
* @author Philipp Jahoda
*/
public class BarChart extends BarLineChartBase<BarData> implements BarDataProvider {
/**
* flag that indicates whether the highlight should be full-bar oriented, or single-value?
*/
protected boolean mHighlightFullBarEnabled = false;
/**
* if set to true, all values are drawn above their bars, instead of below their top
*/
private boolean mDrawValueAboveBar = true;
/**
* if set to true, a grey area is drawn behind each bar that indicates the maximum value
*/
private boolean mDrawBarShadow = false;
private boolean mFitBars = false;
public BarChart(Context context) {
super(context);
}
public BarChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public BarChart(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void init() {
super.init();
mRenderer = new BarChartRenderer(this, mAnimator, mViewPortHandler);
setHighlighter(new BarHighlighter(this));
getXAxis().setSpaceMin(0.5f);
getXAxis().setSpaceMax(0.5f);
}
@Override
protected void calcMinMax() {
if (mFitBars) {
mXAxis.calculate(mData.getXMin() - mData.getBarWidth() / 2f, mData.getXMax() + mData.getBarWidth() / 2f);
} else {
mXAxis.calculate(mData.getXMin(), mData.getXMax());
}
// calculate axis range (min / max) according to provided data
mAxisLeft.calculate(mData.getYMin(YAxis.AxisDependency.LEFT), mData.getYMax(YAxis.AxisDependency.LEFT));
mAxisRight.calculate(mData.getYMin(YAxis.AxisDependency.RIGHT), mData.getYMax(YAxis.AxisDependency
.RIGHT));
}
/**
* Returns the Highlight object (contains x-index and DataSet index) of the selected value at the given touch
* point
* inside the BarChart.
*
* @param x
* @param y
* @return
*/
@Override
public Highlight getHighlightByTouchPoint(float x, float y) {
if (mData == null) {
Log.e(LOG_TAG, "Can't select by touch. No data set.");
return null;
} else {
Highlight h = getHighlighter().getHighlight(x, y);
if (h == null || !isHighlightFullBarEnabled()) return h;
// For isHighlightFullBarEnabled, remove stackIndex
return new Highlight(h.getX(), h.getY(),
h.getXPx(), h.getYPx(),
h.getDataSetIndex(), -1, h.getAxis());
}
}
/**
* Returns the bounding box of the specified Entry in the specified DataSet. Returns null if the Entry could not be
* found in the charts data. Performance-intensive code should use void getBarBounds(BarEntry, RectF) instead.
*
* @param e
* @return
*/
public RectF getBarBounds(BarEntry e) {
RectF bounds = new RectF();
getBarBounds(e, bounds);
return bounds;
}
/**
* The passed outputRect will be assigned the values of the bounding box of the specified Entry in the specified DataSet.
* The rect will be assigned Float.MIN_VALUE in all locations if the Entry could not be found in the charts data.
*
* @param e
* @return
*/
public void getBarBounds(BarEntry e, RectF outputRect) {
RectF bounds = outputRect;
IBarDataSet set = mData.getDataSetForEntry(e);
if (set == null) {
bounds.set(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE);
return;
}
float y = e.getY();
float x = e.getX();
float barWidth = mData.getBarWidth();
float left = x - barWidth / 2f;
float right = x + barWidth / 2f;
float top = y >= 0 ? y : 0;
float bottom = y <= 0 ? y : 0;
bounds.set(left, top, right, bottom);
getTransformer(set.getAxisDependency()).rectValueToPixel(outputRect);
}
/**
* If set to true, all values are drawn above their bars, instead of below their top.
*
* @param enabled
*/
public void setDrawValueAboveBar(boolean enabled) {
mDrawValueAboveBar = enabled;
}
/**
* returns true if drawing values above bars is enabled, false if not
*
* @return
*/
public boolean isDrawValueAboveBarEnabled() {
return mDrawValueAboveBar;
}
/**
* If set to true, a grey area is drawn behind each bar that indicates the maximum value. Enabling his will reduce
* performance by about 50%.
*
* @param enabled
*/
public void setDrawBarShadow(boolean enabled) {
mDrawBarShadow = enabled;
}
/**
* returns true if drawing shadows (maxvalue) for each bar is enabled, false if not
*
* @return
*/
public boolean isDrawBarShadowEnabled() {
return mDrawBarShadow;
}
/**
* Set this to true to make the highlight operation full-bar oriented, false to make it highlight single values (relevant
* only for stacked). If enabled, highlighting operations will highlight the whole bar, even if only a single stack entry
* was tapped.
* Default: false
*
* @param enabled
*/
public void setHighlightFullBarEnabled(boolean enabled) {
mHighlightFullBarEnabled = enabled;
}
/**
* @return true the highlight operation is be full-bar oriented, false if single-value
*/
@Override
public boolean isHighlightFullBarEnabled() {
return mHighlightFullBarEnabled;
}
/**
* Highlights the value at the given x-value in the given DataSet. Provide
* -1 as the dataSetIndex to undo all highlighting.
*
* @param x
* @param dataSetIndex
* @param stackIndex the index inside the stack - only relevant for stacked entries
*/
public void highlightValue(float x, int dataSetIndex, int stackIndex) {
highlightValue(new Highlight(x, dataSetIndex, stackIndex), false);
}
@Override
public BarData getBarData() {
return mData;
}
/**
* Adds half of the bar width to each side of the x-axis range in order to allow the bars of the barchart to be
* fully displayed.
* Default: false
*
* @param enabled
*/
public void setFitBars(boolean enabled) {
mFitBars = enabled;
}
/**
* Groups all BarDataSet objects this data object holds together by modifying the x-value of their entries.
* Previously set x-values of entries will be overwritten. Leaves space between bars and groups as specified
* by the parameters.
* Calls notifyDataSetChanged() afterwards.
*
* @param fromX the starting point on the x-axis where the grouping should begin
* @param groupSpace the space between groups of bars in values (not pixels) e.g. 0.8f for bar width 1f
* @param barSpace the space between individual bars in values (not pixels) e.g. 0.1f for bar width 1f
*/
public void groupBars(float fromX, float groupSpace, float barSpace) {
if (getBarData() == null) {
throw new RuntimeException("You need to set data for the chart before grouping bars.");
} else {
getBarData().groupBars(fromX, groupSpace, barSpace);
notifyDataSetChanged();
}
}
}

View File

@@ -0,0 +1,43 @@
package com.github.mikephil.charting.charts;
import android.content.Context;
import android.util.AttributeSet;
import com.github.mikephil.charting.data.BubbleData;
import com.github.mikephil.charting.interfaces.dataprovider.BubbleDataProvider;
import com.github.mikephil.charting.renderer.BubbleChartRenderer;
/**
* The BubbleChart. Draws bubbles. Bubble chart implementation: Copyright 2015
* Pierre-Marc Airoldi Licensed under Apache License 2.0. In the BubbleChart, it
* is the area of the bubble, not the radius or diameter of the bubble that
* conveys the data.
*
* @author Philipp Jahoda
*/
public class BubbleChart extends BarLineChartBase<BubbleData> implements BubbleDataProvider {
public BubbleChart(Context context) {
super(context);
}
public BubbleChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public BubbleChart(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void init() {
super.init();
mRenderer = new BubbleChartRenderer(this, mAnimator, mViewPortHandler);
}
public BubbleData getBubbleData() {
return mData;
}
}

View File

@@ -0,0 +1,44 @@
package com.github.mikephil.charting.charts;
import android.content.Context;
import android.util.AttributeSet;
import com.github.mikephil.charting.data.CandleData;
import com.github.mikephil.charting.interfaces.dataprovider.CandleDataProvider;
import com.github.mikephil.charting.renderer.CandleStickChartRenderer;
/**
* Financial chart type that draws candle-sticks (OHCL chart).
*
* @author Philipp Jahoda
*/
public class CandleStickChart extends BarLineChartBase<CandleData> implements CandleDataProvider {
public CandleStickChart(Context context) {
super(context);
}
public CandleStickChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CandleStickChart(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void init() {
super.init();
mRenderer = new CandleStickChartRenderer(this, mAnimator, mViewPortHandler);
getXAxis().setSpaceMin(0.5f);
getXAxis().setSpaceMax(0.5f);
}
@Override
public CandleData getCandleData() {
return mData;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,272 @@
package com.github.mikephil.charting.charts;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
import com.github.mikephil.charting.data.BarData;
import com.github.mikephil.charting.data.BubbleData;
import com.github.mikephil.charting.data.CandleData;
import com.github.mikephil.charting.data.CombinedData;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.ScatterData;
import com.github.mikephil.charting.highlight.CombinedHighlighter;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.interfaces.dataprovider.CombinedDataProvider;
import com.github.mikephil.charting.interfaces.datasets.IDataSet;
import com.github.mikephil.charting.renderer.CombinedChartRenderer;
/**
* This chart class allows the combination of lines, bars, scatter and candle
* data all displayed in one chart area.
*
* @author Philipp Jahoda
*/
public class CombinedChart extends BarLineChartBase<CombinedData> implements CombinedDataProvider {
/**
* if set to true, all values are drawn above their bars, instead of below
* their top
*/
private boolean mDrawValueAboveBar = true;
/**
* flag that indicates whether the highlight should be full-bar oriented, or single-value?
*/
protected boolean mHighlightFullBarEnabled = false;
/**
* if set to true, a grey area is drawn behind each bar that indicates the
* maximum value
*/
private boolean mDrawBarShadow = false;
protected DrawOrder[] mDrawOrder;
/**
* enum that allows to specify the order in which the different data objects
* for the combined-chart are drawn
*/
public enum DrawOrder {
BAR, BUBBLE, LINE, CANDLE, SCATTER
}
public CombinedChart(Context context) {
super(context);
}
public CombinedChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CombinedChart(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void init() {
super.init();
// Default values are not ready here yet
mDrawOrder = new DrawOrder[]{
DrawOrder.BAR, DrawOrder.BUBBLE, DrawOrder.LINE, DrawOrder.CANDLE, DrawOrder.SCATTER
};
setHighlighter(new CombinedHighlighter(this, this));
// Old default behaviour
setHighlightFullBarEnabled(true);
mRenderer = new CombinedChartRenderer(this, mAnimator, mViewPortHandler);
}
@Override
public CombinedData getCombinedData() {
return mData;
}
@Override
public void setData(CombinedData data) {
super.setData(data);
setHighlighter(new CombinedHighlighter(this, this));
((CombinedChartRenderer)mRenderer).createRenderers();
mRenderer.initBuffers();
}
/**
* Returns the Highlight object (contains x-index and DataSet index) of the selected value at the given touch
* point
* inside the CombinedChart.
*
* @param x
* @param y
* @return
*/
@Override
public Highlight getHighlightByTouchPoint(float x, float y) {
if (mData == null) {
Log.e(LOG_TAG, "Can't select by touch. No data set.");
return null;
} else {
Highlight h = getHighlighter().getHighlight(x, y);
if (h == null || !isHighlightFullBarEnabled()) return h;
// For isHighlightFullBarEnabled, remove stackIndex
return new Highlight(h.getX(), h.getY(),
h.getXPx(), h.getYPx(),
h.getDataSetIndex(), -1, h.getAxis());
}
}
@Override
public LineData getLineData() {
if (mData == null)
return null;
return mData.getLineData();
}
@Override
public BarData getBarData() {
if (mData == null)
return null;
return mData.getBarData();
}
@Override
public ScatterData getScatterData() {
if (mData == null)
return null;
return mData.getScatterData();
}
@Override
public CandleData getCandleData() {
if (mData == null)
return null;
return mData.getCandleData();
}
@Override
public BubbleData getBubbleData() {
if (mData == null)
return null;
return mData.getBubbleData();
}
@Override
public boolean isDrawBarShadowEnabled() {
return mDrawBarShadow;
}
@Override
public boolean isDrawValueAboveBarEnabled() {
return mDrawValueAboveBar;
}
/**
* If set to true, all values are drawn above their bars, instead of below
* their top.
*
* @param enabled
*/
public void setDrawValueAboveBar(boolean enabled) {
mDrawValueAboveBar = enabled;
}
/**
* If set to true, a grey area is drawn behind each bar that indicates the
* maximum value. Enabling his will reduce performance by about 50%.
*
* @param enabled
*/
public void setDrawBarShadow(boolean enabled) {
mDrawBarShadow = enabled;
}
/**
* Set this to true to make the highlight operation full-bar oriented,
* false to make it highlight single values (relevant only for stacked).
*
* @param enabled
*/
public void setHighlightFullBarEnabled(boolean enabled) {
mHighlightFullBarEnabled = enabled;
}
/**
* @return true the highlight operation is be full-bar oriented, false if single-value
*/
@Override
public boolean isHighlightFullBarEnabled() {
return mHighlightFullBarEnabled;
}
/**
* Returns the currently set draw order.
*
* @return
*/
public DrawOrder[] getDrawOrder() {
return mDrawOrder;
}
/**
* Sets the order in which the provided data objects should be drawn. The
* earlier you place them in the provided array, the further they will be in
* the background. e.g. if you provide new DrawOrer[] { DrawOrder.BAR,
* DrawOrder.LINE }, the bars will be drawn behind the lines.
*
* @param order
*/
public void setDrawOrder(DrawOrder[] order) {
if (order == null || order.length <= 0)
return;
mDrawOrder = order;
}
/**
* draws all MarkerViews on the highlighted positions
*/
protected void drawMarkers(Canvas canvas) {
// if there is no marker view or drawing marker is disabled
if (mMarker == null || !isDrawMarkersEnabled() || !valuesToHighlight())
return;
for (int i = 0; i < mIndicesToHighlight.length; i++) {
Highlight highlight = mIndicesToHighlight[i];
IDataSet set = mData.getDataSetByHighlight(highlight);
Entry e = mData.getEntryForHighlight(highlight);
if (e == null)
continue;
int entryIndex = set.getEntryIndex(e);
// make sure entry not null
if (entryIndex > set.getEntryCount() * mAnimator.getPhaseX())
continue;
float[] pos = getMarkerPosition(highlight);
// check bounds
if (!mViewPortHandler.isInBounds(pos[0], pos[1]))
continue;
// callbacks to update the content
mMarker.refreshContent(e, highlight);
// draw the marker
mMarker.draw(canvas, pos[0], pos[1]);
}
}
}

View File

@@ -0,0 +1,346 @@
package com.github.mikephil.charting.charts;
import android.content.Context;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import com.github.mikephil.charting.components.XAxis.XAxisPosition;
import com.github.mikephil.charting.components.YAxis.AxisDependency;
import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.highlight.HorizontalBarHighlighter;
import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
import com.github.mikephil.charting.renderer.HorizontalBarChartRenderer;
import com.github.mikephil.charting.renderer.XAxisRendererHorizontalBarChart;
import com.github.mikephil.charting.renderer.YAxisRendererHorizontalBarChart;
import com.github.mikephil.charting.utils.HorizontalViewPortHandler;
import com.github.mikephil.charting.utils.MPPointF;
import com.github.mikephil.charting.utils.TransformerHorizontalBarChart;
import com.github.mikephil.charting.utils.Utils;
/**
* BarChart with horizontal bar orientation. In this implementation, x- and y-axis are switched, meaning the YAxis class
* represents the horizontal values and the XAxis class represents the vertical values.
*
* @author Philipp Jahoda
*/
public class HorizontalBarChart extends BarChart {
public HorizontalBarChart(Context context) {
super(context);
}
public HorizontalBarChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public HorizontalBarChart(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void init() {
mViewPortHandler = new HorizontalViewPortHandler();
super.init();
mLeftAxisTransformer = new TransformerHorizontalBarChart(mViewPortHandler);
mRightAxisTransformer = new TransformerHorizontalBarChart(mViewPortHandler);
mRenderer = new HorizontalBarChartRenderer(this, mAnimator, mViewPortHandler);
setHighlighter(new HorizontalBarHighlighter(this));
mAxisRendererLeft = new YAxisRendererHorizontalBarChart(mViewPortHandler, mAxisLeft, mLeftAxisTransformer);
mAxisRendererRight = new YAxisRendererHorizontalBarChart(mViewPortHandler, mAxisRight, mRightAxisTransformer);
mXAxisRenderer = new XAxisRendererHorizontalBarChart(mViewPortHandler, mXAxis, mLeftAxisTransformer, this);
}
private RectF mOffsetsBuffer = new RectF();
protected void calculateLegendOffsets(RectF offsets) {
offsets.left = 0.f;
offsets.right = 0.f;
offsets.top = 0.f;
offsets.bottom = 0.f;
if (mLegend == null || !mLegend.isEnabled() || mLegend.isDrawInsideEnabled())
return;
switch (mLegend.getOrientation()) {
case VERTICAL:
switch (mLegend.getHorizontalAlignment()) {
case LEFT:
offsets.left += Math.min(mLegend.mNeededWidth,
mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent())
+ mLegend.getXOffset();
break;
case RIGHT:
offsets.right += Math.min(mLegend.mNeededWidth,
mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent())
+ mLegend.getXOffset();
break;
case CENTER:
switch (mLegend.getVerticalAlignment()) {
case TOP:
offsets.top += Math.min(mLegend.mNeededHeight,
mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent())
+ mLegend.getYOffset();
break;
case BOTTOM:
offsets.bottom += Math.min(mLegend.mNeededHeight,
mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent())
+ mLegend.getYOffset();
break;
default:
break;
}
}
break;
case HORIZONTAL:
switch (mLegend.getVerticalAlignment()) {
case TOP:
offsets.top += Math.min(mLegend.mNeededHeight,
mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent())
+ mLegend.getYOffset();
if (mAxisLeft.isEnabled() && mAxisLeft.isDrawLabelsEnabled())
offsets.top += mAxisLeft.getRequiredHeightSpace(
mAxisRendererLeft.getPaintAxisLabels());
break;
case BOTTOM:
offsets.bottom += Math.min(mLegend.mNeededHeight,
mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent())
+ mLegend.getYOffset();
if (mAxisRight.isEnabled() && mAxisRight.isDrawLabelsEnabled())
offsets.bottom += mAxisRight.getRequiredHeightSpace(
mAxisRendererRight.getPaintAxisLabels());
break;
default:
break;
}
break;
}
}
@Override
public void calculateOffsets() {
float offsetLeft = 0f, offsetRight = 0f, offsetTop = 0f, offsetBottom = 0f;
calculateLegendOffsets(mOffsetsBuffer);
offsetLeft += mOffsetsBuffer.left;
offsetTop += mOffsetsBuffer.top;
offsetRight += mOffsetsBuffer.right;
offsetBottom += mOffsetsBuffer.bottom;
// offsets for y-labels
if (mAxisLeft.needsOffset()) {
offsetTop += mAxisLeft.getRequiredHeightSpace(mAxisRendererLeft.getPaintAxisLabels());
}
if (mAxisRight.needsOffset()) {
offsetBottom += mAxisRight.getRequiredHeightSpace(mAxisRendererRight.getPaintAxisLabels());
}
float xlabelwidth = mXAxis.mLabelRotatedWidth;
if (mXAxis.isEnabled()) {
// offsets for x-labels
if (mXAxis.getPosition() == XAxisPosition.BOTTOM) {
offsetLeft += xlabelwidth;
} else if (mXAxis.getPosition() == XAxisPosition.TOP) {
offsetRight += xlabelwidth;
} else if (mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) {
offsetLeft += xlabelwidth;
offsetRight += xlabelwidth;
}
}
offsetTop += getExtraTopOffset();
offsetRight += getExtraRightOffset();
offsetBottom += getExtraBottomOffset();
offsetLeft += getExtraLeftOffset();
float minOffset = Utils.convertDpToPixel(mMinOffset);
mViewPortHandler.restrainViewPort(
Math.max(minOffset, offsetLeft),
Math.max(minOffset, offsetTop),
Math.max(minOffset, offsetRight),
Math.max(minOffset, offsetBottom));
if (mLogEnabled) {
Log.i(LOG_TAG, "offsetLeft: " + offsetLeft + ", offsetTop: " + offsetTop + ", offsetRight: " +
offsetRight + ", offsetBottom: "
+ offsetBottom);
Log.i(LOG_TAG, "Content: " + mViewPortHandler.getContentRect().toString());
}
prepareOffsetMatrix();
prepareValuePxMatrix();
}
@Override
protected void prepareValuePxMatrix() {
mRightAxisTransformer.prepareMatrixValuePx(mAxisRight.mAxisMinimum, mAxisRight.mAxisRange, mXAxis.mAxisRange,
mXAxis.mAxisMinimum);
mLeftAxisTransformer.prepareMatrixValuePx(mAxisLeft.mAxisMinimum, mAxisLeft.mAxisRange, mXAxis.mAxisRange,
mXAxis.mAxisMinimum);
}
@Override
protected float[] getMarkerPosition(Highlight high) {
return new float[]{high.getDrawY(), high.getDrawX()};
}
@Override
public void getBarBounds(BarEntry e, RectF outputRect) {
RectF bounds = outputRect;
IBarDataSet set = mData.getDataSetForEntry(e);
if (set == null) {
outputRect.set(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE);
return;
}
float y = e.getY();
float x = e.getX();
float barWidth = mData.getBarWidth();
float top = x - barWidth / 2f;
float bottom = x + barWidth / 2f;
float left = y >= 0 ? y : 0;
float right = y <= 0 ? y : 0;
bounds.set(left, top, right, bottom);
getTransformer(set.getAxisDependency()).rectValueToPixel(bounds);
}
protected float[] mGetPositionBuffer = new float[2];
/**
* Returns a recyclable MPPointF instance.
*
* @param e
* @param axis
* @return
*/
@Override
public MPPointF getPosition(Entry e, AxisDependency axis) {
if (e == null)
return null;
float[] vals = mGetPositionBuffer;
vals[0] = e.getY();
vals[1] = e.getX();
getTransformer(axis).pointValuesToPixel(vals);
return MPPointF.getInstance(vals[0], vals[1]);
}
/**
* Returns the Highlight object (contains x-index and DataSet index) of the selected value at the given touch point
* inside the BarChart.
*
* @param x
* @param y
* @return
*/
@Override
public Highlight getHighlightByTouchPoint(float x, float y) {
if (mData == null) {
if (mLogEnabled)
Log.e(LOG_TAG, "Can't select by touch. No data set.");
return null;
} else
return getHighlighter().getHighlight(y, x); // switch x and y
}
@Override
public float getLowestVisibleX() {
getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(),
mViewPortHandler.contentBottom(), posForGetLowestVisibleX);
float result = (float) Math.max(mXAxis.mAxisMinimum, posForGetLowestVisibleX.y);
return result;
}
@Override
public float getHighestVisibleX() {
getTransformer(AxisDependency.LEFT).getValuesByTouchPoint(mViewPortHandler.contentLeft(),
mViewPortHandler.contentTop(), posForGetHighestVisibleX);
float result = (float) Math.min(mXAxis.mAxisMaximum, posForGetHighestVisibleX.y);
return result;
}
/**
* ###### VIEWPORT METHODS BELOW THIS ######
*/
@Override
public void setVisibleXRangeMaximum(float maxXRange) {
float xScale = mXAxis.mAxisRange / (maxXRange);
mViewPortHandler.setMinimumScaleY(xScale);
}
@Override
public void setVisibleXRangeMinimum(float minXRange) {
float xScale = mXAxis.mAxisRange / (minXRange);
mViewPortHandler.setMaximumScaleY(xScale);
}
@Override
public void setVisibleXRange(float minXRange, float maxXRange) {
float minScale = mXAxis.mAxisRange / minXRange;
float maxScale = mXAxis.mAxisRange / maxXRange;
mViewPortHandler.setMinMaxScaleY(minScale, maxScale);
}
@Override
public void setVisibleYRangeMaximum(float maxYRange, AxisDependency axis) {
float yScale = getAxisRange(axis) / maxYRange;
mViewPortHandler.setMinimumScaleX(yScale);
}
@Override
public void setVisibleYRangeMinimum(float minYRange, AxisDependency axis) {
float yScale = getAxisRange(axis) / minYRange;
mViewPortHandler.setMaximumScaleX(yScale);
}
@Override
public void setVisibleYRange(float minYRange, float maxYRange, AxisDependency axis) {
float minScale = getAxisRange(axis) / minYRange;
float maxScale = getAxisRange(axis) / maxYRange;
mViewPortHandler.setMinMaxScaleX(minScale, maxScale);
}
}

View File

@@ -0,0 +1,50 @@
package com.github.mikephil.charting.charts;
import android.content.Context;
import android.util.AttributeSet;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider;
import com.github.mikephil.charting.renderer.LineChartRenderer;
/**
* Chart that draws lines, surfaces, circles, ...
*
* @author Philipp Jahoda
*/
public class LineChart extends BarLineChartBase<LineData> implements LineDataProvider {
public LineChart(Context context) {
super(context);
}
public LineChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LineChart(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void init() {
super.init();
mRenderer = new LineChartRenderer(this, mAnimator, mViewPortHandler);
}
@Override
public LineData getLineData() {
return mData;
}
@Override
protected void onDetachedFromWindow() {
// releases the bitmap in the renderer to avoid oom error
if (mRenderer != null && mRenderer instanceof LineChartRenderer) {
((LineChartRenderer) mRenderer).releaseBitmap();
}
super.onDetachedFromWindow();
}
}

View File

@@ -0,0 +1,804 @@
package com.github.mikephil.charting.charts;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.util.AttributeSet;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.data.PieData;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.highlight.PieHighlighter;
import com.github.mikephil.charting.interfaces.datasets.IPieDataSet;
import com.github.mikephil.charting.renderer.PieChartRenderer;
import com.github.mikephil.charting.utils.MPPointF;
import com.github.mikephil.charting.utils.Utils;
import java.util.List;
/**
* View that represents a pie chart. Draws cake like slices.
*
* @author Philipp Jahoda
*/
public class PieChart extends PieRadarChartBase<PieData> {
/**
* rect object that represents the bounds of the piechart, needed for
* drawing the circle
*/
private RectF mCircleBox = new RectF();
/**
* flag indicating if entry labels should be drawn or not
*/
private boolean mDrawEntryLabels = true;
/**
* array that holds the width of each pie-slice in degrees
*/
private float[] mDrawAngles = new float[1];
/**
* array that holds the absolute angle in degrees of each slice
*/
private float[] mAbsoluteAngles = new float[1];
/**
* if true, the white hole inside the chart will be drawn
*/
private boolean mDrawHole = true;
/**
* if true, the hole will see-through to the inner tips of the slices
*/
private boolean mDrawSlicesUnderHole = false;
/**
* if true, the values inside the piechart are drawn as percent values
*/
private boolean mUsePercentValues = false;
/**
* if true, the slices of the piechart are rounded
*/
private boolean mDrawRoundedSlices = false;
/**
* variable for the text that is drawn in the center of the pie-chart
*/
private CharSequence mCenterText = "";
private MPPointF mCenterTextOffset = MPPointF.getInstance(0, 0);
/**
* indicates the size of the hole in the center of the piechart, default:
* radius / 2
*/
private float mHoleRadiusPercent = 50f;
/**
* the radius of the transparent circle next to the chart-hole in the center
*/
protected float mTransparentCircleRadiusPercent = 55f;
/**
* if enabled, centertext is drawn
*/
private boolean mDrawCenterText = true;
private float mCenterTextRadiusPercent = 100.f;
protected float mMaxAngle = 360f;
/**
* Minimum angle to draw slices, this only works if there is enough room for all slices to have
* the minimum angle, default 0f.
*/
private float mMinAngleForSlices = 0f;
public PieChart(Context context) {
super(context);
}
public PieChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PieChart(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void init() {
super.init();
mRenderer = new PieChartRenderer(this, mAnimator, mViewPortHandler);
mXAxis = null;
mHighlighter = new PieHighlighter(this);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mData == null)
return;
mRenderer.drawData(canvas);
if (valuesToHighlight())
mRenderer.drawHighlighted(canvas, mIndicesToHighlight);
mRenderer.drawExtras(canvas);
mRenderer.drawValues(canvas);
mLegendRenderer.renderLegend(canvas);
drawDescription(canvas);
drawMarkers(canvas);
}
@Override
public void calculateOffsets() {
super.calculateOffsets();
// prevent nullpointer when no data set
if (mData == null)
return;
float diameter = getDiameter();
float radius = diameter / 2f;
MPPointF c = getCenterOffsets();
float shift = mData.getDataSet().getSelectionShift();
// create the circle box that will contain the pie-chart (the bounds of
// the pie-chart)
mCircleBox.set(c.x - radius + shift,
c.y - radius + shift,
c.x + radius - shift,
c.y + radius - shift);
MPPointF.recycleInstance(c);
}
@Override
protected void calcMinMax() {
calcAngles();
}
@Override
protected float[] getMarkerPosition(Highlight highlight) {
MPPointF center = getCenterCircleBox();
float r = getRadius();
float off = r / 10f * 3.6f;
if (isDrawHoleEnabled()) {
off = (r - (r / 100f * getHoleRadius())) / 2f;
}
r -= off; // offset to keep things inside the chart
float rotationAngle = getRotationAngle();
int entryIndex = (int) highlight.getX();
// offset needed to center the drawn text in the slice
float offset = mDrawAngles[entryIndex] / 2;
// calculate the text position
float x = (float) (r
* Math.cos(Math.toRadians((rotationAngle + mAbsoluteAngles[entryIndex] - offset)
* mAnimator.getPhaseY())) + center.x);
float y = (float) (r
* Math.sin(Math.toRadians((rotationAngle + mAbsoluteAngles[entryIndex] - offset)
* mAnimator.getPhaseY())) + center.y);
MPPointF.recycleInstance(center);
return new float[]{x, y};
}
/**
* calculates the needed angles for the chart slices
*/
private void calcAngles() {
int entryCount = mData.getEntryCount();
if (mDrawAngles.length != entryCount) {
mDrawAngles = new float[entryCount];
} else {
for (int i = 0; i < entryCount; i++) {
mDrawAngles[i] = 0;
}
}
if (mAbsoluteAngles.length != entryCount) {
mAbsoluteAngles = new float[entryCount];
} else {
for (int i = 0; i < entryCount; i++) {
mAbsoluteAngles[i] = 0;
}
}
float yValueSum = mData.getYValueSum();
List<IPieDataSet> dataSets = mData.getDataSets();
boolean hasMinAngle = mMinAngleForSlices != 0f && entryCount * mMinAngleForSlices <= mMaxAngle;
float[] minAngles = new float[entryCount];
int cnt = 0;
float offset = 0f;
float diff = 0f;
for (int i = 0; i < mData.getDataSetCount(); i++) {
IPieDataSet set = dataSets.get(i);
for (int j = 0; j < set.getEntryCount(); j++) {
float drawAngle = calcAngle(Math.abs(set.getEntryForIndex(j).getY()), yValueSum);
if (hasMinAngle) {
float temp = drawAngle - mMinAngleForSlices;
if (temp <= 0) {
minAngles[cnt] = mMinAngleForSlices;
offset += -temp;
} else {
minAngles[cnt] = drawAngle;
diff += temp;
}
}
mDrawAngles[cnt] = drawAngle;
if (cnt == 0) {
mAbsoluteAngles[cnt] = mDrawAngles[cnt];
} else {
mAbsoluteAngles[cnt] = mAbsoluteAngles[cnt - 1] + mDrawAngles[cnt];
}
cnt++;
}
}
if (hasMinAngle) {
// Correct bigger slices by relatively reducing their angles based on the total angle needed to subtract
// This requires that `entryCount * mMinAngleForSlices <= mMaxAngle` be true to properly work!
for (int i = 0; i < entryCount; i++) {
minAngles[i] -= (minAngles[i] - mMinAngleForSlices) / diff * offset;
if (i == 0) {
mAbsoluteAngles[0] = minAngles[0];
} else {
mAbsoluteAngles[i] = mAbsoluteAngles[i - 1] + minAngles[i];
}
}
mDrawAngles = minAngles;
}
}
/**
* Checks if the given index is set to be highlighted.
*
* @param index
* @return
*/
public boolean needsHighlight(int index) {
// no highlight
if (!valuesToHighlight())
return false;
for (int i = 0; i < mIndicesToHighlight.length; i++)
// check if the xvalue for the given dataset needs highlight
if ((int) mIndicesToHighlight[i].getX() == index)
return true;
return false;
}
/**
* calculates the needed angle for a given value
*
* @param value
* @return
*/
private float calcAngle(float value) {
return calcAngle(value, mData.getYValueSum());
}
/**
* calculates the needed angle for a given value
*
* @param value
* @param yValueSum
* @return
*/
private float calcAngle(float value, float yValueSum) {
return value / yValueSum * mMaxAngle;
}
/**
* This will throw an exception, PieChart has no XAxis object.
*
* @return
*/
@Deprecated
@Override
public XAxis getXAxis() {
throw new RuntimeException("PieChart has no XAxis");
}
@Override
public int getIndexForAngle(float angle) {
// take the current angle of the chart into consideration
float a = Utils.getNormalizedAngle(angle - getRotationAngle());
for (int i = 0; i < mAbsoluteAngles.length; i++) {
if (mAbsoluteAngles[i] > a)
return i;
}
return -1; // return -1 if no index found
}
/**
* Returns the index of the DataSet this x-index belongs to.
*
* @param xIndex
* @return
*/
public int getDataSetIndexForIndex(int xIndex) {
List<IPieDataSet> dataSets = mData.getDataSets();
for (int i = 0; i < dataSets.size(); i++) {
if (dataSets.get(i).getEntryForXValue(xIndex, Float.NaN) != null)
return i;
}
return -1;
}
/**
* returns an integer array of all the different angles the chart slices
* have the angles in the returned array determine how much space (of 360°)
* each slice takes
*
* @return
*/
public float[] getDrawAngles() {
return mDrawAngles;
}
/**
* returns the absolute angles of the different chart slices (where the
* slices end)
*
* @return
*/
public float[] getAbsoluteAngles() {
return mAbsoluteAngles;
}
/**
* Sets the color for the hole that is drawn in the center of the PieChart
* (if enabled).
*
* @param color
*/
public void setHoleColor(int color) {
((PieChartRenderer) mRenderer).getPaintHole().setColor(color);
}
/**
* Enable or disable the visibility of the inner tips of the slices behind the hole
*/
public void setDrawSlicesUnderHole(boolean enable) {
mDrawSlicesUnderHole = enable;
}
/**
* Returns true if the inner tips of the slices are visible behind the hole,
* false if not.
*
* @return true if slices are visible behind the hole.
*/
public boolean isDrawSlicesUnderHoleEnabled() {
return mDrawSlicesUnderHole;
}
/**
* set this to true to draw the pie center empty
*
* @param enabled
*/
public void setDrawHoleEnabled(boolean enabled) {
this.mDrawHole = enabled;
}
/**
* returns true if the hole in the center of the pie-chart is set to be
* visible, false if not
*
* @return
*/
public boolean isDrawHoleEnabled() {
return mDrawHole;
}
/**
* Sets the text String that is displayed in the center of the PieChart.
*
* @param text
*/
public void setCenterText(CharSequence text) {
if (text == null)
mCenterText = "";
else
mCenterText = text;
}
/**
* returns the text that is drawn in the center of the pie-chart
*
* @return
*/
public CharSequence getCenterText() {
return mCenterText;
}
/**
* set this to true to draw the text that is displayed in the center of the
* pie chart
*
* @param enabled
*/
public void setDrawCenterText(boolean enabled) {
this.mDrawCenterText = enabled;
}
/**
* returns true if drawing the center text is enabled
*
* @return
*/
public boolean isDrawCenterTextEnabled() {
return mDrawCenterText;
}
@Override
protected float getRequiredLegendOffset() {
return mLegendRenderer.getLabelPaint().getTextSize() * 2.f;
}
@Override
protected float getRequiredBaseOffset() {
return 0;
}
@Override
public float getRadius() {
if (mCircleBox == null)
return 0;
else
return Math.min(mCircleBox.width() / 2f, mCircleBox.height() / 2f);
}
/**
* returns the circlebox, the boundingbox of the pie-chart slices
*
* @return
*/
public RectF getCircleBox() {
return mCircleBox;
}
/**
* returns the center of the circlebox
*
* @return
*/
public MPPointF getCenterCircleBox() {
return MPPointF.getInstance(mCircleBox.centerX(), mCircleBox.centerY());
}
/**
* sets the typeface for the center-text paint
*
* @param t
*/
public void setCenterTextTypeface(Typeface t) {
((PieChartRenderer) mRenderer).getPaintCenterText().setTypeface(t);
}
/**
* Sets the size of the center text of the PieChart in dp.
*
* @param sizeDp
*/
public void setCenterTextSize(float sizeDp) {
((PieChartRenderer) mRenderer).getPaintCenterText().setTextSize(
Utils.convertDpToPixel(sizeDp));
}
/**
* Sets the size of the center text of the PieChart in pixels.
*
* @param sizePixels
*/
public void setCenterTextSizePixels(float sizePixels) {
((PieChartRenderer) mRenderer).getPaintCenterText().setTextSize(sizePixels);
}
/**
* Sets the offset the center text should have from it's original position in dp. Default x = 0, y = 0
*
* @param x
* @param y
*/
public void setCenterTextOffset(float x, float y) {
mCenterTextOffset.x = Utils.convertDpToPixel(x);
mCenterTextOffset.y = Utils.convertDpToPixel(y);
}
/**
* Returns the offset on the x- and y-axis the center text has in dp.
*
* @return
*/
public MPPointF getCenterTextOffset() {
return MPPointF.getInstance(mCenterTextOffset.x, mCenterTextOffset.y);
}
/**
* Sets the color of the center text of the PieChart.
*
* @param color
*/
public void setCenterTextColor(int color) {
((PieChartRenderer) mRenderer).getPaintCenterText().setColor(color);
}
/**
* sets the radius of the hole in the center of the piechart in percent of
* the maximum radius (max = the radius of the whole chart), default 50%
*
* @param percent
*/
public void setHoleRadius(final float percent) {
mHoleRadiusPercent = percent;
}
/**
* Returns the size of the hole radius in percent of the total radius.
*
* @return
*/
public float getHoleRadius() {
return mHoleRadiusPercent;
}
/**
* Sets the color the transparent-circle should have.
*
* @param color
*/
public void setTransparentCircleColor(int color) {
Paint p = ((PieChartRenderer) mRenderer).getPaintTransparentCircle();
int alpha = p.getAlpha();
p.setColor(color);
p.setAlpha(alpha);
}
/**
* sets the radius of the transparent circle that is drawn next to the hole
* in the piechart in percent of the maximum radius (max = the radius of the
* whole chart), default 55% -> means 5% larger than the center-hole by
* default
*
* @param percent
*/
public void setTransparentCircleRadius(final float percent) {
mTransparentCircleRadiusPercent = percent;
}
public float getTransparentCircleRadius() {
return mTransparentCircleRadiusPercent;
}
/**
* Sets the amount of transparency the transparent circle should have 0 = fully transparent,
* 255 = fully opaque.
* Default value is 100.
*
* @param alpha 0-255
*/
public void setTransparentCircleAlpha(int alpha) {
((PieChartRenderer) mRenderer).getPaintTransparentCircle().setAlpha(alpha);
}
/**
* Set this to true to draw the entry labels into the pie slices (Provided by the getLabel() method of the PieEntry class).
* Deprecated -> use setDrawEntryLabels(...) instead.
*
* @param enabled
*/
@Deprecated
public void setDrawSliceText(boolean enabled) {
mDrawEntryLabels = enabled;
}
/**
* Set this to true to draw the entry labels into the pie slices (Provided by the getLabel() method of the PieEntry class).
*
* @param enabled
*/
public void setDrawEntryLabels(boolean enabled) {
mDrawEntryLabels = enabled;
}
/**
* Returns true if drawing the entry labels is enabled, false if not.
*
* @return
*/
public boolean isDrawEntryLabelsEnabled() {
return mDrawEntryLabels;
}
/**
* Sets the color the entry labels are drawn with.
*
* @param color
*/
public void setEntryLabelColor(int color) {
((PieChartRenderer) mRenderer).getPaintEntryLabels().setColor(color);
}
/**
* Sets a custom Typeface for the drawing of the entry labels.
*
* @param tf
*/
public void setEntryLabelTypeface(Typeface tf) {
((PieChartRenderer) mRenderer).getPaintEntryLabels().setTypeface(tf);
}
/**
* Sets the size of the entry labels in dp. Default: 13dp
*
* @param size
*/
public void setEntryLabelTextSize(float size) {
((PieChartRenderer) mRenderer).getPaintEntryLabels().setTextSize(Utils.convertDpToPixel(size));
}
/**
* Sets whether to draw slices in a curved fashion, only works if drawing the hole is enabled
* and if the slices are not drawn under the hole.
*
* @param enabled draw curved ends of slices
*/
public void setDrawRoundedSlices(boolean enabled) {
mDrawRoundedSlices = enabled;
}
/**
* Returns true if the chart is set to draw each end of a pie-slice
* "rounded".
*
* @return
*/
public boolean isDrawRoundedSlicesEnabled() {
return mDrawRoundedSlices;
}
/**
* If this is enabled, values inside the PieChart are drawn in percent and
* not with their original value. Values provided for the IValueFormatter to
* format are then provided in percent.
*
* @param enabled
*/
public void setUsePercentValues(boolean enabled) {
mUsePercentValues = enabled;
}
/**
* Returns true if using percentage values is enabled for the chart.
*
* @return
*/
public boolean isUsePercentValuesEnabled() {
return mUsePercentValues;
}
/**
* the rectangular radius of the bounding box for the center text, as a percentage of the pie
* hole
* default 1.f (100%)
*/
public void setCenterTextRadiusPercent(float percent) {
mCenterTextRadiusPercent = percent;
}
/**
* the rectangular radius of the bounding box for the center text, as a percentage of the pie
* hole
* default 1.f (100%)
*/
public float getCenterTextRadiusPercent() {
return mCenterTextRadiusPercent;
}
public float getMaxAngle() {
return mMaxAngle;
}
/**
* Sets the max angle that is used for calculating the pie-circle. 360f means
* it's a full PieChart, 180f results in a half-pie-chart. Default: 360f
*
* @param maxangle min 90, max 360
*/
public void setMaxAngle(float maxangle) {
if (maxangle > 360)
maxangle = 360f;
if (maxangle < 90)
maxangle = 90f;
this.mMaxAngle = maxangle;
}
/**
* The minimum angle slices on the chart are rendered with, default is 0f.
*
* @return minimum angle for slices
*/
public float getMinAngleForSlices() {
return mMinAngleForSlices;
}
/**
* Set the angle to set minimum size for slices, you must call {@link #notifyDataSetChanged()}
* and {@link #invalidate()} when changing this, only works if there is enough room for all
* slices to have the minimum angle.
*
* @param minAngle minimum 0, maximum is half of {@link #setMaxAngle(float)}
*/
public void setMinAngleForSlices(float minAngle) {
if (minAngle > (mMaxAngle / 2f))
minAngle = mMaxAngle / 2f;
else if (minAngle < 0)
minAngle = 0f;
this.mMinAngleForSlices = minAngle;
}
@Override
protected void onDetachedFromWindow() {
// releases the bitmap in the renderer to avoid oom error
if (mRenderer != null && mRenderer instanceof PieChartRenderer) {
((PieChartRenderer) mRenderer).releaseBitmap();
}
super.onDetachedFromWindow();
}
}

View File

@@ -0,0 +1,499 @@
package com.github.mikephil.charting.charts;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import com.github.mikephil.charting.animation.Easing;
import com.github.mikephil.charting.animation.Easing.EasingFunction;
import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.data.ChartData;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.interfaces.datasets.IDataSet;
import com.github.mikephil.charting.listener.PieRadarChartTouchListener;
import com.github.mikephil.charting.utils.MPPointF;
import com.github.mikephil.charting.utils.Utils;
/**
* Baseclass of PieChart and RadarChart.
*
* @author Philipp Jahoda
*/
public abstract class PieRadarChartBase<T extends ChartData<? extends IDataSet<? extends Entry>>>
extends Chart<T> {
/**
* holds the normalized version of the current rotation angle of the chart
*/
private float mRotationAngle = 270f;
/**
* holds the raw version of the current rotation angle of the chart
*/
private float mRawRotationAngle = 270f;
/**
* flag that indicates if rotation is enabled or not
*/
protected boolean mRotateEnabled = true;
/**
* Sets the minimum offset (padding) around the chart, defaults to 0.f
*/
protected float mMinOffset = 0.f;
public PieRadarChartBase(Context context) {
super(context);
}
public PieRadarChartBase(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PieRadarChartBase(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void init() {
super.init();
mChartTouchListener = new PieRadarChartTouchListener(this);
}
@Override
protected void calcMinMax() {
//mXAxis.mAxisRange = mData.getXVals().size() - 1;
}
@Override
public int getMaxVisibleCount() {
return mData.getEntryCount();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// use the pie- and radarchart listener own listener
if (mTouchEnabled && mChartTouchListener != null)
return mChartTouchListener.onTouch(this, event);
else
return super.onTouchEvent(event);
}
@Override
public void computeScroll() {
if (mChartTouchListener instanceof PieRadarChartTouchListener)
((PieRadarChartTouchListener) mChartTouchListener).computeScroll();
}
@Override
public void notifyDataSetChanged() {
if (mData == null)
return;
calcMinMax();
if (mLegend != null)
mLegendRenderer.computeLegend(mData);
calculateOffsets();
}
@Override
public void calculateOffsets() {
float legendLeft = 0f, legendRight = 0f, legendBottom = 0f, legendTop = 0f;
if (mLegend != null && mLegend.isEnabled() && !mLegend.isDrawInsideEnabled()) {
float fullLegendWidth = Math.min(mLegend.mNeededWidth,
mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent());
switch (mLegend.getOrientation()) {
case VERTICAL: {
float xLegendOffset = 0.f;
if (mLegend.getHorizontalAlignment() == Legend.LegendHorizontalAlignment.LEFT
|| mLegend.getHorizontalAlignment() == Legend.LegendHorizontalAlignment.RIGHT) {
if (mLegend.getVerticalAlignment() == Legend.LegendVerticalAlignment.CENTER) {
// this is the space between the legend and the chart
final float spacing = Utils.convertDpToPixel(13f);
xLegendOffset = fullLegendWidth + spacing;
} else {
// this is the space between the legend and the chart
float spacing = Utils.convertDpToPixel(8f);
float legendWidth = fullLegendWidth + spacing;
float legendHeight = mLegend.mNeededHeight + mLegend.mTextHeightMax;
MPPointF center = getCenter();
float bottomX = mLegend.getHorizontalAlignment() ==
Legend.LegendHorizontalAlignment.RIGHT
? getWidth() - legendWidth + 15.f
: legendWidth - 15.f;
float bottomY = legendHeight + 15.f;
float distLegend = distanceToCenter(bottomX, bottomY);
MPPointF reference = getPosition(center, getRadius(),
getAngleForPoint(bottomX, bottomY));
float distReference = distanceToCenter(reference.x, reference.y);
float minOffset = Utils.convertDpToPixel(5f);
if (bottomY >= center.y && getHeight() - legendWidth > getWidth()) {
xLegendOffset = legendWidth;
} else if (distLegend < distReference) {
float diff = distReference - distLegend;
xLegendOffset = minOffset + diff;
}
MPPointF.recycleInstance(center);
MPPointF.recycleInstance(reference);
}
}
switch (mLegend.getHorizontalAlignment()) {
case LEFT:
legendLeft = xLegendOffset;
break;
case RIGHT:
legendRight = xLegendOffset;
break;
case CENTER:
switch (mLegend.getVerticalAlignment()) {
case TOP:
legendTop = Math.min(mLegend.mNeededHeight,
mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent());
break;
case BOTTOM:
legendBottom = Math.min(mLegend.mNeededHeight,
mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent());
break;
}
break;
}
}
break;
case HORIZONTAL:
float yLegendOffset = 0.f;
if (mLegend.getVerticalAlignment() == Legend.LegendVerticalAlignment.TOP ||
mLegend.getVerticalAlignment() == Legend.LegendVerticalAlignment.BOTTOM) {
// It's possible that we do not need this offset anymore as it
// is available through the extraOffsets, but changing it can mean
// changing default visibility for existing apps.
float yOffset = getRequiredLegendOffset();
yLegendOffset = Math.min(mLegend.mNeededHeight + yOffset,
mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent());
switch (mLegend.getVerticalAlignment()) {
case TOP:
legendTop = yLegendOffset;
break;
case BOTTOM:
legendBottom = yLegendOffset;
break;
}
}
break;
}
legendLeft += getRequiredBaseOffset();
legendRight += getRequiredBaseOffset();
legendTop += getRequiredBaseOffset();
legendBottom += getRequiredBaseOffset();
}
float minOffset = Utils.convertDpToPixel(mMinOffset);
if (this instanceof RadarChart) {
XAxis x = this.getXAxis();
if (x.isEnabled() && x.isDrawLabelsEnabled()) {
minOffset = Math.max(minOffset, x.mLabelRotatedWidth);
}
}
legendTop += getExtraTopOffset();
legendRight += getExtraRightOffset();
legendBottom += getExtraBottomOffset();
legendLeft += getExtraLeftOffset();
float offsetLeft = Math.max(minOffset, legendLeft);
float offsetTop = Math.max(minOffset, legendTop);
float offsetRight = Math.max(minOffset, legendRight);
float offsetBottom = Math.max(minOffset, Math.max(getRequiredBaseOffset(), legendBottom));
mViewPortHandler.restrainViewPort(offsetLeft, offsetTop, offsetRight, offsetBottom);
if (mLogEnabled)
Log.i(LOG_TAG, "offsetLeft: " + offsetLeft + ", offsetTop: " + offsetTop
+ ", offsetRight: " + offsetRight + ", offsetBottom: " + offsetBottom);
}
/**
* returns the angle relative to the chart center for the given point on the
* chart in degrees. The angle is always between 0 and 360°, 0° is NORTH,
* 90° is EAST, ...
*
* @param x
* @param y
* @return
*/
public float getAngleForPoint(float x, float y) {
MPPointF c = getCenterOffsets();
double tx = x - c.x, ty = y - c.y;
double length = Math.sqrt(tx * tx + ty * ty);
double r = Math.acos(ty / length);
float angle = (float) Math.toDegrees(r);
if (x > c.x)
angle = 360f - angle;
// add 90° because chart starts EAST
angle = angle + 90f;
// neutralize overflow
if (angle > 360f)
angle = angle - 360f;
MPPointF.recycleInstance(c);
return angle;
}
/**
* Returns a recyclable MPPointF instance.
* Calculates the position around a center point, depending on the distance
* from the center, and the angle of the position around the center.
*
* @param center
* @param dist
* @param angle in degrees, converted to radians internally
* @return
*/
public MPPointF getPosition(MPPointF center, float dist, float angle) {
MPPointF p = MPPointF.getInstance(0, 0);
getPosition(center, dist, angle, p);
return p;
}
public void getPosition(MPPointF center, float dist, float angle, MPPointF outputPoint) {
outputPoint.x = (float) (center.x + dist * Math.cos(Math.toRadians(angle)));
outputPoint.y = (float) (center.y + dist * Math.sin(Math.toRadians(angle)));
}
/**
* Returns the distance of a certain point on the chart to the center of the
* chart.
*
* @param x
* @param y
* @return
*/
public float distanceToCenter(float x, float y) {
MPPointF c = getCenterOffsets();
float dist = 0f;
float xDist = 0f;
float yDist = 0f;
if (x > c.x) {
xDist = x - c.x;
} else {
xDist = c.x - x;
}
if (y > c.y) {
yDist = y - c.y;
} else {
yDist = c.y - y;
}
// pythagoras
dist = (float) Math.sqrt(Math.pow(xDist, 2.0) + Math.pow(yDist, 2.0));
MPPointF.recycleInstance(c);
return dist;
}
/**
* Returns the xIndex for the given angle around the center of the chart.
* Returns -1 if not found / outofbounds.
*
* @param angle
* @return
*/
public abstract int getIndexForAngle(float angle);
/**
* Set an offset for the rotation of the RadarChart in degrees. Default 270f
* --> top (NORTH)
*
* @param angle
*/
public void setRotationAngle(float angle) {
mRawRotationAngle = angle;
mRotationAngle = Utils.getNormalizedAngle(mRawRotationAngle);
}
/**
* gets the raw version of the current rotation angle of the pie chart the
* returned value could be any value, negative or positive, outside of the
* 360 degrees. this is used when working with rotation direction, mainly by
* gestures and animations.
*
* @return
*/
public float getRawRotationAngle() {
return mRawRotationAngle;
}
/**
* gets a normalized version of the current rotation angle of the pie chart,
* which will always be between 0.0 < 360.0
*
* @return
*/
public float getRotationAngle() {
return mRotationAngle;
}
/**
* Set this to true to enable the rotation / spinning of the chart by touch.
* Set it to false to disable it. Default: true
*
* @param enabled
*/
public void setRotationEnabled(boolean enabled) {
mRotateEnabled = enabled;
}
/**
* Returns true if rotation of the chart by touch is enabled, false if not.
*
* @return
*/
public boolean isRotationEnabled() {
return mRotateEnabled;
}
/**
* Gets the minimum offset (padding) around the chart, defaults to 0.f
*/
public float getMinOffset() {
return mMinOffset;
}
/**
* Sets the minimum offset (padding) around the chart, defaults to 0.f
*/
public void setMinOffset(float minOffset) {
mMinOffset = minOffset;
}
/**
* returns the diameter of the pie- or radar-chart
*
* @return
*/
public float getDiameter() {
RectF content = mViewPortHandler.getContentRect();
content.left += getExtraLeftOffset();
content.top += getExtraTopOffset();
content.right -= getExtraRightOffset();
content.bottom -= getExtraBottomOffset();
return Math.min(content.width(), content.height());
}
/**
* Returns the radius of the chart in pixels.
*
* @return
*/
public abstract float getRadius();
/**
* Returns the required offset for the chart legend.
*
* @return
*/
protected abstract float getRequiredLegendOffset();
/**
* Returns the base offset needed for the chart without calculating the
* legend size.
*
* @return
*/
protected abstract float getRequiredBaseOffset();
@Override
public float getYChartMax() {
// TODO Auto-generated method stub
return 0;
}
@Override
public float getYChartMin() {
// TODO Auto-generated method stub
return 0;
}
/**
* ################ ################ ################ ################
*/
/** CODE BELOW THIS RELATED TO ANIMATION */
/**
* Applys a spin animation to the Chart.
*
* @param durationmillis
* @param fromangle
* @param toangle
*/
@SuppressLint("NewApi")
public void spin(int durationmillis, float fromangle, float toangle, EasingFunction easing) {
setRotationAngle(fromangle);
ObjectAnimator spinAnimator = ObjectAnimator.ofFloat(this, "rotationAngle", fromangle,
toangle);
spinAnimator.setDuration(durationmillis);
spinAnimator.setInterpolator(easing);
spinAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
postInvalidate();
}
});
spinAnimator.start();
}
}

View File

@@ -0,0 +1,362 @@
package com.github.mikephil.charting.charts;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.RectF;
import android.util.AttributeSet;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.components.YAxis.AxisDependency;
import com.github.mikephil.charting.data.RadarData;
import com.github.mikephil.charting.highlight.RadarHighlighter;
import com.github.mikephil.charting.renderer.RadarChartRenderer;
import com.github.mikephil.charting.renderer.XAxisRendererRadarChart;
import com.github.mikephil.charting.renderer.YAxisRendererRadarChart;
import com.github.mikephil.charting.utils.Utils;
/**
* Implementation of the RadarChart, a "spidernet"-like chart. It works best
* when displaying 5-10 entries per DataSet.
*
* @author Philipp Jahoda
*/
public class RadarChart extends PieRadarChartBase<RadarData> {
/**
* width of the main web lines
*/
private float mWebLineWidth = 2.5f;
/**
* width of the inner web lines
*/
private float mInnerWebLineWidth = 1.5f;
/**
* color for the main web lines
*/
private int mWebColor = Color.rgb(122, 122, 122);
/**
* color for the inner web
*/
private int mWebColorInner = Color.rgb(122, 122, 122);
/**
* transparency the grid is drawn with (0-255)
*/
private int mWebAlpha = 150;
/**
* flag indicating if the web lines should be drawn or not
*/
private boolean mDrawWeb = true;
/**
* modulus that determines how many labels and web-lines are skipped before the next is drawn
*/
private int mSkipWebLineCount = 0;
/**
* the object reprsenting the y-axis labels
*/
private YAxis mYAxis;
protected YAxisRendererRadarChart mYAxisRenderer;
protected XAxisRendererRadarChart mXAxisRenderer;
public RadarChart(Context context) {
super(context);
}
public RadarChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RadarChart(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void init() {
super.init();
mYAxis = new YAxis(AxisDependency.LEFT);
mYAxis.setLabelXOffset(10f);
mWebLineWidth = Utils.convertDpToPixel(1.5f);
mInnerWebLineWidth = Utils.convertDpToPixel(0.75f);
mRenderer = new RadarChartRenderer(this, mAnimator, mViewPortHandler);
mYAxisRenderer = new YAxisRendererRadarChart(mViewPortHandler, mYAxis, this);
mXAxisRenderer = new XAxisRendererRadarChart(mViewPortHandler, mXAxis, this);
mHighlighter = new RadarHighlighter(this);
}
@Override
protected void calcMinMax() {
super.calcMinMax();
mYAxis.calculate(mData.getYMin(AxisDependency.LEFT), mData.getYMax(AxisDependency.LEFT));
mXAxis.calculate(0, mData.getMaxEntryCountSet().getEntryCount());
}
@Override
public void notifyDataSetChanged() {
if (mData == null)
return;
calcMinMax();
mYAxisRenderer.computeAxis(mYAxis.mAxisMinimum, mYAxis.mAxisMaximum, mYAxis.isInverted());
mXAxisRenderer.computeAxis(mXAxis.mAxisMinimum, mXAxis.mAxisMaximum, false);
if (mLegend != null && !mLegend.isLegendCustom())
mLegendRenderer.computeLegend(mData);
calculateOffsets();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mData == null)
return;
// if (mYAxis.isEnabled())
// mYAxisRenderer.computeAxis(mYAxis.mAxisMinimum, mYAxis.mAxisMaximum, mYAxis.isInverted());
if (mXAxis.isEnabled())
mXAxisRenderer.computeAxis(mXAxis.mAxisMinimum, mXAxis.mAxisMaximum, false);
mXAxisRenderer.renderAxisLabels(canvas);
if (mDrawWeb)
mRenderer.drawExtras(canvas);
if (mYAxis.isEnabled() && mYAxis.isDrawLimitLinesBehindDataEnabled())
mYAxisRenderer.renderLimitLines(canvas);
mRenderer.drawData(canvas);
if (valuesToHighlight())
mRenderer.drawHighlighted(canvas, mIndicesToHighlight);
if (mYAxis.isEnabled() && !mYAxis.isDrawLimitLinesBehindDataEnabled())
mYAxisRenderer.renderLimitLines(canvas);
mYAxisRenderer.renderAxisLabels(canvas);
mRenderer.drawValues(canvas);
mLegendRenderer.renderLegend(canvas);
drawDescription(canvas);
drawMarkers(canvas);
}
/**
* Returns the factor that is needed to transform values into pixels.
*
* @return
*/
public float getFactor() {
RectF content = mViewPortHandler.getContentRect();
return Math.min(content.width() / 2f, content.height() / 2f) / mYAxis.mAxisRange;
}
/**
* Returns the angle that each slice in the radar chart occupies.
*
* @return
*/
public float getSliceAngle() {
return 360f / (float) mData.getMaxEntryCountSet().getEntryCount();
}
@Override
public int getIndexForAngle(float angle) {
// take the current angle of the chart into consideration
float a = Utils.getNormalizedAngle(angle - getRotationAngle());
float sliceangle = getSliceAngle();
int max = mData.getMaxEntryCountSet().getEntryCount();
int index = 0;
for (int i = 0; i < max; i++) {
float referenceAngle = sliceangle * (i + 1) - sliceangle / 2f;
if (referenceAngle > a) {
index = i;
break;
}
}
return index;
}
/**
* Returns the object that represents all y-labels of the RadarChart.
*
* @return
*/
public YAxis getYAxis() {
return mYAxis;
}
/**
* Sets the width of the web lines that come from the center.
*
* @param width
*/
public void setWebLineWidth(float width) {
mWebLineWidth = Utils.convertDpToPixel(width);
}
public float getWebLineWidth() {
return mWebLineWidth;
}
/**
* Sets the width of the web lines that are in between the lines coming from
* the center.
*
* @param width
*/
public void setWebLineWidthInner(float width) {
mInnerWebLineWidth = Utils.convertDpToPixel(width);
}
public float getWebLineWidthInner() {
return mInnerWebLineWidth;
}
/**
* Sets the transparency (alpha) value for all web lines, default: 150, 255
* = 100% opaque, 0 = 100% transparent
*
* @param alpha
*/
public void setWebAlpha(int alpha) {
mWebAlpha = alpha;
}
/**
* Returns the alpha value for all web lines.
*
* @return
*/
public int getWebAlpha() {
return mWebAlpha;
}
/**
* Sets the color for the web lines that come from the center. Don't forget
* to use getResources().getColor(...) when loading a color from the
* resources. Default: Color.rgb(122, 122, 122)
*
* @param color
*/
public void setWebColor(int color) {
mWebColor = color;
}
public int getWebColor() {
return mWebColor;
}
/**
* Sets the color for the web lines in between the lines that come from the
* center. Don't forget to use getResources().getColor(...) when loading a
* color from the resources. Default: Color.rgb(122, 122, 122)
*
* @param color
*/
public void setWebColorInner(int color) {
mWebColorInner = color;
}
public int getWebColorInner() {
return mWebColorInner;
}
/**
* If set to true, drawing the web is enabled, if set to false, drawing the
* whole web is disabled. Default: true
*
* @param enabled
*/
public void setDrawWeb(boolean enabled) {
mDrawWeb = enabled;
}
/**
* Sets the number of web-lines that should be skipped on chart web before the
* next one is drawn. This targets the lines that come from the center of the RadarChart.
*
* @param count if count = 1 -> 1 line is skipped in between
*/
public void setSkipWebLineCount(int count) {
mSkipWebLineCount = Math.max(0, count);
}
/**
* Returns the modulus that is used for skipping web-lines.
*
* @return
*/
public int getSkipWebLineCount() {
return mSkipWebLineCount;
}
@Override
protected float getRequiredLegendOffset() {
return mLegendRenderer.getLabelPaint().getTextSize() * 4.f;
}
@Override
protected float getRequiredBaseOffset() {
return mXAxis.isEnabled() && mXAxis.isDrawLabelsEnabled() ?
mXAxis.mLabelRotatedWidth :
Utils.convertDpToPixel(10f);
}
@Override
public float getRadius() {
RectF content = mViewPortHandler.getContentRect();
return Math.min(content.width() / 2f, content.height() / 2f);
}
/**
* Returns the maximum value this chart can display on it's y-axis.
*/
public float getYChartMax() {
return mYAxis.mAxisMaximum;
}
/**
* Returns the minimum value this chart can display on it's y-axis.
*/
public float getYChartMin() {
return mYAxis.mAxisMinimum;
}
/**
* Returns the range of y-values this chart can display.
*
* @return
*/
public float getYRange() {
return mYAxis.mAxisRange;
}
}

View File

@@ -0,0 +1,77 @@
package com.github.mikephil.charting.charts;
import android.content.Context;
import android.util.AttributeSet;
import com.github.mikephil.charting.data.ScatterData;
import com.github.mikephil.charting.interfaces.dataprovider.ScatterDataProvider;
import com.github.mikephil.charting.renderer.ScatterChartRenderer;
/**
* The ScatterChart. Draws dots, triangles, squares and custom shapes into the
* Chart-View. CIRCLE and SCQUARE offer the best performance, TRIANGLE has the
* worst performance.
*
* @author Philipp Jahoda
*/
public class ScatterChart extends BarLineChartBase<ScatterData> implements ScatterDataProvider {
public ScatterChart(Context context) {
super(context);
}
public ScatterChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ScatterChart(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void init() {
super.init();
mRenderer = new ScatterChartRenderer(this, mAnimator, mViewPortHandler);
getXAxis().setSpaceMin(0.5f);
getXAxis().setSpaceMax(0.5f);
}
@Override
public ScatterData getScatterData() {
return mData;
}
/**
* Predefined ScatterShapes that allow the specification of a shape a ScatterDataSet should be drawn with.
* If a ScatterShape is specified for a ScatterDataSet, the required renderer is set.
*/
public enum ScatterShape {
SQUARE("SQUARE"),
CIRCLE("CIRCLE"),
TRIANGLE("TRIANGLE"),
CROSS("CROSS"),
X("X"),
CHEVRON_UP("CHEVRON_UP"),
CHEVRON_DOWN("CHEVRON_DOWN");
private final String shapeIdentifier;
ScatterShape(final String shapeIdentifier) {
this.shapeIdentifier = shapeIdentifier;
}
@Override
public String toString() {
return shapeIdentifier;
}
public static ScatterShape[] getAllDefaultShapes() {
return new ScatterShape[]{SQUARE, CIRCLE, TRIANGLE, CROSS, X, CHEVRON_UP, CHEVRON_DOWN};
}
}
}

View File

@@ -0,0 +1,816 @@
package com.github.mikephil.charting.components;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.util.Log;
import com.github.mikephil.charting.formatter.DefaultAxisValueFormatter;
import com.github.mikephil.charting.formatter.IAxisValueFormatter;
import com.github.mikephil.charting.utils.Utils;
import java.util.ArrayList;
import java.util.List;
/**
* Base-class of all axes (previously called labels).
*
* @author Philipp Jahoda
*/
public abstract class AxisBase extends ComponentBase {
/**
* custom formatter that is used instead of the auto-formatter if set
*/
protected IAxisValueFormatter mAxisValueFormatter;
private int mGridColor = Color.GRAY;
private float mGridLineWidth = 1f;
private int mAxisLineColor = Color.GRAY;
private float mAxisLineWidth = 1f;
/**
* the actual array of entries
*/
public float[] mEntries = new float[]{};
/**
* axis label entries only used for centered labels
*/
public float[] mCenteredEntries = new float[]{};
/**
* the number of entries the legend contains
*/
public int mEntryCount;
/**
* the number of decimal digits to use
*/
public int mDecimals;
/**
* the number of label entries the axis should have, default 6
*/
private int mLabelCount = 6;
/**
* the minimum interval between axis values
*/
protected float mGranularity = 1.0f;
/**
* When true, axis labels are controlled by the `granularity` property.
* When false, axis values could possibly be repeated.
* This could happen if two adjacent axis values are rounded to same value.
* If using granularity this could be avoided by having fewer axis values visible.
*/
protected boolean mGranularityEnabled = false;
/**
* if true, the set number of y-labels will be forced
*/
protected boolean mForceLabels = false;
/**
* flag indicating if the grid lines for this axis should be drawn
*/
protected boolean mDrawGridLines = true;
/**
* flag that indicates if the line alongside the axis is drawn or not
*/
protected boolean mDrawAxisLine = true;
/**
* flag that indicates of the labels of this axis should be drawn or not
*/
protected boolean mDrawLabels = true;
protected boolean mCenterAxisLabels = false;
/**
* the path effect of the axis line that makes dashed lines possible
*/
private DashPathEffect mAxisLineDashPathEffect = null;
/**
* the path effect of the grid lines that makes dashed lines possible
*/
private DashPathEffect mGridDashPathEffect = null;
/**
* array of limit lines that can be set for the axis
*/
protected List<LimitLine> mLimitLines;
/**
* flag indicating the limit lines layer depth
*/
protected boolean mDrawLimitLineBehindData = false;
/**
* flag indicating the grid lines layer depth
*/
protected boolean mDrawGridLinesBehindData = true;
/**
* Extra spacing for `axisMinimum` to be added to automatically calculated `axisMinimum`
*/
protected float mSpaceMin = 0.f;
/**
* Extra spacing for `axisMaximum` to be added to automatically calculated `axisMaximum`
*/
protected float mSpaceMax = 0.f;
/**
* flag indicating that the axis-min value has been customized
*/
protected boolean mCustomAxisMin = false;
/**
* flag indicating that the axis-max value has been customized
*/
protected boolean mCustomAxisMax = false;
/**
* don't touch this direclty, use setter
*/
public float mAxisMaximum = 0f;
/**
* don't touch this directly, use setter
*/
public float mAxisMinimum = 0f;
/**
* the total range of values this axis covers
*/
public float mAxisRange = 0f;
private int mAxisMinLabels = 2;
private int mAxisMaxLabels = 25;
/**
* The minumum number of labels on the axis
*/
public int getAxisMinLabels() {
return mAxisMinLabels;
}
/**
* The minumum number of labels on the axis
*/
public void setAxisMinLabels(int labels) {
if (labels > 0)
mAxisMinLabels = labels;
}
/**
* The maximum number of labels on the axis
*/
public int getAxisMaxLabels() {
return mAxisMaxLabels;
}
/**
* The maximum number of labels on the axis
*/
public void setAxisMaxLabels(int labels) {
if (labels > 0)
mAxisMaxLabels = labels;
}
/**
* default constructor
*/
public AxisBase() {
this.mTextSize = Utils.convertDpToPixel(10f);
this.mXOffset = Utils.convertDpToPixel(5f);
this.mYOffset = Utils.convertDpToPixel(5f);
this.mLimitLines = new ArrayList<LimitLine>();
}
/**
* Set this to true to enable drawing the grid lines for this axis.
*
* @param enabled
*/
public void setDrawGridLines(boolean enabled) {
mDrawGridLines = enabled;
}
/**
* Returns true if drawing grid lines is enabled for this axis.
*
* @return
*/
public boolean isDrawGridLinesEnabled() {
return mDrawGridLines;
}
/**
* Set this to true if the line alongside the axis should be drawn or not.
*
* @param enabled
*/
public void setDrawAxisLine(boolean enabled) {
mDrawAxisLine = enabled;
}
/**
* Returns true if the line alongside the axis should be drawn.
*
* @return
*/
public boolean isDrawAxisLineEnabled() {
return mDrawAxisLine;
}
/**
* Centers the axis labels instead of drawing them at their original position.
* This is useful especially for grouped BarChart.
*
* @param enabled
*/
public void setCenterAxisLabels(boolean enabled) {
mCenterAxisLabels = enabled;
}
public boolean isCenterAxisLabelsEnabled() {
return mCenterAxisLabels && mEntryCount > 0;
}
/**
* Sets the color of the grid lines for this axis (the horizontal lines
* coming from each label).
*
* @param color
*/
public void setGridColor(int color) {
mGridColor = color;
}
/**
* Returns the color of the grid lines for this axis (the horizontal lines
* coming from each label).
*
* @return
*/
public int getGridColor() {
return mGridColor;
}
/**
* Sets the width of the border surrounding the chart in dp.
*
* @param width
*/
public void setAxisLineWidth(float width) {
mAxisLineWidth = Utils.convertDpToPixel(width);
}
/**
* Returns the width of the axis line (line alongside the axis).
*
* @return
*/
public float getAxisLineWidth() {
return mAxisLineWidth;
}
/**
* Sets the width of the grid lines that are drawn away from each axis
* label.
*
* @param width
*/
public void setGridLineWidth(float width) {
mGridLineWidth = Utils.convertDpToPixel(width);
}
/**
* Returns the width of the grid lines that are drawn away from each axis
* label.
*
* @return
*/
public float getGridLineWidth() {
return mGridLineWidth;
}
/**
* Sets the color of the border surrounding the chart.
*
* @param color
*/
public void setAxisLineColor(int color) {
mAxisLineColor = color;
}
/**
* Returns the color of the axis line (line alongside the axis).
*
* @return
*/
public int getAxisLineColor() {
return mAxisLineColor;
}
/**
* Set this to true to enable drawing the labels of this axis (this will not
* affect drawing the grid lines or axis lines).
*
* @param enabled
*/
public void setDrawLabels(boolean enabled) {
mDrawLabels = enabled;
}
/**
* Returns true if drawing the labels is enabled for this axis.
*
* @return
*/
public boolean isDrawLabelsEnabled() {
return mDrawLabels;
}
/**
* Sets the number of label entries for the y-axis max = 25, min = 2, default: 6, be aware
* that this number is not fixed.
*
* @param count the number of y-axis labels that should be displayed
*/
public void setLabelCount(int count) {
if (count > getAxisMaxLabels())
count = getAxisMaxLabels();
if (count < getAxisMinLabels())
count = getAxisMinLabels();
mLabelCount = count;
mForceLabels = false;
}
/**
* sets the number of label entries for the y-axis max = 25, min = 2, default: 6, be aware
* that this number is not
* fixed (if force == false) and can only be approximated.
*
* @param count the number of y-axis labels that should be displayed
* @param force if enabled, the set label count will be forced, meaning that the exact
* specified count of labels will
* be drawn and evenly distributed alongside the axis - this might cause labels
* to have uneven values
*/
public void setLabelCount(int count, boolean force) {
setLabelCount(count);
mForceLabels = force;
}
/**
* Returns true if focing the y-label count is enabled. Default: false
*
* @return
*/
public boolean isForceLabelsEnabled() {
return mForceLabels;
}
/**
* Returns the number of label entries the y-axis should have
*
* @return
*/
public int getLabelCount() {
return mLabelCount;
}
/**
* @return true if granularity is enabled
*/
public boolean isGranularityEnabled() {
return mGranularityEnabled;
}
/**
* Enabled/disable granularity control on axis value intervals. If enabled, the axis
* interval is not allowed to go below a certain granularity. Default: false
*
* @param enabled
*/
public void setGranularityEnabled(boolean enabled) {
mGranularityEnabled = enabled;
}
/**
* @return the minimum interval between axis values
*/
public float getGranularity() {
return mGranularity;
}
/**
* Set a minimum interval for the axis when zooming in. The axis is not allowed to go below
* that limit. This can be used to avoid label duplicating when zooming in.
*
* @param granularity
*/
public void setGranularity(float granularity) {
mGranularity = granularity;
// set this to true if it was disabled, as it makes no sense to call this method with granularity disabled
mGranularityEnabled = true;
}
/**
* Adds a new LimitLine to this axis.
*
* @param l
*/
public void addLimitLine(LimitLine l) {
mLimitLines.add(l);
if (mLimitLines.size() > 6) {
Log.e("MPAndroiChart",
"Warning! You have more than 6 LimitLines on your axis, do you really want " +
"that?");
}
}
/**
* Removes the specified LimitLine from the axis.
*
* @param l
*/
public void removeLimitLine(LimitLine l) {
mLimitLines.remove(l);
}
/**
* Removes all LimitLines from the axis.
*/
public void removeAllLimitLines() {
mLimitLines.clear();
}
/**
* Returns the LimitLines of this axis.
*
* @return
*/
public List<LimitLine> getLimitLines() {
return mLimitLines;
}
/**
* If this is set to true, the LimitLines are drawn behind the actual data,
* otherwise on top. Default: false
*
* @param enabled
*/
public void setDrawLimitLinesBehindData(boolean enabled) {
mDrawLimitLineBehindData = enabled;
}
public boolean isDrawLimitLinesBehindDataEnabled() {
return mDrawLimitLineBehindData;
}
/**
* If this is set to false, the grid lines are draw on top of the actual data,
* otherwise behind. Default: true
*
* @param enabled
*/
public void setDrawGridLinesBehindData(boolean enabled) { mDrawGridLinesBehindData = enabled; }
public boolean isDrawGridLinesBehindDataEnabled() {
return mDrawGridLinesBehindData;
}
/**
* Returns the longest formatted label (in terms of characters), this axis
* contains.
*
* @return
*/
public String getLongestLabel() {
String longest = "";
for (int i = 0; i < mEntries.length; i++) {
String text = getFormattedLabel(i);
if (text != null && longest.length() < text.length())
longest = text;
}
return longest;
}
public String getFormattedLabel(int index) {
if (index < 0 || index >= mEntries.length)
return "";
else
return getValueFormatter().getFormattedValue(mEntries[index], this);
}
/**
* Sets the formatter to be used for formatting the axis labels. If no formatter is set, the
* chart will
* automatically determine a reasonable formatting (concerning decimals) for all the values
* that are drawn inside
* the chart. Use chart.getDefaultValueFormatter() to use the formatter calculated by the chart.
*
* @param f
*/
public void setValueFormatter(IAxisValueFormatter f) {
if (f == null)
mAxisValueFormatter = new DefaultAxisValueFormatter(mDecimals);
else
mAxisValueFormatter = f;
}
/**
* Returns the formatter used for formatting the axis labels.
*
* @return
*/
public IAxisValueFormatter getValueFormatter() {
if (mAxisValueFormatter == null ||
(mAxisValueFormatter instanceof DefaultAxisValueFormatter &&
((DefaultAxisValueFormatter)mAxisValueFormatter).getDecimalDigits() != mDecimals))
mAxisValueFormatter = new DefaultAxisValueFormatter(mDecimals);
return mAxisValueFormatter;
}
/**
* Enables the grid line to be drawn in dashed mode, e.g. like this
* "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF.
* Keep in mind that hardware acceleration boosts performance.
*
* @param lineLength the length of the line pieces
* @param spaceLength the length of space in between the pieces
* @param phase offset, in degrees (normally, use 0)
*/
public void enableGridDashedLine(float lineLength, float spaceLength, float phase) {
mGridDashPathEffect = new DashPathEffect(new float[]{
lineLength, spaceLength
}, phase);
}
/**
* Enables the grid line to be drawn in dashed mode, e.g. like this
* "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF.
* Keep in mind that hardware acceleration boosts performance.
*
* @param effect the DashPathEffect
*/
public void setGridDashedLine(DashPathEffect effect) {
mGridDashPathEffect = effect;
}
/**
* Disables the grid line to be drawn in dashed mode.
*/
public void disableGridDashedLine() {
mGridDashPathEffect = null;
}
/**
* Returns true if the grid dashed-line effect is enabled, false if not.
*
* @return
*/
public boolean isGridDashedLineEnabled() {
return mGridDashPathEffect == null ? false : true;
}
/**
* returns the DashPathEffect that is set for grid line
*
* @return
*/
public DashPathEffect getGridDashPathEffect() {
return mGridDashPathEffect;
}
/**
* Enables the axis line to be drawn in dashed mode, e.g. like this
* "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF.
* Keep in mind that hardware acceleration boosts performance.
*
* @param lineLength the length of the line pieces
* @param spaceLength the length of space in between the pieces
* @param phase offset, in degrees (normally, use 0)
*/
public void enableAxisLineDashedLine(float lineLength, float spaceLength, float phase) {
mAxisLineDashPathEffect = new DashPathEffect(new float[]{
lineLength, spaceLength
}, phase);
}
/**
* Enables the axis line to be drawn in dashed mode, e.g. like this
* "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF.
* Keep in mind that hardware acceleration boosts performance.
*
* @param effect the DashPathEffect
*/
public void setAxisLineDashedLine(DashPathEffect effect) {
mAxisLineDashPathEffect = effect;
}
/**
* Disables the axis line to be drawn in dashed mode.
*/
public void disableAxisLineDashedLine() {
mAxisLineDashPathEffect = null;
}
/**
* Returns true if the axis dashed-line effect is enabled, false if not.
*
* @return
*/
public boolean isAxisLineDashedLineEnabled() {
return mAxisLineDashPathEffect == null ? false : true;
}
/**
* returns the DashPathEffect that is set for axis line
*
* @return
*/
public DashPathEffect getAxisLineDashPathEffect() {
return mAxisLineDashPathEffect;
}
/**
* ###### BELOW CODE RELATED TO CUSTOM AXIS VALUES ######
*/
public float getAxisMaximum() {
return mAxisMaximum;
}
public float getAxisMinimum() {
return mAxisMinimum;
}
/**
* By calling this method, any custom maximum value that has been previously set is reseted,
* and the calculation is
* done automatically.
*/
public void resetAxisMaximum() {
mCustomAxisMax = false;
}
/**
* Returns true if the axis max value has been customized (and is not calculated automatically)
*
* @return
*/
public boolean isAxisMaxCustom() {
return mCustomAxisMax;
}
/**
* By calling this method, any custom minimum value that has been previously set is reseted,
* and the calculation is
* done automatically.
*/
public void resetAxisMinimum() {
mCustomAxisMin = false;
}
/**
* Returns true if the axis min value has been customized (and is not calculated automatically)
*
* @return
*/
public boolean isAxisMinCustom() {
return mCustomAxisMin;
}
/**
* Set a custom minimum value for this axis. If set, this value will not be calculated
* automatically depending on
* the provided data. Use resetAxisMinValue() to undo this. Do not forget to call
* setStartAtZero(false) if you use
* this method. Otherwise, the axis-minimum value will still be forced to 0.
*
* @param min
*/
public void setAxisMinimum(float min) {
mCustomAxisMin = true;
mAxisMinimum = min;
this.mAxisRange = Math.abs(mAxisMaximum - min);
}
/**
* Use setAxisMinimum(...) instead.
*
* @param min
*/
@Deprecated
public void setAxisMinValue(float min) {
setAxisMinimum(min);
}
/**
* Set a custom maximum value for this axis. If set, this value will not be calculated
* automatically depending on
* the provided data. Use resetAxisMaxValue() to undo this.
*
* @param max
*/
public void setAxisMaximum(float max) {
mCustomAxisMax = true;
mAxisMaximum = max;
this.mAxisRange = Math.abs(max - mAxisMinimum);
}
/**
* Use setAxisMaximum(...) instead.
*
* @param max
*/
@Deprecated
public void setAxisMaxValue(float max) {
setAxisMaximum(max);
}
/**
* Calculates the minimum / maximum and range values of the axis with the given
* minimum and maximum values from the chart data.
*
* @param dataMin the min value according to chart data
* @param dataMax the max value according to chart data
*/
public void calculate(float dataMin, float dataMax) {
// if custom, use value as is, else use data value
float min = mCustomAxisMin ? mAxisMinimum : (dataMin - mSpaceMin);
float max = mCustomAxisMax ? mAxisMaximum : (dataMax + mSpaceMax);
// temporary range (before calculations)
float range = Math.abs(max - min);
// in case all values are equal
if (range == 0f) {
max = max + 1f;
min = min - 1f;
}
this.mAxisMinimum = min;
this.mAxisMaximum = max;
// actual range
this.mAxisRange = Math.abs(max - min);
}
/**
* Gets extra spacing for `axisMinimum` to be added to automatically calculated `axisMinimum`
*/
public float getSpaceMin()
{
return mSpaceMin;
}
/**
* Sets extra spacing for `axisMinimum` to be added to automatically calculated `axisMinimum`
*/
public void setSpaceMin(float mSpaceMin)
{
this.mSpaceMin = mSpaceMin;
}
/**
* Gets extra spacing for `axisMaximum` to be added to automatically calculated `axisMaximum`
*/
public float getSpaceMax()
{
return mSpaceMax;
}
/**
* Sets extra spacing for `axisMaximum` to be added to automatically calculated `axisMaximum`
*/
public void setSpaceMax(float mSpaceMax)
{
this.mSpaceMax = mSpaceMax;
}
}

View File

@@ -0,0 +1,173 @@
package com.github.mikephil.charting.components;
import android.graphics.Color;
import android.graphics.Typeface;
import com.github.mikephil.charting.utils.Utils;
/**
* This class encapsulates everything both Axis, Legend and LimitLines have in common.
*
* @author Philipp Jahoda
*/
public abstract class ComponentBase {
/**
* flag that indicates if this axis / legend is enabled or not
*/
protected boolean mEnabled = true;
/**
* the offset in pixels this component has on the x-axis
*/
protected float mXOffset = 5f;
/**
* the offset in pixels this component has on the Y-axis
*/
protected float mYOffset = 5f;
/**
* the typeface used for the labels
*/
protected Typeface mTypeface = null;
/**
* the text size of the labels
*/
protected float mTextSize = Utils.convertDpToPixel(10f);
/**
* the text color to use for the labels
*/
protected int mTextColor = Color.BLACK;
public ComponentBase() {
}
/**
* Returns the used offset on the x-axis for drawing the axis or legend
* labels. This offset is applied before and after the label.
*
* @return
*/
public float getXOffset() {
return mXOffset;
}
/**
* Sets the used x-axis offset for the labels on this axis.
*
* @param xOffset
*/
public void setXOffset(float xOffset) {
mXOffset = Utils.convertDpToPixel(xOffset);
}
/**
* Returns the used offset on the x-axis for drawing the axis labels. This
* offset is applied before and after the label.
*
* @return
*/
public float getYOffset() {
return mYOffset;
}
/**
* Sets the used y-axis offset for the labels on this axis. For the legend,
* higher offset means the legend as a whole will be placed further away
* from the top.
*
* @param yOffset
*/
public void setYOffset(float yOffset) {
mYOffset = Utils.convertDpToPixel(yOffset);
}
/**
* returns the Typeface used for the labels, returns null if none is set
*
* @return
*/
public Typeface getTypeface() {
return mTypeface;
}
/**
* sets a specific Typeface for the labels
*
* @param tf
*/
public void setTypeface(Typeface tf) {
mTypeface = tf;
}
/**
* sets the size of the label text in density pixels min = 6f, max = 24f, default
* 10f
*
* @param size the text size, in DP
*/
public void setTextSize(float size) {
if (size > 24f)
size = 24f;
if (size < 6f)
size = 6f;
mTextSize = Utils.convertDpToPixel(size);
}
/**
* returns the text size that is currently set for the labels, in pixels
*
* @return
*/
public float getTextSize() {
return mTextSize;
}
/**
* Sets the text color to use for the labels. Make sure to use
* getResources().getColor(...) when using a color from the resources.
*
* @param color
*/
public void setTextColor(int color) {
mTextColor = color;
}
/**
* Returns the text color that is set for the labels.
*
* @return
*/
public int getTextColor() {
return mTextColor;
}
/**
* Set this to true if this component should be enabled (should be drawn),
* false if not. If disabled, nothing of this component will be drawn.
* Default: true
*
* @param enabled
*/
public void setEnabled(boolean enabled) {
mEnabled = enabled;
}
/**
* Returns true if this comonent is enabled (should be drawn), false if not.
*
* @return
*/
public boolean isEnabled() {
return mEnabled;
}
}

View File

@@ -0,0 +1,95 @@
package com.github.mikephil.charting.components;
import android.graphics.Paint;
import com.github.mikephil.charting.utils.MPPointF;
import com.github.mikephil.charting.utils.Utils;
/**
* Created by Philipp Jahoda on 17/09/16.
*/
public class Description extends ComponentBase {
/**
* the text used in the description
*/
private String text = "Description Label";
/**
* the custom position of the description text
*/
private MPPointF mPosition;
/**
* the alignment of the description text
*/
private Paint.Align mTextAlign = Paint.Align.RIGHT;
public Description() {
super();
// default size
mTextSize = Utils.convertDpToPixel(8f);
}
/**
* Sets the text to be shown as the description.
* Never set this to null as this will cause nullpointer exception when drawing with Android Canvas.
*
* @param text
*/
public void setText(String text) {
this.text = text;
}
/**
* Returns the description text.
*
* @return
*/
public String getText() {
return text;
}
/**
* Sets a custom position for the description text in pixels on the screen.
*
* @param x - xcoordinate
* @param y - ycoordinate
*/
public void setPosition(float x, float y) {
if (mPosition == null) {
mPosition = MPPointF.getInstance(x, y);
} else {
mPosition.x = x;
mPosition.y = y;
}
}
/**
* Returns the customized position of the description, or null if none set.
*
* @return
*/
public MPPointF getPosition() {
return mPosition;
}
/**
* Sets the text alignment of the description text. Default RIGHT.
*
* @param align
*/
public void setTextAlign(Paint.Align align) {
this.mTextAlign = align;
}
/**
* Returns the text alignment of the description.
*
* @return
*/
public Paint.Align getTextAlign() {
return mTextAlign;
}
}

View File

@@ -0,0 +1,47 @@
package com.github.mikephil.charting.components;
import android.graphics.Canvas;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.utils.MPPointF;
public interface IMarker {
/**
* @return The desired (general) offset you wish the IMarker to have on the x- and y-axis.
* By returning x: -(width / 2) you will center the IMarker horizontally.
* By returning y: -(height / 2) you will center the IMarker vertically.
*/
MPPointF getOffset();
/**
* @return The offset for drawing at the specific `point`. This allows conditional adjusting of the Marker position.
* If you have no adjustments to make, return getOffset().
*
* @param posX This is the X position at which the marker wants to be drawn.
* You can adjust the offset conditionally based on this argument.
* @param posY This is the X position at which the marker wants to be drawn.
* You can adjust the offset conditionally based on this argument.
*/
MPPointF getOffsetForDrawingAtPoint(float posX, float posY);
/**
* This method enables a specified custom IMarker to update it's content every time the IMarker is redrawn.
*
* @param e The Entry the IMarker belongs to. This can also be any subclass of Entry, like BarEntry or
* CandleEntry, simply cast it at runtime.
* @param highlight The highlight object contains information about the highlighted value such as it's dataset-index, the
* selected range or stack-index (only stacked bar entries).
*/
void refreshContent(Entry e, Highlight highlight);
/**
* Draws the IMarker on the given position on the screen with the given Canvas object.
*
* @param canvas
* @param posX
* @param posY
*/
void draw(Canvas canvas, float posX, float posY);
}

View File

@@ -0,0 +1,825 @@
package com.github.mikephil.charting.components;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import com.github.mikephil.charting.utils.ColorTemplate;
import com.github.mikephil.charting.utils.FSize;
import com.github.mikephil.charting.utils.Utils;
import com.github.mikephil.charting.utils.ViewPortHandler;
import java.util.ArrayList;
import java.util.List;
/**
* Class representing the legend of the chart. The legend will contain one entry
* per color and DataSet. Multiple colors in one DataSet are grouped together.
* The legend object is NOT available before setting data to the chart.
*
* @author Philipp Jahoda
*/
public class Legend extends ComponentBase {
public enum LegendForm {
/**
* Avoid drawing a form
*/
NONE,
/**
* Do not draw the a form, but leave space for it
*/
EMPTY,
/**
* Use default (default dataset's form to the legend's form)
*/
DEFAULT,
/**
* Draw a square
*/
SQUARE,
/**
* Draw a circle
*/
CIRCLE,
/**
* Draw a horizontal line
*/
LINE
}
public enum LegendHorizontalAlignment {
LEFT, CENTER, RIGHT
}
public enum LegendVerticalAlignment {
TOP, CENTER, BOTTOM
}
public enum LegendOrientation {
HORIZONTAL, VERTICAL
}
public enum LegendDirection {
LEFT_TO_RIGHT, RIGHT_TO_LEFT
}
/**
* The legend entries array
*/
private LegendEntry[] mEntries = new LegendEntry[]{};
/**
* Entries that will be appended to the end of the auto calculated entries after calculating the legend.
* (if the legend has already been calculated, you will need to call notifyDataSetChanged() to let the changes take effect)
*/
private LegendEntry[] mExtraEntries;
/**
* Are the legend labels/colors a custom value or auto calculated? If false,
* then it's auto, if true, then custom. default false (automatic legend)
*/
private boolean mIsLegendCustom = false;
private LegendHorizontalAlignment mHorizontalAlignment = LegendHorizontalAlignment.LEFT;
private LegendVerticalAlignment mVerticalAlignment = LegendVerticalAlignment.BOTTOM;
private LegendOrientation mOrientation = LegendOrientation.HORIZONTAL;
private boolean mDrawInside = false;
/**
* the text direction for the legend
*/
private LegendDirection mDirection = LegendDirection.LEFT_TO_RIGHT;
/**
* the shape/form the legend colors are drawn in
*/
private LegendForm mShape = LegendForm.SQUARE;
/**
* the size of the legend forms/shapes
*/
private float mFormSize = 8f;
/**
* the size of the legend forms/shapes
*/
private float mFormLineWidth = 3f;
/**
* Line dash path effect used for shapes that consist of lines.
*/
private DashPathEffect mFormLineDashEffect = null;
/**
* the space between the legend entries on a horizontal axis, default 6f
*/
private float mXEntrySpace = 6f;
/**
* the space between the legend entries on a vertical axis, default 5f
*/
private float mYEntrySpace = 0f;
/**
* the space between the legend entries on a vertical axis, default 2f
* private float mYEntrySpace = 2f; /** the space between the form and the
* actual label/text
*/
private float mFormToTextSpace = 5f;
/**
* the space that should be left between stacked forms
*/
private float mStackSpace = 3f;
/**
* the maximum relative size out of the whole chart view in percent
*/
private float mMaxSizePercent = 0.95f;
/**
* default constructor
*/
public Legend() {
this.mTextSize = Utils.convertDpToPixel(10f);
this.mXOffset = Utils.convertDpToPixel(5f);
this.mYOffset = Utils.convertDpToPixel(3f); // 2
}
/**
* Constructor. Provide entries for the legend.
*
* @param entries
*/
public Legend(LegendEntry[] entries) {
this();
if (entries == null) {
throw new IllegalArgumentException("entries array is NULL");
}
this.mEntries = entries;
}
/**
* This method sets the automatically computed colors for the legend. Use setCustom(...) to set custom colors.
*
* @param entries
*/
public void setEntries(List<LegendEntry> entries) {
mEntries = entries.toArray(new LegendEntry[entries.size()]);
}
public LegendEntry[] getEntries() {
return mEntries;
}
/**
* returns the maximum length in pixels across all legend labels + formsize
* + formtotextspace
*
* @param p the paint object used for rendering the text
* @return
*/
public float getMaximumEntryWidth(Paint p) {
float max = 0f;
float maxFormSize = 0f;
float formToTextSpace = Utils.convertDpToPixel(mFormToTextSpace);
for (LegendEntry entry : mEntries) {
final float formSize = Utils.convertDpToPixel(
Float.isNaN(entry.formSize)
? mFormSize : entry.formSize);
if (formSize > maxFormSize)
maxFormSize = formSize;
String label = entry.label;
if (label == null) continue;
float length = (float) Utils.calcTextWidth(p, label);
if (length > max)
max = length;
}
return max + maxFormSize + formToTextSpace;
}
/**
* returns the maximum height in pixels across all legend labels
*
* @param p the paint object used for rendering the text
* @return
*/
public float getMaximumEntryHeight(Paint p) {
float max = 0f;
for (LegendEntry entry : mEntries) {
String label = entry.label;
if (label == null) continue;
float length = (float) Utils.calcTextHeight(p, label);
if (length > max)
max = length;
}
return max;
}
public LegendEntry[] getExtraEntries() {
return mExtraEntries;
}
public void setExtra(List<LegendEntry> entries) {
mExtraEntries = entries.toArray(new LegendEntry[entries.size()]);
}
public void setExtra(LegendEntry[] entries) {
if (entries == null)
entries = new LegendEntry[]{};
mExtraEntries = entries;
}
/**
* Entries that will be appended to the end of the auto calculated
* entries after calculating the legend.
* (if the legend has already been calculated, you will need to call notifyDataSetChanged()
* to let the changes take effect)
*/
public void setExtra(int[] colors, String[] labels) {
List<LegendEntry> entries = new ArrayList<>();
for (int i = 0; i < Math.min(colors.length, labels.length); i++) {
final LegendEntry entry = new LegendEntry();
entry.formColor = colors[i];
entry.label = labels[i];
if (entry.formColor == ColorTemplate.COLOR_SKIP ||
entry.formColor == 0)
entry.form = LegendForm.NONE;
else if (entry.formColor == ColorTemplate.COLOR_NONE)
entry.form = LegendForm.EMPTY;
entries.add(entry);
}
mExtraEntries = entries.toArray(new LegendEntry[entries.size()]);
}
/**
* Sets a custom legend's entries array.
* * A null label will start a group.
* This will disable the feature that automatically calculates the legend
* entries from the datasets.
* Call resetCustom() to re-enable automatic calculation (and then
* notifyDataSetChanged() is needed to auto-calculate the legend again)
*/
public void setCustom(LegendEntry[] entries) {
mEntries = entries;
mIsLegendCustom = true;
}
/**
* Sets a custom legend's entries array.
* * A null label will start a group.
* This will disable the feature that automatically calculates the legend
* entries from the datasets.
* Call resetCustom() to re-enable automatic calculation (and then
* notifyDataSetChanged() is needed to auto-calculate the legend again)
*/
public void setCustom(List<LegendEntry> entries) {
mEntries = entries.toArray(new LegendEntry[entries.size()]);
mIsLegendCustom = true;
}
/**
* Calling this will disable the custom legend entries (set by
* setCustom(...)). Instead, the entries will again be calculated
* automatically (after notifyDataSetChanged() is called).
*/
public void resetCustom() {
mIsLegendCustom = false;
}
/**
* @return true if a custom legend entries has been set default
* false (automatic legend)
*/
public boolean isLegendCustom() {
return mIsLegendCustom;
}
/**
* returns the horizontal alignment of the legend
*
* @return
*/
public LegendHorizontalAlignment getHorizontalAlignment() {
return mHorizontalAlignment;
}
/**
* sets the horizontal alignment of the legend
*
* @param value
*/
public void setHorizontalAlignment(LegendHorizontalAlignment value) {
mHorizontalAlignment = value;
}
/**
* returns the vertical alignment of the legend
*
* @return
*/
public LegendVerticalAlignment getVerticalAlignment() {
return mVerticalAlignment;
}
/**
* sets the vertical alignment of the legend
*
* @param value
*/
public void setVerticalAlignment(LegendVerticalAlignment value) {
mVerticalAlignment = value;
}
/**
* returns the orientation of the legend
*
* @return
*/
public LegendOrientation getOrientation() {
return mOrientation;
}
/**
* sets the orientation of the legend
*
* @param value
*/
public void setOrientation(LegendOrientation value) {
mOrientation = value;
}
/**
* returns whether the legend will draw inside the chart or outside
*
* @return
*/
public boolean isDrawInsideEnabled() {
return mDrawInside;
}
/**
* sets whether the legend will draw inside the chart or outside
*
* @param value
*/
public void setDrawInside(boolean value) {
mDrawInside = value;
}
/**
* returns the text direction of the legend
*
* @return
*/
public LegendDirection getDirection() {
return mDirection;
}
/**
* sets the text direction of the legend
*
* @param pos
*/
public void setDirection(LegendDirection pos) {
mDirection = pos;
}
/**
* returns the current form/shape that is set for the legend
*
* @return
*/
public LegendForm getForm() {
return mShape;
}
/**
* sets the form/shape of the legend forms
*
* @param shape
*/
public void setForm(LegendForm shape) {
mShape = shape;
}
/**
* sets the size in dp of the legend forms, default 8f
*
* @param size
*/
public void setFormSize(float size) {
mFormSize = size;
}
/**
* returns the size in dp of the legend forms
*
* @return
*/
public float getFormSize() {
return mFormSize;
}
/**
* sets the line width in dp for forms that consist of lines, default 3f
*
* @param size
*/
public void setFormLineWidth(float size) {
mFormLineWidth = size;
}
/**
* returns the line width in dp for drawing forms that consist of lines
*
* @return
*/
public float getFormLineWidth() {
return mFormLineWidth;
}
/**
* Sets the line dash path effect used for shapes that consist of lines.
*
* @param dashPathEffect
*/
public void setFormLineDashEffect(DashPathEffect dashPathEffect) {
mFormLineDashEffect = dashPathEffect;
}
/**
* @return The line dash path effect used for shapes that consist of lines.
*/
public DashPathEffect getFormLineDashEffect() {
return mFormLineDashEffect;
}
/**
* returns the space between the legend entries on a horizontal axis in
* pixels
*
* @return
*/
public float getXEntrySpace() {
return mXEntrySpace;
}
/**
* sets the space between the legend entries on a horizontal axis in pixels,
* converts to dp internally
*
* @param space
*/
public void setXEntrySpace(float space) {
mXEntrySpace = space;
}
/**
* returns the space between the legend entries on a vertical axis in pixels
*
* @return
*/
public float getYEntrySpace() {
return mYEntrySpace;
}
/**
* sets the space between the legend entries on a vertical axis in pixels,
* converts to dp internally
*
* @param space
*/
public void setYEntrySpace(float space) {
mYEntrySpace = space;
}
/**
* returns the space between the form and the actual label/text
*
* @return
*/
public float getFormToTextSpace() {
return mFormToTextSpace;
}
/**
* sets the space between the form and the actual label/text, converts to dp
* internally
*
* @param space
*/
public void setFormToTextSpace(float space) {
this.mFormToTextSpace = space;
}
/**
* returns the space that is left out between stacked forms (with no label)
*
* @return
*/
public float getStackSpace() {
return mStackSpace;
}
/**
* sets the space that is left out between stacked forms (with no label)
*
* @param space
*/
public void setStackSpace(float space) {
mStackSpace = space;
}
/**
* the total width of the legend (needed width space)
*/
public float mNeededWidth = 0f;
/**
* the total height of the legend (needed height space)
*/
public float mNeededHeight = 0f;
public float mTextHeightMax = 0f;
public float mTextWidthMax = 0f;
/**
* flag that indicates if word wrapping is enabled
*/
private boolean mWordWrapEnabled = false;
/**
* Should the legend word wrap? / this is currently supported only for:
* BelowChartLeft, BelowChartRight, BelowChartCenter. / note that word
* wrapping a legend takes a toll on performance. / you may want to set
* maxSizePercent when word wrapping, to set the point where the text wraps.
* / default: false
*
* @param enabled
*/
public void setWordWrapEnabled(boolean enabled) {
mWordWrapEnabled = enabled;
}
/**
* If this is set, then word wrapping the legend is enabled. This means the
* legend will not be cut off if too long.
*
* @return
*/
public boolean isWordWrapEnabled() {
return mWordWrapEnabled;
}
/**
* The maximum relative size out of the whole chart view. / If the legend is
* to the right/left of the chart, then this affects the width of the
* legend. / If the legend is to the top/bottom of the chart, then this
* affects the height of the legend. / If the legend is the center of the
* piechart, then this defines the size of the rectangular bounds out of the
* size of the "hole". / default: 0.95f (95%)
*
* @return
*/
public float getMaxSizePercent() {
return mMaxSizePercent;
}
/**
* The maximum relative size out of the whole chart view. / If
* the legend is to the right/left of the chart, then this affects the width
* of the legend. / If the legend is to the top/bottom of the chart, then
* this affects the height of the legend. / default: 0.95f (95%)
*
* @param maxSize
*/
public void setMaxSizePercent(float maxSize) {
mMaxSizePercent = maxSize;
}
private List<FSize> mCalculatedLabelSizes = new ArrayList<>(16);
private List<Boolean> mCalculatedLabelBreakPoints = new ArrayList<>(16);
private List<FSize> mCalculatedLineSizes = new ArrayList<>(16);
public List<FSize> getCalculatedLabelSizes() {
return mCalculatedLabelSizes;
}
public List<Boolean> getCalculatedLabelBreakPoints() {
return mCalculatedLabelBreakPoints;
}
public List<FSize> getCalculatedLineSizes() {
return mCalculatedLineSizes;
}
/**
* Calculates the dimensions of the Legend. This includes the maximum width
* and height of a single entry, as well as the total width and height of
* the Legend.
*
* @param labelpaint
*/
public void calculateDimensions(Paint labelpaint, ViewPortHandler viewPortHandler) {
float defaultFormSize = Utils.convertDpToPixel(mFormSize);
float stackSpace = Utils.convertDpToPixel(mStackSpace);
float formToTextSpace = Utils.convertDpToPixel(mFormToTextSpace);
float xEntrySpace = Utils.convertDpToPixel(mXEntrySpace);
float yEntrySpace = Utils.convertDpToPixel(mYEntrySpace);
boolean wordWrapEnabled = mWordWrapEnabled;
LegendEntry[] entries = mEntries;
int entryCount = entries.length;
mTextWidthMax = getMaximumEntryWidth(labelpaint);
mTextHeightMax = getMaximumEntryHeight(labelpaint);
switch (mOrientation) {
case VERTICAL: {
float maxWidth = 0f, maxHeight = 0f, width = 0f;
float labelLineHeight = Utils.getLineHeight(labelpaint);
boolean wasStacked = false;
for (int i = 0; i < entryCount; i++) {
LegendEntry e = entries[i];
boolean drawingForm = e.form != LegendForm.NONE;
float formSize = Float.isNaN(e.formSize)
? defaultFormSize
: Utils.convertDpToPixel(e.formSize);
String label = e.label;
if (!wasStacked)
width = 0.f;
if (drawingForm) {
if (wasStacked)
width += stackSpace;
width += formSize;
}
// grouped forms have null labels
if (label != null) {
// make a step to the left
if (drawingForm && !wasStacked)
width += formToTextSpace;
else if (wasStacked) {
maxWidth = Math.max(maxWidth, width);
maxHeight += labelLineHeight + yEntrySpace;
width = 0.f;
wasStacked = false;
}
width += Utils.calcTextWidth(labelpaint, label);
maxHeight += labelLineHeight + yEntrySpace;
} else {
wasStacked = true;
width += formSize;
if (i < entryCount - 1)
width += stackSpace;
}
maxWidth = Math.max(maxWidth, width);
}
mNeededWidth = maxWidth;
mNeededHeight = maxHeight;
break;
}
case HORIZONTAL: {
float labelLineHeight = Utils.getLineHeight(labelpaint);
float labelLineSpacing = Utils.getLineSpacing(labelpaint) + yEntrySpace;
float contentWidth = viewPortHandler.contentWidth() * mMaxSizePercent;
// Start calculating layout
float maxLineWidth = 0.f;
float currentLineWidth = 0.f;
float requiredWidth = 0.f;
int stackedStartIndex = -1;
mCalculatedLabelBreakPoints.clear();
mCalculatedLabelSizes.clear();
mCalculatedLineSizes.clear();
for (int i = 0; i < entryCount; i++) {
LegendEntry e = entries[i];
boolean drawingForm = e.form != LegendForm.NONE;
float formSize = Float.isNaN(e.formSize)
? defaultFormSize
: Utils.convertDpToPixel(e.formSize);
String label = e.label;
mCalculatedLabelBreakPoints.add(false);
if (stackedStartIndex == -1) {
// we are not stacking, so required width is for this label
// only
requiredWidth = 0.f;
} else {
// add the spacing appropriate for stacked labels/forms
requiredWidth += stackSpace;
}
// grouped forms have null labels
if (label != null) {
mCalculatedLabelSizes.add(Utils.calcTextSize(labelpaint, label));
requiredWidth += drawingForm ? formToTextSpace + formSize : 0.f;
requiredWidth += mCalculatedLabelSizes.get(i).width;
} else {
mCalculatedLabelSizes.add(FSize.getInstance(0.f, 0.f));
requiredWidth += drawingForm ? formSize : 0.f;
if (stackedStartIndex == -1) {
// mark this index as we might want to break here later
stackedStartIndex = i;
}
}
if (label != null || i == entryCount - 1) {
float requiredSpacing = currentLineWidth == 0.f ? 0.f : xEntrySpace;
if (!wordWrapEnabled // No word wrapping, it must fit.
// The line is empty, it must fit
|| currentLineWidth == 0.f
// It simply fits
|| (contentWidth - currentLineWidth >=
requiredSpacing + requiredWidth)) {
// Expand current line
currentLineWidth += requiredSpacing + requiredWidth;
} else { // It doesn't fit, we need to wrap a line
// Add current line size to array
mCalculatedLineSizes.add(FSize.getInstance(currentLineWidth, labelLineHeight));
maxLineWidth = Math.max(maxLineWidth, currentLineWidth);
// Start a new line
mCalculatedLabelBreakPoints.set(
stackedStartIndex > -1 ? stackedStartIndex
: i, true);
currentLineWidth = requiredWidth;
}
if (i == entryCount - 1) {
// Add last line size to array
mCalculatedLineSizes.add(FSize.getInstance(currentLineWidth, labelLineHeight));
maxLineWidth = Math.max(maxLineWidth, currentLineWidth);
}
}
stackedStartIndex = label != null ? -1 : stackedStartIndex;
}
mNeededWidth = maxLineWidth;
mNeededHeight = labelLineHeight
* (float) (mCalculatedLineSizes.size())
+ labelLineSpacing *
(float) (mCalculatedLineSizes.size() == 0
? 0
: (mCalculatedLineSizes.size() - 1));
break;
}
}
mNeededHeight += mYOffset;
mNeededWidth += mXOffset;
}
}

View File

@@ -0,0 +1,78 @@
package com.github.mikephil.charting.components;
import android.graphics.DashPathEffect;
import com.github.mikephil.charting.utils.ColorTemplate;
public class LegendEntry {
public LegendEntry() {
}
/**
*
* @param label The legend entry text. A `null` label will start a group.
* @param form The form to draw for this entry.
* @param formSize Set to NaN to use the legend's default.
* @param formLineWidth Set to NaN to use the legend's default.
* @param formLineDashEffect Set to nil to use the legend's default.
* @param formColor The color for drawing the form.
*/
public LegendEntry(String label,
Legend.LegendForm form,
float formSize,
float formLineWidth,
DashPathEffect formLineDashEffect,
int formColor)
{
this.label = label;
this.form = form;
this.formSize = formSize;
this.formLineWidth = formLineWidth;
this.formLineDashEffect = formLineDashEffect;
this.formColor = formColor;
}
/**
* The legend entry text.
* A `null` label will start a group.
*/
public String label;
/**
* The form to draw for this entry.
*
* `NONE` will avoid drawing a form, and any related space.
* `EMPTY` will avoid drawing a form, but keep its space.
* `DEFAULT` will use the Legend's default.
*/
public Legend.LegendForm form = Legend.LegendForm.DEFAULT;
/**
* Form size will be considered except for when .None is used
*
* Set as NaN to use the legend's default
*/
public float formSize = Float.NaN;
/**
* Line width used for shapes that consist of lines.
*
* Set as NaN to use the legend's default
*/
public float formLineWidth = Float.NaN;
/**
* Line dash path effect used for shapes that consist of lines.
*
* Set to null to use the legend's default
*/
public DashPathEffect formLineDashEffect = null;
/**
* The color for drawing the form
*/
public int formColor = ColorTemplate.COLOR_NONE;
}

View File

@@ -0,0 +1,215 @@
package com.github.mikephil.charting.components;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Typeface;
import com.github.mikephil.charting.utils.Utils;
/**
* The limit line is an additional feature for all Line-, Bar- and
* ScatterCharts. It allows the displaying of an additional line in the chart
* that marks a certain maximum / limit on the specified axis (x- or y-axis).
*
* @author Philipp Jahoda
*/
public class LimitLine extends ComponentBase {
/** limit / maximum (the y-value or xIndex) */
private float mLimit = 0f;
/** the width of the limit line */
private float mLineWidth = 2f;
/** the color of the limit line */
private int mLineColor = Color.rgb(237, 91, 91);
/** the style of the label text */
private Paint.Style mTextStyle = Paint.Style.FILL_AND_STROKE;
/** label string that is drawn next to the limit line */
private String mLabel = "";
/** the path effect of this LimitLine that makes dashed lines possible */
private DashPathEffect mDashPathEffect = null;
/** indicates the position of the LimitLine label */
private LimitLabelPosition mLabelPosition = LimitLabelPosition.RIGHT_TOP;
/** enum that indicates the position of the LimitLine label */
public enum LimitLabelPosition {
LEFT_TOP, LEFT_BOTTOM, RIGHT_TOP, RIGHT_BOTTOM
}
/**
* Constructor with limit.
*
* @param limit - the position (the value) on the y-axis (y-value) or x-axis
* (xIndex) where this line should appear
*/
public LimitLine(float limit) {
mLimit = limit;
}
/**
* Constructor with limit and label.
*
* @param limit - the position (the value) on the y-axis (y-value) or x-axis
* (xIndex) where this line should appear
* @param label - provide "" if no label is required
*/
public LimitLine(float limit, String label) {
mLimit = limit;
mLabel = label;
}
/**
* Returns the limit that is set for this line.
*
* @return
*/
public float getLimit() {
return mLimit;
}
/**
* set the line width of the chart (min = 0.2f, max = 12f); default 2f NOTE:
* thinner line == better performance, thicker line == worse performance
*
* @param width
*/
public void setLineWidth(float width) {
if (width < 0.2f)
width = 0.2f;
if (width > 12.0f)
width = 12.0f;
mLineWidth = Utils.convertDpToPixel(width);
}
/**
* returns the width of limit line
*
* @return
*/
public float getLineWidth() {
return mLineWidth;
}
/**
* Sets the linecolor for this LimitLine. Make sure to use
* getResources().getColor(...)
*
* @param color
*/
public void setLineColor(int color) {
mLineColor = color;
}
/**
* Returns the color that is used for this LimitLine
*
* @return
*/
public int getLineColor() {
return mLineColor;
}
/**
* Enables the line to be drawn in dashed mode, e.g. like this "- - - - - -"
*
* @param lineLength the length of the line pieces
* @param spaceLength the length of space inbetween the pieces
* @param phase offset, in degrees (normally, use 0)
*/
public void enableDashedLine(float lineLength, float spaceLength, float phase) {
mDashPathEffect = new DashPathEffect(new float[] {
lineLength, spaceLength
}, phase);
}
/**
* Disables the line to be drawn in dashed mode.
*/
public void disableDashedLine() {
mDashPathEffect = null;
}
/**
* Returns true if the dashed-line effect is enabled, false if not. Default:
* disabled
*
* @return
*/
public boolean isDashedLineEnabled() {
return mDashPathEffect == null ? false : true;
}
/**
* returns the DashPathEffect that is set for this LimitLine
*
* @return
*/
public DashPathEffect getDashPathEffect() {
return mDashPathEffect;
}
/**
* Sets the color of the value-text that is drawn next to the LimitLine.
* Default: Paint.Style.FILL_AND_STROKE
*
* @param style
*/
public void setTextStyle(Paint.Style style) {
this.mTextStyle = style;
}
/**
* Returns the color of the value-text that is drawn next to the LimitLine.
*
* @return
*/
public Paint.Style getTextStyle() {
return mTextStyle;
}
/**
* Sets the position of the LimitLine value label (either on the right or on
* the left edge of the chart). Not supported for RadarChart.
*
* @param pos
*/
public void setLabelPosition(LimitLabelPosition pos) {
mLabelPosition = pos;
}
/**
* Returns the position of the LimitLine label (value).
*
* @return
*/
public LimitLabelPosition getLabelPosition() {
return mLabelPosition;
}
/**
* Sets the label that is drawn next to the limit line. Provide "" if no
* label is required.
*
* @param label
*/
public void setLabel(String label) {
mLabel = label;
}
/**
* Returns the label that is drawn next to the limit line.
*
* @return
*/
public String getLabel() {
return mLabel;
}
}

View File

@@ -0,0 +1,167 @@
package com.github.mikephil.charting.components;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.RelativeLayout;
import com.github.mikephil.charting.charts.Chart;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.utils.FSize;
import com.github.mikephil.charting.utils.MPPointF;
import java.lang.ref.WeakReference;
/**
* View that can be displayed when selecting values in the chart. Extend this class to provide custom layouts for your
* markers.
*
* @author Philipp Jahoda
*/
public class MarkerImage implements IMarker {
private Context mContext;
private Drawable mDrawable;
private MPPointF mOffset = new MPPointF();
private MPPointF mOffset2 = new MPPointF();
private WeakReference<Chart> mWeakChart;
private FSize mSize = new FSize();
private Rect mDrawableBoundsCache = new Rect();
/**
* Constructor. Sets up the MarkerView with a custom layout resource.
*
* @param context
* @param drawableResourceId the drawable resource to render
*/
public MarkerImage(Context context, int drawableResourceId) {
mContext = context;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
mDrawable = mContext.getResources().getDrawable(drawableResourceId, null);
}
else
{
mDrawable = mContext.getResources().getDrawable(drawableResourceId);
}
}
public void setOffset(MPPointF offset) {
mOffset = offset;
if (mOffset == null) {
mOffset = new MPPointF();
}
}
public void setOffset(float offsetX, float offsetY) {
mOffset.x = offsetX;
mOffset.y = offsetY;
}
@Override
public MPPointF getOffset() {
return mOffset;
}
public void setSize(FSize size) {
mSize = size;
if (mSize == null) {
mSize = new FSize();
}
}
public FSize getSize() {
return mSize;
}
public void setChartView(Chart chart) {
mWeakChart = new WeakReference<>(chart);
}
public Chart getChartView() {
return mWeakChart == null ? null : mWeakChart.get();
}
@Override
public MPPointF getOffsetForDrawingAtPoint(float posX, float posY) {
MPPointF offset = getOffset();
mOffset2.x = offset.x;
mOffset2.y = offset.y;
Chart chart = getChartView();
float width = mSize.width;
float height = mSize.height;
if (width == 0.f && mDrawable != null) {
width = mDrawable.getIntrinsicWidth();
}
if (height == 0.f && mDrawable != null) {
height = mDrawable.getIntrinsicHeight();
}
if (posX + mOffset2.x < 0) {
mOffset2.x = - posX;
} else if (chart != null && posX + width + mOffset2.x > chart.getWidth()) {
mOffset2.x = chart.getWidth() - posX - width;
}
if (posY + mOffset2.y < 0) {
mOffset2.y = - posY;
} else if (chart != null && posY + height + mOffset2.y > chart.getHeight()) {
mOffset2.y = chart.getHeight() - posY - height;
}
return mOffset2;
}
@Override
public void refreshContent(Entry e, Highlight highlight) {
}
@Override
public void draw(Canvas canvas, float posX, float posY) {
if (mDrawable == null) return;
MPPointF offset = getOffsetForDrawingAtPoint(posX, posY);
float width = mSize.width;
float height = mSize.height;
if (width == 0.f) {
width = mDrawable.getIntrinsicWidth();
}
if (height == 0.f) {
height = mDrawable.getIntrinsicHeight();
}
mDrawable.copyBounds(mDrawableBoundsCache);
mDrawable.setBounds(
mDrawableBoundsCache.left,
mDrawableBoundsCache.top,
mDrawableBoundsCache.left + (int)width,
mDrawableBoundsCache.top + (int)height);
int saveId = canvas.save();
// translate to the correct position and draw
canvas.translate(posX + offset.x, posY + offset.y);
mDrawable.draw(canvas);
canvas.restoreToCount(saveId);
mDrawable.setBounds(mDrawableBoundsCache);
}
}

View File

@@ -0,0 +1,129 @@
package com.github.mikephil.charting.components;
import android.content.Context;
import android.graphics.Canvas;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.RelativeLayout;
import com.github.mikephil.charting.charts.Chart;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.utils.FSize;
import com.github.mikephil.charting.utils.MPPointF;
import java.lang.ref.WeakReference;
/**
* View that can be displayed when selecting values in the chart. Extend this class to provide custom layouts for your
* markers.
*
* @author Philipp Jahoda
*/
public class MarkerView extends RelativeLayout implements IMarker {
private MPPointF mOffset = new MPPointF();
private MPPointF mOffset2 = new MPPointF();
private WeakReference<Chart> mWeakChart;
/**
* Constructor. Sets up the MarkerView with a custom layout resource.
*
* @param context
* @param layoutResource the layout resource to use for the MarkerView
*/
public MarkerView(Context context, int layoutResource) {
super(context);
setupLayoutResource(layoutResource);
}
/**
* Sets the layout resource for a custom MarkerView.
*
* @param layoutResource
*/
private void setupLayoutResource(int layoutResource) {
View inflated = LayoutInflater.from(getContext()).inflate(layoutResource, this);
inflated.setLayoutParams(new LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT));
inflated.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
// measure(getWidth(), getHeight());
inflated.layout(0, 0, inflated.getMeasuredWidth(), inflated.getMeasuredHeight());
}
public void setOffset(MPPointF offset) {
mOffset = offset;
if (mOffset == null) {
mOffset = new MPPointF();
}
}
public void setOffset(float offsetX, float offsetY) {
mOffset.x = offsetX;
mOffset.y = offsetY;
}
@Override
public MPPointF getOffset() {
return mOffset;
}
public void setChartView(Chart chart) {
mWeakChart = new WeakReference<>(chart);
}
public Chart getChartView() {
return mWeakChart == null ? null : mWeakChart.get();
}
@Override
public MPPointF getOffsetForDrawingAtPoint(float posX, float posY) {
MPPointF offset = getOffset();
mOffset2.x = offset.x;
mOffset2.y = offset.y;
Chart chart = getChartView();
float width = getWidth();
float height = getHeight();
if (posX + mOffset2.x < 0) {
mOffset2.x = - posX;
} else if (chart != null && posX + width + mOffset2.x > chart.getWidth()) {
mOffset2.x = chart.getWidth() - posX - width;
}
if (posY + mOffset2.y < 0) {
mOffset2.y = - posY;
} else if (chart != null && posY + height + mOffset2.y > chart.getHeight()) {
mOffset2.y = chart.getHeight() - posY - height;
}
return mOffset2;
}
@Override
public void refreshContent(Entry e, Highlight highlight) {
measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
}
@Override
public void draw(Canvas canvas, float posX, float posY) {
MPPointF offset = getOffsetForDrawingAtPoint(posX, posY);
int saveId = canvas.save();
// translate to the correct position and draw
canvas.translate(posX + offset.x, posY + offset.y);
draw(canvas);
canvas.restoreToCount(saveId);
}
}

View File

@@ -0,0 +1,118 @@
package com.github.mikephil.charting.components;
import com.github.mikephil.charting.utils.Utils;
/**
* Class representing the x-axis labels settings. Only use the setter methods to
* modify it. Do not access public variables directly. Be aware that not all
* features the XLabels class provides are suitable for the RadarChart.
*
* @author Philipp Jahoda
*/
public class XAxis extends AxisBase {
/**
* width of the x-axis labels in pixels - this is automatically
* calculated by the computeSize() methods in the renderers
*/
public int mLabelWidth = 1;
/**
* height of the x-axis labels in pixels - this is automatically
* calculated by the computeSize() methods in the renderers
*/
public int mLabelHeight = 1;
/**
* width of the (rotated) x-axis labels in pixels - this is automatically
* calculated by the computeSize() methods in the renderers
*/
public int mLabelRotatedWidth = 1;
/**
* height of the (rotated) x-axis labels in pixels - this is automatically
* calculated by the computeSize() methods in the renderers
*/
public int mLabelRotatedHeight = 1;
/**
* This is the angle for drawing the X axis labels (in degrees)
*/
protected float mLabelRotationAngle = 0f;
/**
* if set to true, the chart will avoid that the first and last label entry
* in the chart "clip" off the edge of the chart
*/
private boolean mAvoidFirstLastClipping = false;
/**
* the position of the x-labels relative to the chart
*/
private XAxisPosition mPosition = XAxisPosition.TOP;
/**
* enum for the position of the x-labels relative to the chart
*/
public enum XAxisPosition {
TOP, BOTTOM, BOTH_SIDED, TOP_INSIDE, BOTTOM_INSIDE
}
public XAxis() {
super();
mYOffset = Utils.convertDpToPixel(4.f); // -3
}
/**
* returns the position of the x-labels
*/
public XAxisPosition getPosition() {
return mPosition;
}
/**
* sets the position of the x-labels
*
* @param pos
*/
public void setPosition(XAxisPosition pos) {
mPosition = pos;
}
/**
* returns the angle for drawing the X axis labels (in degrees)
*/
public float getLabelRotationAngle() {
return mLabelRotationAngle;
}
/**
* sets the angle for drawing the X axis labels (in degrees)
*
* @param angle the angle in degrees
*/
public void setLabelRotationAngle(float angle) {
mLabelRotationAngle = angle;
}
/**
* if set to true, the chart will avoid that the first and last label entry
* in the chart "clip" off the edge of the chart or the screen
*
* @param enabled
*/
public void setAvoidFirstLastClipping(boolean enabled) {
mAvoidFirstLastClipping = enabled;
}
/**
* returns true if avoid-first-lastclipping is enabled, false if not
*
* @return
*/
public boolean isAvoidFirstLastClippingEnabled() {
return mAvoidFirstLastClipping;
}
}

View File

@@ -0,0 +1,467 @@
package com.github.mikephil.charting.components;
import android.graphics.Color;
import android.graphics.Paint;
import com.github.mikephil.charting.utils.Utils;
/**
* Class representing the y-axis labels settings and its entries. Only use the setter methods to
* modify it. Do not
* access public variables directly. Be aware that not all features the YLabels class provides
* are suitable for the
* RadarChart. Customizations that affect the value range of the axis need to be applied before
* setting data for the
* chart.
*
* @author Philipp Jahoda
*/
public class YAxis extends AxisBase {
/**
* indicates if the bottom y-label entry is drawn or not
*/
private boolean mDrawBottomYLabelEntry = true;
/**
* indicates if the top y-label entry is drawn or not
*/
private boolean mDrawTopYLabelEntry = true;
/**
* flag that indicates if the axis is inverted or not
*/
protected boolean mInverted = false;
/**
* flag that indicates if the zero-line should be drawn regardless of other grid lines
*/
protected boolean mDrawZeroLine = false;
/**
* flag indicating that auto scale min restriction should be used
*/
private boolean mUseAutoScaleRestrictionMin = false;
/**
* flag indicating that auto scale max restriction should be used
*/
private boolean mUseAutoScaleRestrictionMax = false;
/**
* Color of the zero line
*/
protected int mZeroLineColor = Color.GRAY;
/**
* Width of the zero line in pixels
*/
protected float mZeroLineWidth = 1f;
/**
* axis space from the largest value to the top in percent of the total axis range
*/
protected float mSpacePercentTop = 10f;
/**
* axis space from the smallest value to the bottom in percent of the total axis range
*/
protected float mSpacePercentBottom = 10f;
/**
* the position of the y-labels relative to the chart
*/
private YAxisLabelPosition mPosition = YAxisLabelPosition.OUTSIDE_CHART;
/**
* the horizontal offset of the y-label
*/
private float mXLabelOffset = 0.0f;
/**
* enum for the position of the y-labels relative to the chart
*/
public enum YAxisLabelPosition {
OUTSIDE_CHART, INSIDE_CHART
}
/**
* the side this axis object represents
*/
private AxisDependency mAxisDependency;
/**
* the minimum width that the axis should take (in dp).
* <p/>
* default: 0.0
*/
protected float mMinWidth = 0.f;
/**
* the maximum width that the axis can take (in dp).
* use Inifinity for disabling the maximum
* default: Float.POSITIVE_INFINITY (no maximum specified)
*/
protected float mMaxWidth = Float.POSITIVE_INFINITY;
/**
* Enum that specifies the axis a DataSet should be plotted against, either LEFT or RIGHT.
*
* @author Philipp Jahoda
*/
public enum AxisDependency {
LEFT, RIGHT
}
public YAxis() {
super();
// default left
this.mAxisDependency = AxisDependency.LEFT;
this.mYOffset = 0f;
}
public YAxis(AxisDependency position) {
super();
this.mAxisDependency = position;
this.mYOffset = 0f;
}
public AxisDependency getAxisDependency() {
return mAxisDependency;
}
/**
* @return the minimum width that the axis should take (in dp).
*/
public float getMinWidth() {
return mMinWidth;
}
/**
* Sets the minimum width that the axis should take (in dp).
*
* @param minWidth
*/
public void setMinWidth(float minWidth) {
mMinWidth = minWidth;
}
/**
* @return the maximum width that the axis can take (in dp).
*/
public float getMaxWidth() {
return mMaxWidth;
}
/**
* Sets the maximum width that the axis can take (in dp).
*
* @param maxWidth
*/
public void setMaxWidth(float maxWidth) {
mMaxWidth = maxWidth;
}
/**
* returns the position of the y-labels
*/
public YAxisLabelPosition getLabelPosition() {
return mPosition;
}
/**
* sets the position of the y-labels
*
* @param pos
*/
public void setPosition(YAxisLabelPosition pos) {
mPosition = pos;
}
/**
* returns the horizontal offset of the y-label
*/
public float getLabelXOffset() {
return mXLabelOffset;
}
/**
* sets the horizontal offset of the y-label
*
* @param xOffset
*/
public void setLabelXOffset(float xOffset) {
mXLabelOffset = xOffset;
}
/**
* returns true if drawing the top y-axis label entry is enabled
*
* @return
*/
public boolean isDrawTopYLabelEntryEnabled() {
return mDrawTopYLabelEntry;
}
/**
* returns true if drawing the bottom y-axis label entry is enabled
*
* @return
*/
public boolean isDrawBottomYLabelEntryEnabled() {
return mDrawBottomYLabelEntry;
}
/**
* set this to true to enable drawing the top y-label entry. Disabling this can be helpful
* when the top y-label and
* left x-label interfere with each other. default: true
*
* @param enabled
*/
public void setDrawTopYLabelEntry(boolean enabled) {
mDrawTopYLabelEntry = enabled;
}
/**
* If this is set to true, the y-axis is inverted which means that low values are on top of
* the chart, high values
* on bottom.
*
* @param enabled
*/
public void setInverted(boolean enabled) {
mInverted = enabled;
}
/**
* If this returns true, the y-axis is inverted.
*
* @return
*/
public boolean isInverted() {
return mInverted;
}
/**
* This method is deprecated.
* Use setAxisMinimum(...) / setAxisMaximum(...) instead.
*
* @param startAtZero
*/
@Deprecated
public void setStartAtZero(boolean startAtZero) {
if (startAtZero)
setAxisMinimum(0f);
else
resetAxisMinimum();
}
/**
* Sets the top axis space in percent of the full range. Default 10f
*
* @param percent
*/
public void setSpaceTop(float percent) {
mSpacePercentTop = percent;
}
/**
* Returns the top axis space in percent of the full range. Default 10f
*
* @return
*/
public float getSpaceTop() {
return mSpacePercentTop;
}
/**
* Sets the bottom axis space in percent of the full range. Default 10f
*
* @param percent
*/
public void setSpaceBottom(float percent) {
mSpacePercentBottom = percent;
}
/**
* Returns the bottom axis space in percent of the full range. Default 10f
*
* @return
*/
public float getSpaceBottom() {
return mSpacePercentBottom;
}
public boolean isDrawZeroLineEnabled() {
return mDrawZeroLine;
}
/**
* Set this to true to draw the zero-line regardless of weather other
* grid-lines are enabled or not. Default: false
*
* @param mDrawZeroLine
*/
public void setDrawZeroLine(boolean mDrawZeroLine) {
this.mDrawZeroLine = mDrawZeroLine;
}
public int getZeroLineColor() {
return mZeroLineColor;
}
/**
* Sets the color of the zero line
*
* @param color
*/
public void setZeroLineColor(int color) {
mZeroLineColor = color;
}
public float getZeroLineWidth() {
return mZeroLineWidth;
}
/**
* Sets the width of the zero line in dp
*
* @param width
*/
public void setZeroLineWidth(float width) {
this.mZeroLineWidth = Utils.convertDpToPixel(width);
}
/**
* This is for normal (not horizontal) charts horizontal spacing.
*
* @param p
* @return
*/
public float getRequiredWidthSpace(Paint p) {
p.setTextSize(mTextSize);
String label = getLongestLabel();
float width = (float) Utils.calcTextWidth(p, label) + getXOffset() * 2f;
float minWidth = getMinWidth();
float maxWidth = getMaxWidth();
if (minWidth > 0.f)
minWidth = Utils.convertDpToPixel(minWidth);
if (maxWidth > 0.f && maxWidth != Float.POSITIVE_INFINITY)
maxWidth = Utils.convertDpToPixel(maxWidth);
width = Math.max(minWidth, Math.min(width, maxWidth > 0.0 ? maxWidth : width));
return width;
}
/**
* This is for HorizontalBarChart vertical spacing.
*
* @param p
* @return
*/
public float getRequiredHeightSpace(Paint p) {
p.setTextSize(mTextSize);
String label = getLongestLabel();
return (float) Utils.calcTextHeight(p, label) + getYOffset() * 2f;
}
/**
* Returns true if this axis needs horizontal offset, false if no offset is needed.
*
* @return
*/
public boolean needsOffset() {
if (isEnabled() && isDrawLabelsEnabled() && getLabelPosition() == YAxisLabelPosition
.OUTSIDE_CHART)
return true;
else
return false;
}
/**
* Returns true if autoscale restriction for axis min value is enabled
*/
@Deprecated
public boolean isUseAutoScaleMinRestriction( ) {
return mUseAutoScaleRestrictionMin;
}
/**
* Sets autoscale restriction for axis min value as enabled/disabled
*/
@Deprecated
public void setUseAutoScaleMinRestriction( boolean isEnabled ) {
mUseAutoScaleRestrictionMin = isEnabled;
}
/**
* Returns true if autoscale restriction for axis max value is enabled
*/
@Deprecated
public boolean isUseAutoScaleMaxRestriction() {
return mUseAutoScaleRestrictionMax;
}
/**
* Sets autoscale restriction for axis max value as enabled/disabled
*/
@Deprecated
public void setUseAutoScaleMaxRestriction( boolean isEnabled ) {
mUseAutoScaleRestrictionMax = isEnabled;
}
@Override
public void calculate(float dataMin, float dataMax) {
float min = dataMin;
float max = dataMax;
// Make sure max is greater than min
// Discussion: https://github.com/danielgindi/Charts/pull/3650#discussion_r221409991
if (min > max)
{
if (mCustomAxisMax && mCustomAxisMin)
{
float t = min;
min = max;
max = t;
}
else if (mCustomAxisMax)
{
min = max < 0f ? max * 1.5f : max * 0.5f;
}
else if (mCustomAxisMin)
{
max = min < 0f ? min * 0.5f : min * 1.5f;
}
}
float range = Math.abs(max - min);
// in case all values are equal
if (range == 0f) {
max = max + 1f;
min = min - 1f;
}
// recalculate
range = Math.abs(max - min);
// calc extra spacing
this.mAxisMinimum = mCustomAxisMin ? this.mAxisMinimum : min - (range / 100f) * getSpaceBottom();
this.mAxisMaximum = mCustomAxisMax ? this.mAxisMaximum : max + (range / 100f) * getSpaceTop();
this.mAxisRange = Math.abs(this.mAxisMinimum - this.mAxisMaximum);
}
}

View File

@@ -0,0 +1,119 @@
package com.github.mikephil.charting.data;
import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
import java.util.List;
/**
* Data object that represents all data for the BarChart.
*
* @author Philipp Jahoda
*/
public class BarData extends BarLineScatterCandleBubbleData<IBarDataSet> {
/**
* the width of the bars on the x-axis, in values (not pixels)
*/
private float mBarWidth = 0.85f;
public BarData() {
super();
}
public BarData(IBarDataSet... dataSets) {
super(dataSets);
}
public BarData(List<IBarDataSet> dataSets) {
super(dataSets);
}
/**
* Sets the width each bar should have on the x-axis (in values, not pixels).
* Default 0.85f
*
* @param mBarWidth
*/
public void setBarWidth(float mBarWidth) {
this.mBarWidth = mBarWidth;
}
public float getBarWidth() {
return mBarWidth;
}
/**
* Groups all BarDataSet objects this data object holds together by modifying the x-value of their entries.
* Previously set x-values of entries will be overwritten. Leaves space between bars and groups as specified
* by the parameters.
* Do not forget to call notifyDataSetChanged() on your BarChart object after calling this method.
*
* @param fromX the starting point on the x-axis where the grouping should begin
* @param groupSpace the space between groups of bars in values (not pixels) e.g. 0.8f for bar width 1f
* @param barSpace the space between individual bars in values (not pixels) e.g. 0.1f for bar width 1f
*/
public void groupBars(float fromX, float groupSpace, float barSpace) {
int setCount = mDataSets.size();
if (setCount <= 1) {
throw new RuntimeException("BarData needs to hold at least 2 BarDataSets to allow grouping.");
}
IBarDataSet max = getMaxEntryCountSet();
int maxEntryCount = max.getEntryCount();
float groupSpaceWidthHalf = groupSpace / 2f;
float barSpaceHalf = barSpace / 2f;
float barWidthHalf = mBarWidth / 2f;
float interval = getGroupWidth(groupSpace, barSpace);
for (int i = 0; i < maxEntryCount; i++) {
float start = fromX;
fromX += groupSpaceWidthHalf;
for (IBarDataSet set : mDataSets) {
fromX += barSpaceHalf;
fromX += barWidthHalf;
if (i < set.getEntryCount()) {
BarEntry entry = set.getEntryForIndex(i);
if (entry != null) {
entry.setX(fromX);
}
}
fromX += barWidthHalf;
fromX += barSpaceHalf;
}
fromX += groupSpaceWidthHalf;
float end = fromX;
float innerInterval = end - start;
float diff = interval - innerInterval;
// correct rounding errors
if (diff > 0 || diff < 0) {
fromX += diff;
}
}
notifyDataChanged();
}
/**
* In case of grouped bars, this method returns the space an individual group of bar needs on the x-axis.
*
* @param groupSpace
* @param barSpace
* @return
*/
public float getGroupWidth(float groupSpace, float barSpace) {
return mDataSets.size() * (mBarWidth + barSpace) + groupSpace;
}
}

View File

@@ -0,0 +1,299 @@
package com.github.mikephil.charting.data;
import android.graphics.Color;
import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
import com.github.mikephil.charting.utils.Fill;
import java.util.ArrayList;
import java.util.List;
public class BarDataSet extends BarLineScatterCandleBubbleDataSet<BarEntry> implements IBarDataSet {
/**
* the maximum number of bars that are stacked upon each other, this value
* is calculated from the Entries that are added to the DataSet
*/
private int mStackSize = 1;
/**
* the color used for drawing the bar shadows
*/
private int mBarShadowColor = Color.rgb(215, 215, 215);
private float mBarBorderWidth = 0.0f;
private int mBarBorderColor = Color.BLACK;
/**
* the alpha value used to draw the highlight indicator bar
*/
private int mHighLightAlpha = 120;
/**
* the overall entry count, including counting each stack-value individually
*/
private int mEntryCountStacks = 0;
/**
* array of labels used to describe the different values of the stacked bars
*/
private String[] mStackLabels = new String[]{};
protected List<Fill> mFills = null;
public BarDataSet(List<BarEntry> yVals, String label) {
super(yVals, label);
mHighLightColor = Color.rgb(0, 0, 0);
calcStackSize(yVals);
calcEntryCountIncludingStacks(yVals);
}
@Override
public DataSet<BarEntry> copy() {
List<BarEntry> entries = new ArrayList<BarEntry>();
for (int i = 0; i < mEntries.size(); i++) {
entries.add(mEntries.get(i).copy());
}
BarDataSet copied = new BarDataSet(entries, getLabel());
copy(copied);
return copied;
}
protected void copy(BarDataSet barDataSet) {
super.copy(barDataSet);
barDataSet.mStackSize = mStackSize;
barDataSet.mBarShadowColor = mBarShadowColor;
barDataSet.mBarBorderWidth = mBarBorderWidth;
barDataSet.mStackLabels = mStackLabels;
barDataSet.mHighLightAlpha = mHighLightAlpha;
}
@Override
public List<Fill> getFills() {
return mFills;
}
@Override
public Fill getFill(int index) {
return mFills.get(index % mFills.size());
}
/**
* This method is deprecated.
* Use getFills() instead.
*/
@Deprecated
public List<Fill> getGradients() {
return mFills;
}
/**
* This method is deprecated.
* Use getFill(...) instead.
*
* @param index
*/
@Deprecated
public Fill getGradient(int index) {
return getFill(index);
}
/**
* Sets the start and end color for gradient color, ONLY color that should be used for this DataSet.
*
* @param startColor
* @param endColor
*/
public void setGradientColor(int startColor, int endColor) {
mFills.clear();
mFills.add(new Fill(startColor, endColor));
}
/**
* This method is deprecated.
* Use setFills(...) instead.
*
* @param gradientColors
*/
@Deprecated
public void setGradientColors(List<Fill> gradientColors) {
this.mFills = gradientColors;
}
/**
* Sets the fills for the bars in this dataset.
*
* @param fills
*/
public void setFills(List<Fill> fills) {
this.mFills = fills;
}
/**
* Calculates the total number of entries this DataSet represents, including
* stacks. All values belonging to a stack are calculated separately.
*/
private void calcEntryCountIncludingStacks(List<BarEntry> yVals) {
mEntryCountStacks = 0;
for (int i = 0; i < yVals.size(); i++) {
float[] vals = yVals.get(i).getYVals();
if (vals == null)
mEntryCountStacks++;
else
mEntryCountStacks += vals.length;
}
}
/**
* calculates the maximum stacksize that occurs in the Entries array of this
* DataSet
*/
private void calcStackSize(List<BarEntry> yVals) {
for (int i = 0; i < yVals.size(); i++) {
float[] vals = yVals.get(i).getYVals();
if (vals != null && vals.length > mStackSize)
mStackSize = vals.length;
}
}
@Override
protected void calcMinMax(BarEntry e) {
if (e != null && !Float.isNaN(e.getY())) {
if (e.getYVals() == null) {
if (e.getY() < mYMin)
mYMin = e.getY();
if (e.getY() > mYMax)
mYMax = e.getY();
} else {
if (-e.getNegativeSum() < mYMin)
mYMin = -e.getNegativeSum();
if (e.getPositiveSum() > mYMax)
mYMax = e.getPositiveSum();
}
calcMinMaxX(e);
}
}
@Override
public int getStackSize() {
return mStackSize;
}
@Override
public boolean isStacked() {
return mStackSize > 1 ? true : false;
}
/**
* returns the overall entry count, including counting each stack-value
* individually
*
* @return
*/
public int getEntryCountStacks() {
return mEntryCountStacks;
}
/**
* Sets the color used for drawing the bar-shadows. The bar shadows is a
* surface behind the bar that indicates the maximum value. Don't for get to
* use getResources().getColor(...) to set this. Or Color.rgb(...).
*
* @param color
*/
public void setBarShadowColor(int color) {
mBarShadowColor = color;
}
@Override
public int getBarShadowColor() {
return mBarShadowColor;
}
/**
* Sets the width used for drawing borders around the bars.
* If borderWidth == 0, no border will be drawn.
*
* @return
*/
public void setBarBorderWidth(float width) {
mBarBorderWidth = width;
}
/**
* Returns the width used for drawing borders around the bars.
* If borderWidth == 0, no border will be drawn.
*
* @return
*/
@Override
public float getBarBorderWidth() {
return mBarBorderWidth;
}
/**
* Sets the color drawing borders around the bars.
*
* @return
*/
public void setBarBorderColor(int color) {
mBarBorderColor = color;
}
/**
* Returns the color drawing borders around the bars.
*
* @return
*/
@Override
public int getBarBorderColor() {
return mBarBorderColor;
}
/**
* Set the alpha value (transparency) that is used for drawing the highlight
* indicator bar. min = 0 (fully transparent), max = 255 (fully opaque)
*
* @param alpha
*/
public void setHighLightAlpha(int alpha) {
mHighLightAlpha = alpha;
}
@Override
public int getHighLightAlpha() {
return mHighLightAlpha;
}
/**
* Sets labels for different values of bar-stacks, in case there are one.
*
* @param labels
*/
public void setStackLabels(String[] labels) {
mStackLabels = labels;
}
@Override
public String[] getStackLabels() {
return mStackLabels;
}
}

View File

@@ -0,0 +1,310 @@
package com.github.mikephil.charting.data;
import android.annotation.SuppressLint;
import android.graphics.drawable.Drawable;
import com.github.mikephil.charting.highlight.Range;
/**
* Entry class for the BarChart. (especially stacked bars)
*
* @author Philipp Jahoda
*/
@SuppressLint("ParcelCreator")
public class BarEntry extends Entry {
/**
* the values the stacked barchart holds
*/
private float[] mYVals;
/**
* the ranges for the individual stack values - automatically calculated
*/
private Range[] mRanges;
/**
* the sum of all negative values this entry (if stacked) contains
*/
private float mNegativeSum;
/**
* the sum of all positive values this entry (if stacked) contains
*/
private float mPositiveSum;
/**
* Constructor for normal bars (not stacked).
*
* @param x
* @param y
*/
public BarEntry(float x, float y) {
super(x, y);
}
/**
* Constructor for normal bars (not stacked).
*
* @param x
* @param y
* @param data - Spot for additional data this Entry represents.
*/
public BarEntry(float x, float y, Object data) {
super(x, y, data);
}
/**
* Constructor for normal bars (not stacked).
*
* @param x
* @param y
* @param icon - icon image
*/
public BarEntry(float x, float y, Drawable icon) {
super(x, y, icon);
}
/**
* Constructor for normal bars (not stacked).
*
* @param x
* @param y
* @param icon - icon image
* @param data - Spot for additional data this Entry represents.
*/
public BarEntry(float x, float y, Drawable icon, Object data) {
super(x, y, icon, data);
}
/**
* Constructor for stacked bar entries. One data object for whole stack
*
* @param x
* @param vals - the stack values, use at least 2
*/
public BarEntry(float x, float[] vals) {
super(x, calcSum(vals));
this.mYVals = vals;
calcPosNegSum();
calcRanges();
}
/**
* Constructor for stacked bar entries. One data object for whole stack
*
* @param x
* @param vals - the stack values, use at least 2
* @param data - Spot for additional data this Entry represents.
*/
public BarEntry(float x, float[] vals, Object data) {
super(x, calcSum(vals), data);
this.mYVals = vals;
calcPosNegSum();
calcRanges();
}
/**
* Constructor for stacked bar entries. One data object for whole stack
*
* @param x
* @param vals - the stack values, use at least 2
* @param icon - icon image
*/
public BarEntry(float x, float[] vals, Drawable icon) {
super(x, calcSum(vals), icon);
this.mYVals = vals;
calcPosNegSum();
calcRanges();
}
/**
* Constructor for stacked bar entries. One data object for whole stack
*
* @param x
* @param vals - the stack values, use at least 2
* @param icon - icon image
* @param data - Spot for additional data this Entry represents.
*/
public BarEntry(float x, float[] vals, Drawable icon, Object data) {
super(x, calcSum(vals), icon, data);
this.mYVals = vals;
calcPosNegSum();
calcRanges();
}
/**
* Returns an exact copy of the BarEntry.
*/
public BarEntry copy() {
BarEntry copied = new BarEntry(getX(), getY(), getData());
copied.setVals(mYVals);
return copied;
}
/**
* Returns the stacked values this BarEntry represents, or null, if only a single value is represented (then, use
* getY()).
*
* @return
*/
public float[] getYVals() {
return mYVals;
}
/**
* Set the array of values this BarEntry should represent.
*
* @param vals
*/
public void setVals(float[] vals) {
setY(calcSum(vals));
mYVals = vals;
calcPosNegSum();
calcRanges();
}
/**
* Returns the value of this BarEntry. If the entry is stacked, it returns the positive sum of all values.
*
* @return
*/
@Override
public float getY() {
return super.getY();
}
/**
* Returns the ranges of the individual stack-entries. Will return null if this entry is not stacked.
*
* @return
*/
public Range[] getRanges() {
return mRanges;
}
/**
* Returns true if this BarEntry is stacked (has a values array), false if not.
*
* @return
*/
public boolean isStacked() {
return mYVals != null;
}
/**
* Use `getSumBelow(stackIndex)` instead.
*/
@Deprecated
public float getBelowSum(int stackIndex) {
return getSumBelow(stackIndex);
}
public float getSumBelow(int stackIndex) {
if (mYVals == null)
return 0;
float remainder = 0f;
int index = mYVals.length - 1;
while (index > stackIndex && index >= 0) {
remainder += mYVals[index];
index--;
}
return remainder;
}
/**
* Reuturns the sum of all positive values this entry (if stacked) contains.
*
* @return
*/
public float getPositiveSum() {
return mPositiveSum;
}
/**
* Returns the sum of all negative values this entry (if stacked) contains. (this is a positive number)
*
* @return
*/
public float getNegativeSum() {
return mNegativeSum;
}
private void calcPosNegSum() {
if (mYVals == null) {
mNegativeSum = 0;
mPositiveSum = 0;
return;
}
float sumNeg = 0f;
float sumPos = 0f;
for (float f : mYVals) {
if (f <= 0f)
sumNeg += Math.abs(f);
else
sumPos += f;
}
mNegativeSum = sumNeg;
mPositiveSum = sumPos;
}
/**
* Calculates the sum across all values of the given stack.
*
* @param vals
* @return
*/
private static float calcSum(float[] vals) {
if (vals == null)
return 0f;
float sum = 0f;
for (float f : vals)
sum += f;
return sum;
}
protected void calcRanges() {
float[] values = getYVals();
if (values == null || values.length == 0)
return;
mRanges = new Range[values.length];
float negRemain = -getNegativeSum();
float posRemain = 0f;
for (int i = 0; i < mRanges.length; i++) {
float value = values[i];
if (value < 0) {
mRanges[i] = new Range(negRemain, negRemain - value);
negRemain -= value;
} else {
mRanges[i] = new Range(posRemain, posRemain + value);
posRemain += value;
}
}
}
}

View File

@@ -0,0 +1,27 @@
package com.github.mikephil.charting.data;
import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet;
import java.util.List;
/**
* Baseclass for all Line, Bar, Scatter, Candle and Bubble data.
*
* @author Philipp Jahoda
*/
public abstract class BarLineScatterCandleBubbleData<T extends IBarLineScatterCandleBubbleDataSet<? extends Entry>>
extends ChartData<T> {
public BarLineScatterCandleBubbleData() {
super();
}
public BarLineScatterCandleBubbleData(T... sets) {
super(sets);
}
public BarLineScatterCandleBubbleData(List<T> sets) {
super(sets);
}
}

View File

@@ -0,0 +1,48 @@
package com.github.mikephil.charting.data;
import android.graphics.Color;
import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet;
import java.util.List;
/**
* Baseclass of all DataSets for Bar-, Line-, Scatter- and CandleStickChart.
*
* @author Philipp Jahoda
*/
public abstract class BarLineScatterCandleBubbleDataSet<T extends Entry>
extends DataSet<T>
implements IBarLineScatterCandleBubbleDataSet<T> {
/**
* default highlight color
*/
protected int mHighLightColor = Color.rgb(255, 187, 115);
public BarLineScatterCandleBubbleDataSet(List<T> yVals, String label) {
super(yVals, label);
}
/**
* Sets the color that is used for drawing the highlight indicators. Dont
* forget to resolve the color using getResources().getColor(...) or
* Color.rgb(...).
*
* @param color
*/
public void setHighLightColor(int color) {
mHighLightColor = color;
}
@Override
public int getHighLightColor() {
return mHighLightColor;
}
protected void copy(BarLineScatterCandleBubbleDataSet barLineScatterCandleBubbleDataSet) {
super.copy(barLineScatterCandleBubbleDataSet);
barLineScatterCandleBubbleDataSet.mHighLightColor = mHighLightColor;
}
}

View File

@@ -0,0 +1,506 @@
package com.github.mikephil.charting.data;
import android.content.Context;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Typeface;
import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.formatter.IValueFormatter;
import com.github.mikephil.charting.interfaces.datasets.IDataSet;
import com.github.mikephil.charting.utils.ColorTemplate;
import com.github.mikephil.charting.utils.MPPointF;
import com.github.mikephil.charting.utils.Utils;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Philipp Jahoda on 21/10/15.
* This is the base dataset of all DataSets. It's purpose is to implement critical methods
* provided by the IDataSet interface.
*/
public abstract class BaseDataSet<T extends Entry> implements IDataSet<T> {
/**
* List representing all colors that are used for this DataSet
*/
protected List<Integer> mColors = null;
/**
* List representing all colors that are used for drawing the actual values for this DataSet
*/
protected List<Integer> mValueColors = null;
/**
* label that describes the DataSet or the data the DataSet represents
*/
private String mLabel = "DataSet";
/**
* this specifies which axis this DataSet should be plotted against
*/
protected YAxis.AxisDependency mAxisDependency = YAxis.AxisDependency.LEFT;
/**
* if true, value highlightning is enabled
*/
protected boolean mHighlightEnabled = true;
/**
* custom formatter that is used instead of the auto-formatter if set
*/
protected transient IValueFormatter mValueFormatter;
/**
* the typeface used for the value text
*/
protected Typeface mValueTypeface;
private Legend.LegendForm mForm = Legend.LegendForm.DEFAULT;
private float mFormSize = Float.NaN;
private float mFormLineWidth = Float.NaN;
private DashPathEffect mFormLineDashEffect = null;
/**
* if true, y-values are drawn on the chart
*/
protected boolean mDrawValues = true;
/**
* if true, y-icons are drawn on the chart
*/
protected boolean mDrawIcons = true;
/**
* the offset for drawing icons (in dp)
*/
protected MPPointF mIconsOffset = new MPPointF();
/**
* the size of the value-text labels
*/
protected float mValueTextSize = 17f;
/**
* flag that indicates if the DataSet is visible or not
*/
protected boolean mVisible = true;
/**
* Default constructor.
*/
public BaseDataSet() {
mColors = new ArrayList<Integer>();
mValueColors = new ArrayList<Integer>();
// default color
mColors.add(Color.rgb(140, 234, 255));
mValueColors.add(Color.BLACK);
}
/**
* Constructor with label.
*
* @param label
*/
public BaseDataSet(String label) {
this();
this.mLabel = label;
}
/**
* Use this method to tell the data set that the underlying data has changed.
*/
public void notifyDataSetChanged() {
calcMinMax();
}
/**
* ###### ###### COLOR GETTING RELATED METHODS ##### ######
*/
@Override
public List<Integer> getColors() {
return mColors;
}
public List<Integer> getValueColors() {
return mValueColors;
}
@Override
public int getColor() {
return mColors.get(0);
}
@Override
public int getColor(int index) {
return mColors.get(index % mColors.size());
}
/**
* ###### ###### COLOR SETTING RELATED METHODS ##### ######
*/
/**
* Sets the colors that should be used fore this DataSet. Colors are reused
* as soon as the number of Entries the DataSet represents is higher than
* the size of the colors array. If you are using colors from the resources,
* make sure that the colors are already prepared (by calling
* getResources().getColor(...)) before adding them to the DataSet.
*
* @param colors
*/
public void setColors(List<Integer> colors) {
this.mColors = colors;
}
/**
* Sets the colors that should be used fore this DataSet. Colors are reused
* as soon as the number of Entries the DataSet represents is higher than
* the size of the colors array. If you are using colors from the resources,
* make sure that the colors are already prepared (by calling
* getResources().getColor(...)) before adding them to the DataSet.
*
* @param colors
*/
public void setColors(int... colors) {
this.mColors = ColorTemplate.createColors(colors);
}
/**
* Sets the colors that should be used fore this DataSet. Colors are reused
* as soon as the number of Entries the DataSet represents is higher than
* the size of the colors array. You can use
* "new int[] { R.color.red, R.color.green, ... }" to provide colors for
* this method. Internally, the colors are resolved using
* getResources().getColor(...)
*
* @param colors
*/
public void setColors(int[] colors, Context c) {
if (mColors == null) {
mColors = new ArrayList<>();
}
mColors.clear();
for (int color : colors) {
mColors.add(c.getResources().getColor(color));
}
}
/**
* Adds a new color to the colors array of the DataSet.
*
* @param color
*/
public void addColor(int color) {
if (mColors == null)
mColors = new ArrayList<Integer>();
mColors.add(color);
}
/**
* Sets the one and ONLY color that should be used for this DataSet.
* Internally, this recreates the colors array and adds the specified color.
*
* @param color
*/
public void setColor(int color) {
resetColors();
mColors.add(color);
}
/**
* Sets a color with a specific alpha value.
*
* @param color
* @param alpha from 0-255
*/
public void setColor(int color, int alpha) {
setColor(Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color)));
}
/**
* Sets colors with a specific alpha value.
*
* @param colors
* @param alpha
*/
public void setColors(int[] colors, int alpha) {
resetColors();
for (int color : colors) {
addColor(Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color)));
}
}
/**
* Resets all colors of this DataSet and recreates the colors array.
*/
public void resetColors() {
if (mColors == null) {
mColors = new ArrayList<Integer>();
}
mColors.clear();
}
/**
* ###### ###### OTHER STYLING RELATED METHODS ##### ######
*/
@Override
public void setLabel(String label) {
mLabel = label;
}
@Override
public String getLabel() {
return mLabel;
}
@Override
public void setHighlightEnabled(boolean enabled) {
mHighlightEnabled = enabled;
}
@Override
public boolean isHighlightEnabled() {
return mHighlightEnabled;
}
@Override
public void setValueFormatter(IValueFormatter f) {
if (f == null)
return;
else
mValueFormatter = f;
}
@Override
public IValueFormatter getValueFormatter() {
if (needsFormatter())
return Utils.getDefaultValueFormatter();
return mValueFormatter;
}
@Override
public boolean needsFormatter() {
return mValueFormatter == null;
}
@Override
public void setValueTextColor(int color) {
mValueColors.clear();
mValueColors.add(color);
}
@Override
public void setValueTextColors(List<Integer> colors) {
mValueColors = colors;
}
@Override
public void setValueTypeface(Typeface tf) {
mValueTypeface = tf;
}
@Override
public void setValueTextSize(float size) {
mValueTextSize = Utils.convertDpToPixel(size);
}
@Override
public int getValueTextColor() {
return mValueColors.get(0);
}
@Override
public int getValueTextColor(int index) {
return mValueColors.get(index % mValueColors.size());
}
@Override
public Typeface getValueTypeface() {
return mValueTypeface;
}
@Override
public float getValueTextSize() {
return mValueTextSize;
}
public void setForm(Legend.LegendForm form) {
mForm = form;
}
@Override
public Legend.LegendForm getForm() {
return mForm;
}
public void setFormSize(float formSize) {
mFormSize = formSize;
}
@Override
public float getFormSize() {
return mFormSize;
}
public void setFormLineWidth(float formLineWidth) {
mFormLineWidth = formLineWidth;
}
@Override
public float getFormLineWidth() {
return mFormLineWidth;
}
public void setFormLineDashEffect(DashPathEffect dashPathEffect) {
mFormLineDashEffect = dashPathEffect;
}
@Override
public DashPathEffect getFormLineDashEffect() {
return mFormLineDashEffect;
}
@Override
public void setDrawValues(boolean enabled) {
this.mDrawValues = enabled;
}
@Override
public boolean isDrawValuesEnabled() {
return mDrawValues;
}
@Override
public void setDrawIcons(boolean enabled) {
mDrawIcons = enabled;
}
@Override
public boolean isDrawIconsEnabled() {
return mDrawIcons;
}
@Override
public void setIconsOffset(MPPointF offsetDp) {
mIconsOffset.x = offsetDp.x;
mIconsOffset.y = offsetDp.y;
}
@Override
public MPPointF getIconsOffset() {
return mIconsOffset;
}
@Override
public void setVisible(boolean visible) {
mVisible = visible;
}
@Override
public boolean isVisible() {
return mVisible;
}
@Override
public YAxis.AxisDependency getAxisDependency() {
return mAxisDependency;
}
@Override
public void setAxisDependency(YAxis.AxisDependency dependency) {
mAxisDependency = dependency;
}
/**
* ###### ###### DATA RELATED METHODS ###### ######
*/
@Override
public int getIndexInEntries(int xIndex) {
for (int i = 0; i < getEntryCount(); i++) {
if (xIndex == getEntryForIndex(i).getX())
return i;
}
return -1;
}
@Override
public boolean removeFirst() {
if (getEntryCount() > 0) {
T entry = getEntryForIndex(0);
return removeEntry(entry);
} else
return false;
}
@Override
public boolean removeLast() {
if (getEntryCount() > 0) {
T e = getEntryForIndex(getEntryCount() - 1);
return removeEntry(e);
} else
return false;
}
@Override
public boolean removeEntryByXValue(float xValue) {
T e = getEntryForXValue(xValue, Float.NaN);
return removeEntry(e);
}
@Override
public boolean removeEntry(int index) {
T e = getEntryForIndex(index);
return removeEntry(e);
}
@Override
public boolean contains(T e) {
for (int i = 0; i < getEntryCount(); i++) {
if (getEntryForIndex(i).equals(e))
return true;
}
return false;
}
protected void copy(BaseDataSet baseDataSet) {
baseDataSet.mAxisDependency = mAxisDependency;
baseDataSet.mColors = mColors;
baseDataSet.mDrawIcons = mDrawIcons;
baseDataSet.mDrawValues = mDrawValues;
baseDataSet.mForm = mForm;
baseDataSet.mFormLineDashEffect = mFormLineDashEffect;
baseDataSet.mFormLineWidth = mFormLineWidth;
baseDataSet.mFormSize = mFormSize;
baseDataSet.mHighlightEnabled = mHighlightEnabled;
baseDataSet.mIconsOffset = mIconsOffset;
baseDataSet.mValueColors = mValueColors;
baseDataSet.mValueFormatter = mValueFormatter;
baseDataSet.mValueColors = mValueColors;
baseDataSet.mValueTextSize = mValueTextSize;
baseDataSet.mVisible = mVisible;
}
}

View File

@@ -0,0 +1,97 @@
package com.github.mikephil.charting.data;
import android.graphics.drawable.Drawable;
/**
* Created by Philipp Jahoda on 02/06/16.
*/
public abstract class BaseEntry {
/** the y value */
private float y = 0f;
/** optional spot for additional data this Entry represents */
private Object mData = null;
/** optional icon image */
private Drawable mIcon = null;
public BaseEntry() {
}
public BaseEntry(float y) {
this.y = y;
}
public BaseEntry(float y, Object data) {
this(y);
this.mData = data;
}
public BaseEntry(float y, Drawable icon) {
this(y);
this.mIcon = icon;
}
public BaseEntry(float y, Drawable icon, Object data) {
this(y);
this.mIcon = icon;
this.mData = data;
}
/**
* Returns the y value of this Entry.
*
* @return
*/
public float getY() {
return y;
}
/**
* Sets the icon drawable
*
* @param icon
*/
public void setIcon(Drawable icon) {
this.mIcon = icon;
}
/**
* Returns the icon of this Entry.
*
* @return
*/
public Drawable getIcon() {
return mIcon;
}
/**
* Sets the y-value for the Entry.
*
* @param y
*/
public void setY(float y) {
this.y = y;
}
/**
* Returns the data, additional information that this Entry represents, or
* null, if no data has been specified.
*
* @return
*/
public Object getData() {
return mData;
}
/**
* Sets additional data this Entry should represent.
*
* @param data
*/
public void setData(Object data) {
this.mData = data;
}
}

View File

@@ -0,0 +1,34 @@
package com.github.mikephil.charting.data;
import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet;
import java.util.List;
public class BubbleData extends BarLineScatterCandleBubbleData<IBubbleDataSet> {
public BubbleData() {
super();
}
public BubbleData(IBubbleDataSet... dataSets) {
super(dataSets);
}
public BubbleData(List<IBubbleDataSet> dataSets) {
super(dataSets);
}
/**
* Sets the width of the circle that surrounds the bubble when highlighted
* for all DataSet objects this data object contains, in dp.
*
* @param width
*/
public void setHighlightCircleWidth(float width) {
for (IBubbleDataSet set : mDataSets) {
set.setHighlightCircleWidth(width);
}
}
}

View File

@@ -0,0 +1,71 @@
package com.github.mikephil.charting.data;
import com.github.mikephil.charting.interfaces.datasets.IBubbleDataSet;
import com.github.mikephil.charting.utils.Utils;
import java.util.ArrayList;
import java.util.List;
public class BubbleDataSet extends BarLineScatterCandleBubbleDataSet<BubbleEntry> implements IBubbleDataSet {
protected float mMaxSize;
protected boolean mNormalizeSize = true;
private float mHighlightCircleWidth = 2.5f;
public BubbleDataSet(List<BubbleEntry> yVals, String label) {
super(yVals, label);
}
@Override
public void setHighlightCircleWidth(float width) {
mHighlightCircleWidth = Utils.convertDpToPixel(width);
}
@Override
public float getHighlightCircleWidth() {
return mHighlightCircleWidth;
}
@Override
protected void calcMinMax(BubbleEntry e) {
super.calcMinMax(e);
final float size = e.getSize();
if (size > mMaxSize) {
mMaxSize = size;
}
}
@Override
public DataSet<BubbleEntry> copy() {
List<BubbleEntry> entries = new ArrayList<BubbleEntry>();
for (int i = 0; i < mEntries.size(); i++) {
entries.add(mEntries.get(i).copy());
}
BubbleDataSet copied = new BubbleDataSet(entries, getLabel());
copy(copied);
return copied;
}
protected void copy(BubbleDataSet bubbleDataSet) {
bubbleDataSet.mHighlightCircleWidth = mHighlightCircleWidth;
bubbleDataSet.mNormalizeSize = mNormalizeSize;
}
@Override
public float getMaxSize() {
return mMaxSize;
}
@Override
public boolean isNormalizeSizeEnabled() {
return mNormalizeSize;
}
public void setNormalizeSizeEnabled(boolean normalizeSize) {
mNormalizeSize = normalizeSize;
}
}

View File

@@ -0,0 +1,91 @@
package com.github.mikephil.charting.data;
import android.annotation.SuppressLint;
import android.graphics.drawable.Drawable;
/**
* Subclass of Entry that holds a value for one entry in a BubbleChart. Bubble
* chart implementation: Copyright 2015 Pierre-Marc Airoldi Licensed under
* Apache License 2.0
*
* @author Philipp Jahoda
*/
@SuppressLint("ParcelCreator")
public class BubbleEntry extends Entry {
/** size value */
private float mSize = 0f;
/**
* Constructor.
*
* @param x The value on the x-axis.
* @param y The value on the y-axis.
* @param size The size of the bubble.
*/
public BubbleEntry(float x, float y, float size) {
super(x, y);
this.mSize = size;
}
/**
* Constructor.
*
* @param x The value on the x-axis.
* @param y The value on the y-axis.
* @param size The size of the bubble.
* @param data Spot for additional data this Entry represents.
*/
public BubbleEntry(float x, float y, float size, Object data) {
super(x, y, data);
this.mSize = size;
}
/**
* Constructor.
*
* @param x The value on the x-axis.
* @param y The value on the y-axis.
* @param size The size of the bubble.
* @param icon Icon image
*/
public BubbleEntry(float x, float y, float size, Drawable icon) {
super(x, y, icon);
this.mSize = size;
}
/**
* Constructor.
*
* @param x The value on the x-axis.
* @param y The value on the y-axis.
* @param size The size of the bubble.
* @param icon Icon image
* @param data Spot for additional data this Entry represents.
*/
public BubbleEntry(float x, float y, float size, Drawable icon, Object data) {
super(x, y, icon, data);
this.mSize = size;
}
public BubbleEntry copy() {
BubbleEntry c = new BubbleEntry(getX(), getY(), mSize, getData());
return c;
}
/**
* Returns the size of this entry (the size of the bubble).
*
* @return
*/
public float getSize() {
return mSize;
}
public void setSize(float size) {
this.mSize = size;
}
}

View File

@@ -0,0 +1,21 @@
package com.github.mikephil.charting.data;
import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet;
import java.util.ArrayList;
import java.util.List;
public class CandleData extends BarLineScatterCandleBubbleData<ICandleDataSet> {
public CandleData() {
super();
}
public CandleData(List<ICandleDataSet> dataSets) {
super(dataSets);
}
public CandleData(ICandleDataSet... dataSets) {
super(dataSets);
}
}

View File

@@ -0,0 +1,295 @@
package com.github.mikephil.charting.data;
import android.graphics.Paint;
import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet;
import com.github.mikephil.charting.utils.ColorTemplate;
import com.github.mikephil.charting.utils.Utils;
import java.util.ArrayList;
import java.util.List;
/**
* DataSet for the CandleStickChart.
*
* @author Philipp Jahoda
*/
public class CandleDataSet extends LineScatterCandleRadarDataSet<CandleEntry> implements ICandleDataSet {
/**
* the width of the shadow of the candle
*/
private float mShadowWidth = 3f;
/**
* should the candle bars show?
* when false, only "ticks" will show
* <p/>
* - default: true
*/
private boolean mShowCandleBar = true;
/**
* the space between the candle entries, default 0.1f (10%)
*/
private float mBarSpace = 0.1f;
/**
* use candle color for the shadow
*/
private boolean mShadowColorSameAsCandle = false;
/**
* paint style when open < close
* increasing candlesticks are traditionally hollow
*/
protected Paint.Style mIncreasingPaintStyle = Paint.Style.STROKE;
/**
* paint style when open > close
* descreasing candlesticks are traditionally filled
*/
protected Paint.Style mDecreasingPaintStyle = Paint.Style.FILL;
/**
* color for open == close
*/
protected int mNeutralColor = ColorTemplate.COLOR_SKIP;
/**
* color for open < close
*/
protected int mIncreasingColor = ColorTemplate.COLOR_SKIP;
/**
* color for open > close
*/
protected int mDecreasingColor = ColorTemplate.COLOR_SKIP;
/**
* shadow line color, set -1 for backward compatibility and uses default
* color
*/
protected int mShadowColor = ColorTemplate.COLOR_SKIP;
public CandleDataSet(List<CandleEntry> yVals, String label) {
super(yVals, label);
}
@Override
public DataSet<CandleEntry> copy() {
List<CandleEntry> entries = new ArrayList<CandleEntry>();
for (int i = 0; i < mEntries.size(); i++) {
entries.add(mEntries.get(i).copy());
}
CandleDataSet copied = new CandleDataSet(entries, getLabel());
copy(copied);
return copied;
}
protected void copy(CandleDataSet candleDataSet) {
super.copy(candleDataSet);
candleDataSet.mShadowWidth = mShadowWidth;
candleDataSet.mShowCandleBar = mShowCandleBar;
candleDataSet.mBarSpace = mBarSpace;
candleDataSet.mShadowColorSameAsCandle = mShadowColorSameAsCandle;
candleDataSet.mHighLightColor = mHighLightColor;
candleDataSet.mIncreasingPaintStyle = mIncreasingPaintStyle;
candleDataSet.mDecreasingPaintStyle = mDecreasingPaintStyle;
candleDataSet.mNeutralColor = mNeutralColor;
candleDataSet.mIncreasingColor = mIncreasingColor;
candleDataSet.mDecreasingColor = mDecreasingColor;
candleDataSet.mShadowColor = mShadowColor;
}
@Override
protected void calcMinMax(CandleEntry e) {
if (e.getLow() < mYMin)
mYMin = e.getLow();
if (e.getHigh() > mYMax)
mYMax = e.getHigh();
calcMinMaxX(e);
}
@Override
protected void calcMinMaxY(CandleEntry e) {
if (e.getHigh() < mYMin)
mYMin = e.getHigh();
if (e.getHigh() > mYMax)
mYMax = e.getHigh();
if (e.getLow() < mYMin)
mYMin = e.getLow();
if (e.getLow() > mYMax)
mYMax = e.getLow();
}
/**
* Sets the space that is left out on the left and right side of each
* candle, default 0.1f (10%), max 0.45f, min 0f
*
* @param space
*/
public void setBarSpace(float space) {
if (space < 0f)
space = 0f;
if (space > 0.45f)
space = 0.45f;
mBarSpace = space;
}
@Override
public float getBarSpace() {
return mBarSpace;
}
/**
* Sets the width of the candle-shadow-line in pixels. Default 3f.
*
* @param width
*/
public void setShadowWidth(float width) {
mShadowWidth = Utils.convertDpToPixel(width);
}
@Override
public float getShadowWidth() {
return mShadowWidth;
}
/**
* Sets whether the candle bars should show?
*
* @param showCandleBar
*/
public void setShowCandleBar(boolean showCandleBar) {
mShowCandleBar = showCandleBar;
}
@Override
public boolean getShowCandleBar() {
return mShowCandleBar;
}
// TODO
/**
* It is necessary to implement ColorsList class that will encapsulate
* colors list functionality, because It's wrong to copy paste setColor,
* addColor, ... resetColors for each time when we want to add a coloring
* options for one of objects
*
* @author Mesrop
*/
/** BELOW THIS COLOR HANDLING */
/**
* Sets the one and ONLY color that should be used for this DataSet when
* open == close.
*
* @param color
*/
public void setNeutralColor(int color) {
mNeutralColor = color;
}
@Override
public int getNeutralColor() {
return mNeutralColor;
}
/**
* Sets the one and ONLY color that should be used for this DataSet when
* open <= close.
*
* @param color
*/
public void setIncreasingColor(int color) {
mIncreasingColor = color;
}
@Override
public int getIncreasingColor() {
return mIncreasingColor;
}
/**
* Sets the one and ONLY color that should be used for this DataSet when
* open > close.
*
* @param color
*/
public void setDecreasingColor(int color) {
mDecreasingColor = color;
}
@Override
public int getDecreasingColor() {
return mDecreasingColor;
}
@Override
public Paint.Style getIncreasingPaintStyle() {
return mIncreasingPaintStyle;
}
/**
* Sets paint style when open < close
*
* @param paintStyle
*/
public void setIncreasingPaintStyle(Paint.Style paintStyle) {
this.mIncreasingPaintStyle = paintStyle;
}
@Override
public Paint.Style getDecreasingPaintStyle() {
return mDecreasingPaintStyle;
}
/**
* Sets paint style when open > close
*
* @param decreasingPaintStyle
*/
public void setDecreasingPaintStyle(Paint.Style decreasingPaintStyle) {
this.mDecreasingPaintStyle = decreasingPaintStyle;
}
@Override
public int getShadowColor() {
return mShadowColor;
}
/**
* Sets shadow color for all entries
*
* @param shadowColor
*/
public void setShadowColor(int shadowColor) {
this.mShadowColor = shadowColor;
}
@Override
public boolean getShadowColorSameAsCandle() {
return mShadowColorSameAsCandle;
}
/**
* Sets shadow color to be the same color as the candle color
*
* @param shadowColorSameAsCandle
*/
public void setShadowColorSameAsCandle(boolean shadowColorSameAsCandle) {
this.mShadowColorSameAsCandle = shadowColorSameAsCandle;
}
}

View File

@@ -0,0 +1,193 @@
package com.github.mikephil.charting.data;
import android.annotation.SuppressLint;
import android.graphics.drawable.Drawable;
/**
* Subclass of Entry that holds all values for one entry in a CandleStickChart.
*
* @author Philipp Jahoda
*/
@SuppressLint("ParcelCreator")
public class CandleEntry extends Entry {
/** shadow-high value */
private float mShadowHigh = 0f;
/** shadow-low value */
private float mShadowLow = 0f;
/** close value */
private float mClose = 0f;
/** open value */
private float mOpen = 0f;
/**
* Constructor.
*
* @param x The value on the x-axis
* @param shadowH The (shadow) high value
* @param shadowL The (shadow) low value
* @param open The open value
* @param close The close value
*/
public CandleEntry(float x, float shadowH, float shadowL, float open, float close) {
super(x, (shadowH + shadowL) / 2f);
this.mShadowHigh = shadowH;
this.mShadowLow = shadowL;
this.mOpen = open;
this.mClose = close;
}
/**
* Constructor.
*
* @param x The value on the x-axis
* @param shadowH The (shadow) high value
* @param shadowL The (shadow) low value
* @param open
* @param close
* @param data Spot for additional data this Entry represents
*/
public CandleEntry(float x, float shadowH, float shadowL, float open, float close,
Object data) {
super(x, (shadowH + shadowL) / 2f, data);
this.mShadowHigh = shadowH;
this.mShadowLow = shadowL;
this.mOpen = open;
this.mClose = close;
}
/**
* Constructor.
*
* @param x The value on the x-axis
* @param shadowH The (shadow) high value
* @param shadowL The (shadow) low value
* @param open
* @param close
* @param icon Icon image
*/
public CandleEntry(float x, float shadowH, float shadowL, float open, float close,
Drawable icon) {
super(x, (shadowH + shadowL) / 2f, icon);
this.mShadowHigh = shadowH;
this.mShadowLow = shadowL;
this.mOpen = open;
this.mClose = close;
}
/**
* Constructor.
*
* @param x The value on the x-axis
* @param shadowH The (shadow) high value
* @param shadowL The (shadow) low value
* @param open
* @param close
* @param icon Icon image
* @param data Spot for additional data this Entry represents
*/
public CandleEntry(float x, float shadowH, float shadowL, float open, float close,
Drawable icon, Object data) {
super(x, (shadowH + shadowL) / 2f, icon, data);
this.mShadowHigh = shadowH;
this.mShadowLow = shadowL;
this.mOpen = open;
this.mClose = close;
}
/**
* Returns the overall range (difference) between shadow-high and
* shadow-low.
*
* @return
*/
public float getShadowRange() {
return Math.abs(mShadowHigh - mShadowLow);
}
/**
* Returns the body size (difference between open and close).
*
* @return
*/
public float getBodyRange() {
return Math.abs(mOpen - mClose);
}
/**
* Returns the center value of the candle. (Middle value between high and
* low)
*/
@Override
public float getY() {
return super.getY();
}
public CandleEntry copy() {
CandleEntry c = new CandleEntry(getX(), mShadowHigh, mShadowLow, mOpen,
mClose, getData());
return c;
}
/**
* Returns the upper shadows highest value.
*
* @return
*/
public float getHigh() {
return mShadowHigh;
}
public void setHigh(float mShadowHigh) {
this.mShadowHigh = mShadowHigh;
}
/**
* Returns the lower shadows lowest value.
*
* @return
*/
public float getLow() {
return mShadowLow;
}
public void setLow(float mShadowLow) {
this.mShadowLow = mShadowLow;
}
/**
* Returns the bodys close value.
*
* @return
*/
public float getClose() {
return mClose;
}
public void setClose(float mClose) {
this.mClose = mClose;
}
/**
* Returns the bodys open value.
*
* @return
*/
public float getOpen() {
return mOpen;
}
public void setOpen(float mOpen) {
this.mOpen = mOpen;
}
}

View File

@@ -0,0 +1,821 @@
package com.github.mikephil.charting.data;
import android.graphics.Typeface;
import android.util.Log;
import com.github.mikephil.charting.components.YAxis.AxisDependency;
import com.github.mikephil.charting.formatter.IValueFormatter;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.interfaces.datasets.IDataSet;
import java.util.ArrayList;
import java.util.List;
/**
* Class that holds all relevant data that represents the chart. That involves
* at least one (or more) DataSets, and an array of x-values.
*
* @author Philipp Jahoda
*/
public abstract class ChartData<T extends IDataSet<? extends Entry>> {
/**
* maximum y-value in the value array across all axes
*/
protected float mYMax = -Float.MAX_VALUE;
/**
* the minimum y-value in the value array across all axes
*/
protected float mYMin = Float.MAX_VALUE;
/**
* maximum x-value in the value array
*/
protected float mXMax = -Float.MAX_VALUE;
/**
* minimum x-value in the value array
*/
protected float mXMin = Float.MAX_VALUE;
protected float mLeftAxisMax = -Float.MAX_VALUE;
protected float mLeftAxisMin = Float.MAX_VALUE;
protected float mRightAxisMax = -Float.MAX_VALUE;
protected float mRightAxisMin = Float.MAX_VALUE;
/**
* array that holds all DataSets the ChartData object represents
*/
protected List<T> mDataSets;
/**
* Default constructor.
*/
public ChartData() {
mDataSets = new ArrayList<T>();
}
/**
* Constructor taking single or multiple DataSet objects.
*
* @param dataSets
*/
public ChartData(T... dataSets) {
mDataSets = arrayToList(dataSets);
notifyDataChanged();
}
/**
* Created because Arrays.asList(...) does not support modification.
*
* @param array
* @return
*/
private List<T> arrayToList(T[] array) {
List<T> list = new ArrayList<>();
for (T set : array) {
list.add(set);
}
return list;
}
/**
* constructor for chart data
*
* @param sets the dataset array
*/
public ChartData(List<T> sets) {
this.mDataSets = sets;
notifyDataChanged();
}
/**
* Call this method to let the ChartData know that the underlying data has
* changed. Calling this performs all necessary recalculations needed when
* the contained data has changed.
*/
public void notifyDataChanged() {
calcMinMax();
}
/**
* Calc minimum and maximum y-values over all DataSets.
* Tell DataSets to recalculate their min and max y-values, this is only needed for autoScaleMinMax.
*
* @param fromX the x-value to start the calculation from
* @param toX the x-value to which the calculation should be performed
*/
public void calcMinMaxY(float fromX, float toX) {
for (T set : mDataSets) {
set.calcMinMaxY(fromX, toX);
}
// apply the new data
calcMinMax();
}
/**
* Calc minimum and maximum values (both x and y) over all DataSets.
*/
protected void calcMinMax() {
if (mDataSets == null)
return;
mYMax = -Float.MAX_VALUE;
mYMin = Float.MAX_VALUE;
mXMax = -Float.MAX_VALUE;
mXMin = Float.MAX_VALUE;
for (T set : mDataSets) {
calcMinMax(set);
}
mLeftAxisMax = -Float.MAX_VALUE;
mLeftAxisMin = Float.MAX_VALUE;
mRightAxisMax = -Float.MAX_VALUE;
mRightAxisMin = Float.MAX_VALUE;
// left axis
T firstLeft = getFirstLeft(mDataSets);
if (firstLeft != null) {
mLeftAxisMax = firstLeft.getYMax();
mLeftAxisMin = firstLeft.getYMin();
for (T dataSet : mDataSets) {
if (dataSet.getAxisDependency() == AxisDependency.LEFT) {
if (dataSet.getYMin() < mLeftAxisMin)
mLeftAxisMin = dataSet.getYMin();
if (dataSet.getYMax() > mLeftAxisMax)
mLeftAxisMax = dataSet.getYMax();
}
}
}
// right axis
T firstRight = getFirstRight(mDataSets);
if (firstRight != null) {
mRightAxisMax = firstRight.getYMax();
mRightAxisMin = firstRight.getYMin();
for (T dataSet : mDataSets) {
if (dataSet.getAxisDependency() == AxisDependency.RIGHT) {
if (dataSet.getYMin() < mRightAxisMin)
mRightAxisMin = dataSet.getYMin();
if (dataSet.getYMax() > mRightAxisMax)
mRightAxisMax = dataSet.getYMax();
}
}
}
}
/** ONLY GETTERS AND SETTERS BELOW THIS */
/**
* returns the number of LineDataSets this object contains
*
* @return
*/
public int getDataSetCount() {
if (mDataSets == null)
return 0;
return mDataSets.size();
}
/**
* Returns the smallest y-value the data object contains.
*
* @return
*/
public float getYMin() {
return mYMin;
}
/**
* Returns the minimum y-value for the specified axis.
*
* @param axis
* @return
*/
public float getYMin(AxisDependency axis) {
if (axis == AxisDependency.LEFT) {
if (mLeftAxisMin == Float.MAX_VALUE) {
return mRightAxisMin;
} else
return mLeftAxisMin;
} else {
if (mRightAxisMin == Float.MAX_VALUE) {
return mLeftAxisMin;
} else
return mRightAxisMin;
}
}
/**
* Returns the greatest y-value the data object contains.
*
* @return
*/
public float getYMax() {
return mYMax;
}
/**
* Returns the maximum y-value for the specified axis.
*
* @param axis
* @return
*/
public float getYMax(AxisDependency axis) {
if (axis == AxisDependency.LEFT) {
if (mLeftAxisMax == -Float.MAX_VALUE) {
return mRightAxisMax;
} else
return mLeftAxisMax;
} else {
if (mRightAxisMax == -Float.MAX_VALUE) {
return mLeftAxisMax;
} else
return mRightAxisMax;
}
}
/**
* Returns the minimum x-value this data object contains.
*
* @return
*/
public float getXMin() {
return mXMin;
}
/**
* Returns the maximum x-value this data object contains.
*
* @return
*/
public float getXMax() {
return mXMax;
}
/**
* Returns all DataSet objects this ChartData object holds.
*
* @return
*/
public List<T> getDataSets() {
return mDataSets;
}
/**
* Retrieve the index of a DataSet with a specific label from the ChartData.
* Search can be case sensitive or not. IMPORTANT: This method does
* calculations at runtime, do not over-use in performance critical
* situations.
*
* @param dataSets the DataSet array to search
* @param label
* @param ignorecase if true, the search is not case-sensitive
* @return
*/
protected int getDataSetIndexByLabel(List<T> dataSets, String label,
boolean ignorecase) {
if (ignorecase) {
for (int i = 0; i < dataSets.size(); i++)
if (label.equalsIgnoreCase(dataSets.get(i).getLabel()))
return i;
} else {
for (int i = 0; i < dataSets.size(); i++)
if (label.equals(dataSets.get(i).getLabel()))
return i;
}
return -1;
}
/**
* Returns the labels of all DataSets as a string array.
*
* @return
*/
public String[] getDataSetLabels() {
String[] types = new String[mDataSets.size()];
for (int i = 0; i < mDataSets.size(); i++) {
types[i] = mDataSets.get(i).getLabel();
}
return types;
}
/**
* Get the Entry for a corresponding highlight object
*
* @param highlight
* @return the entry that is highlighted
*/
public Entry getEntryForHighlight(Highlight highlight) {
if (highlight.getDataSetIndex() >= mDataSets.size())
return null;
else {
return mDataSets.get(highlight.getDataSetIndex()).getEntryForXValue(highlight.getX(), highlight.getY());
}
}
/**
* Returns the DataSet object with the given label. Search can be case
* sensitive or not. IMPORTANT: This method does calculations at runtime.
* Use with care in performance critical situations.
*
* @param label
* @param ignorecase
* @return
*/
public T getDataSetByLabel(String label, boolean ignorecase) {
int index = getDataSetIndexByLabel(mDataSets, label, ignorecase);
if (index < 0 || index >= mDataSets.size())
return null;
else
return mDataSets.get(index);
}
public T getDataSetByIndex(int index) {
if (mDataSets == null || index < 0 || index >= mDataSets.size())
return null;
return mDataSets.get(index);
}
/**
* Adds a DataSet dynamically.
*
* @param d
*/
public void addDataSet(T d) {
if (d == null)
return;
calcMinMax(d);
mDataSets.add(d);
}
/**
* Removes the given DataSet from this data object. Also recalculates all
* minimum and maximum values. Returns true if a DataSet was removed, false
* if no DataSet could be removed.
*
* @param d
*/
public boolean removeDataSet(T d) {
if (d == null)
return false;
boolean removed = mDataSets.remove(d);
// if a DataSet was removed
if (removed) {
notifyDataChanged();
}
return removed;
}
/**
* Removes the DataSet at the given index in the DataSet array from the data
* object. Also recalculates all minimum and maximum values. Returns true if
* a DataSet was removed, false if no DataSet could be removed.
*
* @param index
*/
public boolean removeDataSet(int index) {
if (index >= mDataSets.size() || index < 0)
return false;
T set = mDataSets.get(index);
return removeDataSet(set);
}
/**
* Adds an Entry to the DataSet at the specified index.
* Entries are added to the end of the list.
*
* @param e
* @param dataSetIndex
*/
public void addEntry(Entry e, int dataSetIndex) {
if (mDataSets.size() > dataSetIndex && dataSetIndex >= 0) {
IDataSet set = mDataSets.get(dataSetIndex);
// add the entry to the dataset
if (!set.addEntry(e))
return;
calcMinMax(e, set.getAxisDependency());
} else {
Log.e("addEntry", "Cannot add Entry because dataSetIndex too high or too low.");
}
}
/**
* Adjusts the current minimum and maximum values based on the provided Entry object.
*
* @param e
* @param axis
*/
protected void calcMinMax(Entry e, AxisDependency axis) {
if (mYMax < e.getY())
mYMax = e.getY();
if (mYMin > e.getY())
mYMin = e.getY();
if (mXMax < e.getX())
mXMax = e.getX();
if (mXMin > e.getX())
mXMin = e.getX();
if (axis == AxisDependency.LEFT) {
if (mLeftAxisMax < e.getY())
mLeftAxisMax = e.getY();
if (mLeftAxisMin > e.getY())
mLeftAxisMin = e.getY();
} else {
if (mRightAxisMax < e.getY())
mRightAxisMax = e.getY();
if (mRightAxisMin > e.getY())
mRightAxisMin = e.getY();
}
}
/**
* Adjusts the minimum and maximum values based on the given DataSet.
*
* @param d
*/
protected void calcMinMax(T d) {
if (mYMax < d.getYMax())
mYMax = d.getYMax();
if (mYMin > d.getYMin())
mYMin = d.getYMin();
if (mXMax < d.getXMax())
mXMax = d.getXMax();
if (mXMin > d.getXMin())
mXMin = d.getXMin();
if (d.getAxisDependency() == AxisDependency.LEFT) {
if (mLeftAxisMax < d.getYMax())
mLeftAxisMax = d.getYMax();
if (mLeftAxisMin > d.getYMin())
mLeftAxisMin = d.getYMin();
} else {
if (mRightAxisMax < d.getYMax())
mRightAxisMax = d.getYMax();
if (mRightAxisMin > d.getYMin())
mRightAxisMin = d.getYMin();
}
}
/**
* Removes the given Entry object from the DataSet at the specified index.
*
* @param e
* @param dataSetIndex
*/
public boolean removeEntry(Entry e, int dataSetIndex) {
// entry null, outofbounds
if (e == null || dataSetIndex >= mDataSets.size())
return false;
IDataSet set = mDataSets.get(dataSetIndex);
if (set != null) {
// remove the entry from the dataset
boolean removed = set.removeEntry(e);
if (removed) {
notifyDataChanged();
}
return removed;
} else
return false;
}
/**
* Removes the Entry object closest to the given DataSet at the
* specified index. Returns true if an Entry was removed, false if no Entry
* was found that meets the specified requirements.
*
* @param xValue
* @param dataSetIndex
* @return
*/
public boolean removeEntry(float xValue, int dataSetIndex) {
if (dataSetIndex >= mDataSets.size())
return false;
IDataSet dataSet = mDataSets.get(dataSetIndex);
Entry e = dataSet.getEntryForXValue(xValue, Float.NaN);
if (e == null)
return false;
return removeEntry(e, dataSetIndex);
}
/**
* Returns the DataSet that contains the provided Entry, or null, if no
* DataSet contains this Entry.
*
* @param e
* @return
*/
public T getDataSetForEntry(Entry e) {
if (e == null)
return null;
for (int i = 0; i < mDataSets.size(); i++) {
T set = mDataSets.get(i);
for (int j = 0; j < set.getEntryCount(); j++) {
if (e.equalTo(set.getEntryForXValue(e.getX(), e.getY())))
return set;
}
}
return null;
}
/**
* Returns all colors used across all DataSet objects this object
* represents.
*
* @return
*/
public int[] getColors() {
if (mDataSets == null)
return null;
int clrcnt = 0;
for (int i = 0; i < mDataSets.size(); i++) {
clrcnt += mDataSets.get(i).getColors().size();
}
int[] colors = new int[clrcnt];
int cnt = 0;
for (int i = 0; i < mDataSets.size(); i++) {
List<Integer> clrs = mDataSets.get(i).getColors();
for (Integer clr : clrs) {
colors[cnt] = clr;
cnt++;
}
}
return colors;
}
/**
* Returns the index of the provided DataSet in the DataSet array of this data object, or -1 if it does not exist.
*
* @param dataSet
* @return
*/
public int getIndexOfDataSet(T dataSet) {
return mDataSets.indexOf(dataSet);
}
/**
* Returns the first DataSet from the datasets-array that has it's dependency on the left axis.
* Returns null if no DataSet with left dependency could be found.
*
* @return
*/
protected T getFirstLeft(List<T> sets) {
for (T dataSet : sets) {
if (dataSet.getAxisDependency() == AxisDependency.LEFT)
return dataSet;
}
return null;
}
/**
* Returns the first DataSet from the datasets-array that has it's dependency on the right axis.
* Returns null if no DataSet with right dependency could be found.
*
* @return
*/
public T getFirstRight(List<T> sets) {
for (T dataSet : sets) {
if (dataSet.getAxisDependency() == AxisDependency.RIGHT)
return dataSet;
}
return null;
}
/**
* Sets a custom IValueFormatter for all DataSets this data object contains.
*
* @param f
*/
public void setValueFormatter(IValueFormatter f) {
if (f == null)
return;
else {
for (IDataSet set : mDataSets) {
set.setValueFormatter(f);
}
}
}
/**
* Sets the color of the value-text (color in which the value-labels are
* drawn) for all DataSets this data object contains.
*
* @param color
*/
public void setValueTextColor(int color) {
for (IDataSet set : mDataSets) {
set.setValueTextColor(color);
}
}
/**
* Sets the same list of value-colors for all DataSets this
* data object contains.
*
* @param colors
*/
public void setValueTextColors(List<Integer> colors) {
for (IDataSet set : mDataSets) {
set.setValueTextColors(colors);
}
}
/**
* Sets the Typeface for all value-labels for all DataSets this data object
* contains.
*
* @param tf
*/
public void setValueTypeface(Typeface tf) {
for (IDataSet set : mDataSets) {
set.setValueTypeface(tf);
}
}
/**
* Sets the size (in dp) of the value-text for all DataSets this data object
* contains.
*
* @param size
*/
public void setValueTextSize(float size) {
for (IDataSet set : mDataSets) {
set.setValueTextSize(size);
}
}
/**
* Enables / disables drawing values (value-text) for all DataSets this data
* object contains.
*
* @param enabled
*/
public void setDrawValues(boolean enabled) {
for (IDataSet set : mDataSets) {
set.setDrawValues(enabled);
}
}
/**
* Enables / disables highlighting values for all DataSets this data object
* contains. If set to true, this means that values can
* be highlighted programmatically or by touch gesture.
*/
public void setHighlightEnabled(boolean enabled) {
for (IDataSet set : mDataSets) {
set.setHighlightEnabled(enabled);
}
}
/**
* Returns true if highlighting of all underlying values is enabled, false
* if not.
*
* @return
*/
public boolean isHighlightEnabled() {
for (IDataSet set : mDataSets) {
if (!set.isHighlightEnabled())
return false;
}
return true;
}
/**
* Clears this data object from all DataSets and removes all Entries. Don't
* forget to invalidate the chart after this.
*/
public void clearValues() {
if (mDataSets != null) {
mDataSets.clear();
}
notifyDataChanged();
}
/**
* Checks if this data object contains the specified DataSet. Returns true
* if so, false if not.
*
* @param dataSet
* @return
*/
public boolean contains(T dataSet) {
for (T set : mDataSets) {
if (set.equals(dataSet))
return true;
}
return false;
}
/**
* Returns the total entry count across all DataSet objects this data object contains.
*
* @return
*/
public int getEntryCount() {
int count = 0;
for (T set : mDataSets) {
count += set.getEntryCount();
}
return count;
}
/**
* Returns the DataSet object with the maximum number of entries or null if there are no DataSets.
*
* @return
*/
public T getMaxEntryCountSet() {
if (mDataSets == null || mDataSets.isEmpty())
return null;
T max = mDataSets.get(0);
for (T set : mDataSets) {
if (set.getEntryCount() > max.getEntryCount())
max = set;
}
return max;
}
}

View File

@@ -0,0 +1,272 @@
package com.github.mikephil.charting.data;
import android.util.Log;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet;
import java.util.ArrayList;
import java.util.List;
/**
* Data object that allows the combination of Line-, Bar-, Scatter-, Bubble- and
* CandleData. Used in the CombinedChart class.
*
* @author Philipp Jahoda
*/
public class CombinedData extends BarLineScatterCandleBubbleData<IBarLineScatterCandleBubbleDataSet<? extends Entry>> {
private LineData mLineData;
private BarData mBarData;
private ScatterData mScatterData;
private CandleData mCandleData;
private BubbleData mBubbleData;
public CombinedData() {
super();
}
public void setData(LineData data) {
mLineData = data;
notifyDataChanged();
}
public void setData(BarData data) {
mBarData = data;
notifyDataChanged();
}
public void setData(ScatterData data) {
mScatterData = data;
notifyDataChanged();
}
public void setData(CandleData data) {
mCandleData = data;
notifyDataChanged();
}
public void setData(BubbleData data) {
mBubbleData = data;
notifyDataChanged();
}
@Override
public void calcMinMax() {
if(mDataSets == null){
mDataSets = new ArrayList<>();
}
mDataSets.clear();
mYMax = -Float.MAX_VALUE;
mYMin = Float.MAX_VALUE;
mXMax = -Float.MAX_VALUE;
mXMin = Float.MAX_VALUE;
mLeftAxisMax = -Float.MAX_VALUE;
mLeftAxisMin = Float.MAX_VALUE;
mRightAxisMax = -Float.MAX_VALUE;
mRightAxisMin = Float.MAX_VALUE;
List<BarLineScatterCandleBubbleData> allData = getAllData();
for (ChartData data : allData) {
data.calcMinMax();
List<IBarLineScatterCandleBubbleDataSet<? extends Entry>> sets = data.getDataSets();
mDataSets.addAll(sets);
if (data.getYMax() > mYMax)
mYMax = data.getYMax();
if (data.getYMin() < mYMin)
mYMin = data.getYMin();
if (data.getXMax() > mXMax)
mXMax = data.getXMax();
if (data.getXMin() < mXMin)
mXMin = data.getXMin();
for (IBarLineScatterCandleBubbleDataSet<? extends Entry> dataset : sets) {
if (dataset.getAxisDependency() == YAxis.AxisDependency.LEFT) {
if (dataset.getYMax() > mLeftAxisMax) {
mLeftAxisMax = dataset.getYMax();
}
if (dataset.getYMin() < mLeftAxisMin) {
mLeftAxisMin = dataset.getYMin();
}
}
else {
if (dataset.getYMax() > mRightAxisMax) {
mRightAxisMax = dataset.getYMax();
}
if (dataset.getYMin() < mRightAxisMin) {
mRightAxisMin = dataset.getYMin();
}
}
}
}
}
public BubbleData getBubbleData() {
return mBubbleData;
}
public LineData getLineData() {
return mLineData;
}
public BarData getBarData() {
return mBarData;
}
public ScatterData getScatterData() {
return mScatterData;
}
public CandleData getCandleData() {
return mCandleData;
}
/**
* Returns all data objects in row: line-bar-scatter-candle-bubble if not null.
*
* @return
*/
public List<BarLineScatterCandleBubbleData> getAllData() {
List<BarLineScatterCandleBubbleData> data = new ArrayList<BarLineScatterCandleBubbleData>();
if (mLineData != null)
data.add(mLineData);
if (mBarData != null)
data.add(mBarData);
if (mScatterData != null)
data.add(mScatterData);
if (mCandleData != null)
data.add(mCandleData);
if (mBubbleData != null)
data.add(mBubbleData);
return data;
}
public BarLineScatterCandleBubbleData getDataByIndex(int index) {
return getAllData().get(index);
}
@Override
public void notifyDataChanged() {
if (mLineData != null)
mLineData.notifyDataChanged();
if (mBarData != null)
mBarData.notifyDataChanged();
if (mCandleData != null)
mCandleData.notifyDataChanged();
if (mScatterData != null)
mScatterData.notifyDataChanged();
if (mBubbleData != null)
mBubbleData.notifyDataChanged();
calcMinMax(); // recalculate everything
}
/**
* Get the Entry for a corresponding highlight object
*
* @param highlight
* @return the entry that is highlighted
*/
@Override
public Entry getEntryForHighlight(Highlight highlight) {
if (highlight.getDataIndex() >= getAllData().size())
return null;
ChartData data = getDataByIndex(highlight.getDataIndex());
if (highlight.getDataSetIndex() >= data.getDataSetCount())
return null;
// The value of the highlighted entry could be NaN -
// if we are not interested in highlighting a specific value.
List<Entry> entries = data.getDataSetByIndex(highlight.getDataSetIndex())
.getEntriesForXValue(highlight.getX());
for (Entry entry : entries)
if (entry.getY() == highlight.getY() ||
Float.isNaN(highlight.getY()))
return entry;
return null;
}
/**
* Get dataset for highlight
*
* @param highlight current highlight
* @return dataset related to highlight
*/
public IBarLineScatterCandleBubbleDataSet<? extends Entry> getDataSetByHighlight(Highlight highlight) {
if (highlight.getDataIndex() >= getAllData().size())
return null;
BarLineScatterCandleBubbleData data = getDataByIndex(highlight.getDataIndex());
if (highlight.getDataSetIndex() >= data.getDataSetCount())
return null;
return (IBarLineScatterCandleBubbleDataSet<? extends Entry>)
data.getDataSets().get(highlight.getDataSetIndex());
}
public int getDataIndex(ChartData data) {
return getAllData().indexOf(data);
}
@Override
public boolean removeDataSet(IBarLineScatterCandleBubbleDataSet<? extends Entry> d) {
List<BarLineScatterCandleBubbleData> datas = getAllData();
boolean success = false;
for (ChartData data : datas) {
success = data.removeDataSet(d);
if (success) {
break;
}
}
return success;
}
@Deprecated
@Override
public boolean removeDataSet(int index) {
Log.e("MPAndroidChart", "removeDataSet(int index) not supported for CombinedData");
return false;
}
@Deprecated
@Override
public boolean removeEntry(Entry e, int dataSetIndex) {
Log.e("MPAndroidChart", "removeEntry(...) not supported for CombinedData");
return false;
}
@Deprecated
@Override
public boolean removeEntry(float xValue, int dataSetIndex) {
Log.e("MPAndroidChart", "removeEntry(...) not supported for CombinedData");
return false;
}
}

View File

@@ -0,0 +1,456 @@
package com.github.mikephil.charting.data;
import java.util.ArrayList;
import java.util.List;
/**
* The DataSet class represents one group or type of entries (Entry) in the
* Chart that belong together. It is designed to logically separate different
* groups of values inside the Chart (e.g. the values for a specific line in the
* LineChart, or the values of a specific group of bars in the BarChart).
*
* @author Philipp Jahoda
*/
public abstract class DataSet<T extends Entry> extends BaseDataSet<T> {
/**
* the entries that this DataSet represents / holds together
*/
protected List<T> mEntries;
/**
* maximum y-value in the value array
*/
protected float mYMax = -Float.MAX_VALUE;
/**
* minimum y-value in the value array
*/
protected float mYMin = Float.MAX_VALUE;
/**
* maximum x-value in the value array
*/
protected float mXMax = -Float.MAX_VALUE;
/**
* minimum x-value in the value array
*/
protected float mXMin = Float.MAX_VALUE;
/**
* Creates a new DataSet object with the given values (entries) it represents. Also, a
* label that describes the DataSet can be specified. The label can also be
* used to retrieve the DataSet from a ChartData object.
*
* @param entries
* @param label
*/
public DataSet(List<T> entries, String label) {
super(label);
this.mEntries = entries;
if (mEntries == null)
mEntries = new ArrayList<T>();
calcMinMax();
}
@Override
public void calcMinMax() {
mYMax = -Float.MAX_VALUE;
mYMin = Float.MAX_VALUE;
mXMax = -Float.MAX_VALUE;
mXMin = Float.MAX_VALUE;
if (mEntries == null || mEntries.isEmpty())
return;
for (T e : mEntries) {
calcMinMax(e);
}
}
@Override
public void calcMinMaxY(float fromX, float toX) {
mYMax = -Float.MAX_VALUE;
mYMin = Float.MAX_VALUE;
if (mEntries == null || mEntries.isEmpty())
return;
int indexFrom = getEntryIndex(fromX, Float.NaN, Rounding.DOWN);
int indexTo = getEntryIndex(toX, Float.NaN, Rounding.UP);
if (indexTo < indexFrom) return;
for (int i = indexFrom; i <= indexTo; i++) {
// only recalculate y
calcMinMaxY(mEntries.get(i));
}
}
/**
* Updates the min and max x and y value of this DataSet based on the given Entry.
*
* @param e
*/
protected void calcMinMax(T e) {
if (e == null)
return;
calcMinMaxX(e);
calcMinMaxY(e);
}
protected void calcMinMaxX(T e) {
if (e.getX() < mXMin)
mXMin = e.getX();
if (e.getX() > mXMax)
mXMax = e.getX();
}
protected void calcMinMaxY(T e) {
if (e.getY() < mYMin)
mYMin = e.getY();
if (e.getY() > mYMax)
mYMax = e.getY();
}
@Override
public int getEntryCount() {
return mEntries.size();
}
/**
* This method is deprecated.
* Use getEntries() instead.
*
* @return
*/
@Deprecated
public List<T> getValues() {
return mEntries;
}
/**
* Returns the array of entries that this DataSet represents.
*
* @return
*/
public List<T> getEntries() {
return mEntries;
}
/**
* This method is deprecated.
* Use setEntries(...) instead.
*
* @param values
*/
@Deprecated
public void setValues(List<T> values) {
setEntries(values);
}
/**
* Sets the array of entries that this DataSet represents, and calls notifyDataSetChanged()
*
* @return
*/
public void setEntries(List<T> entries) {
mEntries = entries;
notifyDataSetChanged();
}
/**
* Provides an exact copy of the DataSet this method is used on.
*
* @return
*/
public abstract DataSet<T> copy();
/**
*
* @param dataSet
*/
protected void copy(DataSet dataSet) {
super.copy(dataSet);
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append(toSimpleString());
for (int i = 0; i < mEntries.size(); i++) {
buffer.append(mEntries.get(i).toString() + " ");
}
return buffer.toString();
}
/**
* Returns a simple string representation of the DataSet with the type and
* the number of Entries.
*
* @return
*/
public String toSimpleString() {
StringBuffer buffer = new StringBuffer();
buffer.append("DataSet, label: " + (getLabel() == null ? "" : getLabel()) + ", entries: " + mEntries.size() +
"\n");
return buffer.toString();
}
@Override
public float getYMin() {
return mYMin;
}
@Override
public float getYMax() {
return mYMax;
}
@Override
public float getXMin() {
return mXMin;
}
@Override
public float getXMax() {
return mXMax;
}
@Override
public void addEntryOrdered(T e) {
if (e == null)
return;
if (mEntries == null) {
mEntries = new ArrayList<T>();
}
calcMinMax(e);
if (mEntries.size() > 0 && mEntries.get(mEntries.size() - 1).getX() > e.getX()) {
int closestIndex = getEntryIndex(e.getX(), e.getY(), Rounding.UP);
mEntries.add(closestIndex, e);
} else {
mEntries.add(e);
}
}
@Override
public void clear() {
mEntries.clear();
notifyDataSetChanged();
}
@Override
public boolean addEntry(T e) {
if (e == null)
return false;
List<T> values = getEntries();
if (values == null) {
values = new ArrayList<>();
}
calcMinMax(e);
// add the entry
return values.add(e);
}
@Override
public boolean removeEntry(T e) {
if (e == null)
return false;
if (mEntries == null)
return false;
// remove the entry
boolean removed = mEntries.remove(e);
if (removed) {
calcMinMax();
}
return removed;
}
@Override
public int getEntryIndex(Entry e) {
return mEntries.indexOf(e);
}
@Override
public T getEntryForXValue(float xValue, float closestToY, Rounding rounding) {
int index = getEntryIndex(xValue, closestToY, rounding);
if (index > -1)
return mEntries.get(index);
return null;
}
@Override
public T getEntryForXValue(float xValue, float closestToY) {
return getEntryForXValue(xValue, closestToY, Rounding.CLOSEST);
}
@Override
public T getEntryForIndex(int index) {
return mEntries.get(index);
}
@Override
public int getEntryIndex(float xValue, float closestToY, Rounding rounding) {
if (mEntries == null || mEntries.isEmpty())
return -1;
int low = 0;
int high = mEntries.size() - 1;
int closest = high;
while (low < high) {
int m = (low + high) / 2;
final float d1 = mEntries.get(m).getX() - xValue,
d2 = mEntries.get(m + 1).getX() - xValue,
ad1 = Math.abs(d1), ad2 = Math.abs(d2);
if (ad2 < ad1) {
// [m + 1] is closer to xValue
// Search in an higher place
low = m + 1;
} else if (ad1 < ad2) {
// [m] is closer to xValue
// Search in a lower place
high = m;
} else {
// We have multiple sequential x-value with same distance
if (d1 >= 0.0) {
// Search in a lower place
high = m;
} else if (d1 < 0.0) {
// Search in an higher place
low = m + 1;
}
}
closest = high;
}
if (closest != -1) {
float closestXValue = mEntries.get(closest).getX();
if (rounding == Rounding.UP) {
// If rounding up, and found x-value is lower than specified x, and we can go upper...
if (closestXValue < xValue && closest < mEntries.size() - 1) {
++closest;
}
} else if (rounding == Rounding.DOWN) {
// If rounding down, and found x-value is upper than specified x, and we can go lower...
if (closestXValue > xValue && closest > 0) {
--closest;
}
}
// Search by closest to y-value
if (!Float.isNaN(closestToY)) {
while (closest > 0 && mEntries.get(closest - 1).getX() == closestXValue)
closest -= 1;
float closestYValue = mEntries.get(closest).getY();
int closestYIndex = closest;
while (true) {
closest += 1;
if (closest >= mEntries.size())
break;
final Entry value = mEntries.get(closest);
if (value.getX() != closestXValue)
break;
if (Math.abs(value.getY() - closestToY) <= Math.abs(closestYValue - closestToY)) {
closestYValue = closestToY;
closestYIndex = closest;
}
}
closest = closestYIndex;
}
}
return closest;
}
@Override
public List<T> getEntriesForXValue(float xValue) {
List<T> entries = new ArrayList<T>();
int low = 0;
int high = mEntries.size() - 1;
while (low <= high) {
int m = (high + low) / 2;
T entry = mEntries.get(m);
// if we have a match
if (xValue == entry.getX()) {
while (m > 0 && mEntries.get(m - 1).getX() == xValue)
m--;
high = mEntries.size();
// loop over all "equal" entries
for (; m < high; m++) {
entry = mEntries.get(m);
if (entry.getX() == xValue) {
entries.add(entry);
} else {
break;
}
}
break;
} else {
if (xValue > entry.getX())
low = m + 1;
else
high = m - 1;
}
}
return entries;
}
/**
* Determines how to round DataSet index values for
* {@link DataSet#getEntryIndex(float, float, Rounding)} DataSet.getEntryIndex()}
* when an exact x-index is not found.
*/
public enum Rounding {
UP,
DOWN,
CLOSEST,
}
}

View File

@@ -0,0 +1,173 @@
package com.github.mikephil.charting.data;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.ParcelFormatException;
import android.os.Parcelable;
import com.github.mikephil.charting.utils.Utils;
/**
* Class representing one entry in the chart. Might contain multiple values.
* Might only contain a single value depending on the used constructor.
*
* @author Philipp Jahoda
*/
public class Entry extends BaseEntry implements Parcelable {
/** the x value */
private float x = 0f;
public Entry() {
}
/**
* A Entry represents one single entry in the chart.
*
* @param x the x value
* @param y the y value (the actual value of the entry)
*/
public Entry(float x, float y) {
super(y);
this.x = x;
}
/**
* A Entry represents one single entry in the chart.
*
* @param x the x value
* @param y the y value (the actual value of the entry)
* @param data Spot for additional data this Entry represents.
*/
public Entry(float x, float y, Object data) {
super(y, data);
this.x = x;
}
/**
* A Entry represents one single entry in the chart.
*
* @param x the x value
* @param y the y value (the actual value of the entry)
* @param icon icon image
*/
public Entry(float x, float y, Drawable icon) {
super(y, icon);
this.x = x;
}
/**
* A Entry represents one single entry in the chart.
*
* @param x the x value
* @param y the y value (the actual value of the entry)
* @param icon icon image
* @param data Spot for additional data this Entry represents.
*/
public Entry(float x, float y, Drawable icon, Object data) {
super(y, icon, data);
this.x = x;
}
/**
* Returns the x-value of this Entry object.
*
* @return
*/
public float getX() {
return x;
}
/**
* Sets the x-value of this Entry object.
*
* @param x
*/
public void setX(float x) {
this.x = x;
}
/**
* returns an exact copy of the entry
*
* @return
*/
public Entry copy() {
Entry e = new Entry(x, getY(), getData());
return e;
}
/**
* Compares value, xIndex and data of the entries. Returns true if entries
* are equal in those points, false if not. Does not check by hash-code like
* it's done by the "equals" method.
*
* @param e
* @return
*/
public boolean equalTo(Entry e) {
if (e == null)
return false;
if (e.getData() != this.getData())
return false;
if (Math.abs(e.x - this.x) > Utils.FLOAT_EPSILON)
return false;
if (Math.abs(e.getY() - this.getY()) > Utils.FLOAT_EPSILON)
return false;
return true;
}
/**
* returns a string representation of the entry containing x-index and value
*/
@Override
public String toString() {
return "Entry, x: " + x + " y: " + getY();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeFloat(this.x);
dest.writeFloat(this.getY());
if (getData() != null) {
if (getData() instanceof Parcelable) {
dest.writeInt(1);
dest.writeParcelable((Parcelable) this.getData(), flags);
} else {
throw new ParcelFormatException("Cannot parcel an Entry with non-parcelable data");
}
} else {
dest.writeInt(0);
}
}
protected Entry(Parcel in) {
this.x = in.readFloat();
this.setY(in.readFloat());
if (in.readInt() == 1) {
this.setData(in.readParcelable(Object.class.getClassLoader()));
}
}
public static final Parcelable.Creator<Entry> CREATOR = new Parcelable.Creator<Entry>() {
public Entry createFromParcel(Parcel source) {
return new Entry(source);
}
public Entry[] newArray(int size) {
return new Entry[size];
}
};
}

View File

@@ -0,0 +1,27 @@
package com.github.mikephil.charting.data;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
import java.util.ArrayList;
import java.util.List;
/**
* Data object that encapsulates all data associated with a LineChart.
*
* @author Philipp Jahoda
*/
public class LineData extends BarLineScatterCandleBubbleData<ILineDataSet> {
public LineData() {
super();
}
public LineData(ILineDataSet... dataSets) {
super(dataSets);
}
public LineData(List<ILineDataSet> dataSets) {
super(dataSets);
}
}

View File

@@ -0,0 +1,417 @@
package com.github.mikephil.charting.data;
import android.content.Context;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.util.Log;
import com.github.mikephil.charting.formatter.DefaultFillFormatter;
import com.github.mikephil.charting.formatter.IFillFormatter;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
import com.github.mikephil.charting.utils.ColorTemplate;
import com.github.mikephil.charting.utils.Utils;
import java.util.ArrayList;
import java.util.List;
public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet {
/**
* Drawing mode for this line dataset
**/
private LineDataSet.Mode mMode = Mode.LINEAR;
/**
* List representing all colors that are used for the circles
*/
private List<Integer> mCircleColors = null;
/**
* the color of the inner circles
*/
private int mCircleHoleColor = Color.WHITE;
/**
* the radius of the circle-shaped value indicators
*/
private float mCircleRadius = 8f;
/**
* the hole radius of the circle-shaped value indicators
*/
private float mCircleHoleRadius = 4f;
/**
* sets the intensity of the cubic lines
*/
private float mCubicIntensity = 0.2f;
/**
* the path effect of this DataSet that makes dashed lines possible
*/
private DashPathEffect mDashPathEffect = null;
/**
* formatter for customizing the position of the fill-line
*/
private IFillFormatter mFillFormatter = new DefaultFillFormatter();
/**
* if true, drawing circles is enabled
*/
private boolean mDrawCircles = true;
private boolean mDrawCircleHole = true;
public LineDataSet(List<Entry> yVals, String label) {
super(yVals, label);
// mCircleRadius = Utils.convertDpToPixel(4f);
// mLineWidth = Utils.convertDpToPixel(1f);
if (mCircleColors == null) {
mCircleColors = new ArrayList<Integer>();
}
mCircleColors.clear();
// default colors
// mColors.add(Color.rgb(192, 255, 140));
// mColors.add(Color.rgb(255, 247, 140));
mCircleColors.add(Color.rgb(140, 234, 255));
}
@Override
public DataSet<Entry> copy() {
List<Entry> entries = new ArrayList<Entry>();
for (int i = 0; i < mEntries.size(); i++) {
entries.add(mEntries.get(i).copy());
}
LineDataSet copied = new LineDataSet(entries, getLabel());
copy(copied);
return copied;
}
protected void copy(LineDataSet lineDataSet) {
super.copy(lineDataSet);
lineDataSet.mCircleColors = mCircleColors;
lineDataSet.mCircleHoleColor = mCircleHoleColor;
lineDataSet.mCircleHoleRadius = mCircleHoleRadius;
lineDataSet.mCircleRadius = mCircleRadius;
lineDataSet.mCubicIntensity = mCubicIntensity;
lineDataSet.mDashPathEffect = mDashPathEffect;
lineDataSet.mDrawCircleHole = mDrawCircleHole;
lineDataSet.mDrawCircles = mDrawCircleHole;
lineDataSet.mFillFormatter = mFillFormatter;
lineDataSet.mMode = mMode;
}
/**
* Returns the drawing mode for this line dataset
*
* @return
*/
@Override
public LineDataSet.Mode getMode() {
return mMode;
}
/**
* Returns the drawing mode for this LineDataSet
*
* @return
*/
public void setMode(LineDataSet.Mode mode) {
mMode = mode;
}
/**
* Sets the intensity for cubic lines (if enabled). Max = 1f = very cubic,
* Min = 0.05f = low cubic effect, Default: 0.2f
*
* @param intensity
*/
public void setCubicIntensity(float intensity) {
if (intensity > 1f)
intensity = 1f;
if (intensity < 0.05f)
intensity = 0.05f;
mCubicIntensity = intensity;
}
@Override
public float getCubicIntensity() {
return mCubicIntensity;
}
/**
* Sets the radius of the drawn circles.
* Default radius = 4f, Min = 1f
*
* @param radius
*/
public void setCircleRadius(float radius) {
if (radius >= 1f) {
mCircleRadius = Utils.convertDpToPixel(radius);
} else {
Log.e("LineDataSet", "Circle radius cannot be < 1");
}
}
@Override
public float getCircleRadius() {
return mCircleRadius;
}
/**
* Sets the hole radius of the drawn circles.
* Default radius = 2f, Min = 0.5f
*
* @param holeRadius
*/
public void setCircleHoleRadius(float holeRadius) {
if (holeRadius >= 0.5f) {
mCircleHoleRadius = Utils.convertDpToPixel(holeRadius);
} else {
Log.e("LineDataSet", "Circle radius cannot be < 0.5");
}
}
@Override
public float getCircleHoleRadius() {
return mCircleHoleRadius;
}
/**
* sets the size (radius) of the circle shpaed value indicators,
* default size = 4f
* <p/>
* This method is deprecated because of unclarity. Use setCircleRadius instead.
*
* @param size
*/
@Deprecated
public void setCircleSize(float size) {
setCircleRadius(size);
}
/**
* This function is deprecated because of unclarity. Use getCircleRadius instead.
*/
@Deprecated
public float getCircleSize() {
return getCircleRadius();
}
/**
* Enables the line to be drawn in dashed mode, e.g. like this
* "- - - - - -". THIS ONLY WORKS IF HARDWARE-ACCELERATION IS TURNED OFF.
* Keep in mind that hardware acceleration boosts performance.
*
* @param lineLength the length of the line pieces
* @param spaceLength the length of space in between the pieces
* @param phase offset, in degrees (normally, use 0)
*/
public void enableDashedLine(float lineLength, float spaceLength, float phase) {
mDashPathEffect = new DashPathEffect(new float[]{
lineLength, spaceLength
}, phase);
}
/**
* Disables the line to be drawn in dashed mode.
*/
public void disableDashedLine() {
mDashPathEffect = null;
}
@Override
public boolean isDashedLineEnabled() {
return mDashPathEffect == null ? false : true;
}
@Override
public DashPathEffect getDashPathEffect() {
return mDashPathEffect;
}
/**
* set this to true to enable the drawing of circle indicators for this
* DataSet, default true
*
* @param enabled
*/
public void setDrawCircles(boolean enabled) {
this.mDrawCircles = enabled;
}
@Override
public boolean isDrawCirclesEnabled() {
return mDrawCircles;
}
@Deprecated
@Override
public boolean isDrawCubicEnabled() {
return mMode == Mode.CUBIC_BEZIER;
}
@Deprecated
@Override
public boolean isDrawSteppedEnabled() {
return mMode == Mode.STEPPED;
}
/** ALL CODE BELOW RELATED TO CIRCLE-COLORS */
/**
* returns all colors specified for the circles
*
* @return
*/
public List<Integer> getCircleColors() {
return mCircleColors;
}
@Override
public int getCircleColor(int index) {
return mCircleColors.get(index);
}
@Override
public int getCircleColorCount() {
return mCircleColors.size();
}
/**
* Sets the colors that should be used for the circles of this DataSet.
* Colors are reused as soon as the number of Entries the DataSet represents
* is higher than the size of the colors array. Make sure that the colors
* are already prepared (by calling getResources().getColor(...)) before
* adding them to the DataSet.
*
* @param colors
*/
public void setCircleColors(List<Integer> colors) {
mCircleColors = colors;
}
/**
* Sets the colors that should be used for the circles of this DataSet.
* Colors are reused as soon as the number of Entries the DataSet represents
* is higher than the size of the colors array. Make sure that the colors
* are already prepared (by calling getResources().getColor(...)) before
* adding them to the DataSet.
*
* @param colors
*/
public void setCircleColors(int... colors) {
this.mCircleColors = ColorTemplate.createColors(colors);
}
/**
* ets the colors that should be used for the circles of this DataSet.
* Colors are reused as soon as the number of Entries the DataSet represents
* is higher than the size of the colors array. You can use
* "new String[] { R.color.red, R.color.green, ... }" to provide colors for
* this method. Internally, the colors are resolved using
* getResources().getColor(...)
*
* @param colors
*/
public void setCircleColors(int[] colors, Context c) {
List<Integer> clrs = mCircleColors;
if (clrs == null) {
clrs = new ArrayList<>();
}
clrs.clear();
for (int color : colors) {
clrs.add(c.getResources().getColor(color));
}
mCircleColors = clrs;
}
/**
* Sets the one and ONLY color that should be used for this DataSet.
* Internally, this recreates the colors array and adds the specified color.
*
* @param color
*/
public void setCircleColor(int color) {
resetCircleColors();
mCircleColors.add(color);
}
/**
* resets the circle-colors array and creates a new one
*/
public void resetCircleColors() {
if (mCircleColors == null) {
mCircleColors = new ArrayList<Integer>();
}
mCircleColors.clear();
}
/**
* Sets the color of the inner circle of the line-circles.
*
* @param color
*/
public void setCircleHoleColor(int color) {
mCircleHoleColor = color;
}
@Override
public int getCircleHoleColor() {
return mCircleHoleColor;
}
/**
* Set this to true to allow drawing a hole in each data circle.
*
* @param enabled
*/
public void setDrawCircleHole(boolean enabled) {
mDrawCircleHole = enabled;
}
@Override
public boolean isDrawCircleHoleEnabled() {
return mDrawCircleHole;
}
/**
* Sets a custom IFillFormatter to the chart that handles the position of the
* filled-line for each DataSet. Set this to null to use the default logic.
*
* @param formatter
*/
public void setFillFormatter(IFillFormatter formatter) {
if (formatter == null)
mFillFormatter = new DefaultFillFormatter();
else
mFillFormatter = formatter;
}
@Override
public IFillFormatter getFillFormatter() {
return mFillFormatter;
}
public enum Mode {
LINEAR,
STEPPED,
CUBIC_BEZIER,
HORIZONTAL_BEZIER
}
}

View File

@@ -0,0 +1,135 @@
package com.github.mikephil.charting.data;
import android.annotation.TargetApi;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import com.github.mikephil.charting.interfaces.datasets.ILineRadarDataSet;
import com.github.mikephil.charting.utils.Utils;
import java.util.List;
/**
* Base dataset for line and radar DataSets.
*
* @author Philipp Jahoda
*/
public abstract class LineRadarDataSet<T extends Entry> extends LineScatterCandleRadarDataSet<T> implements ILineRadarDataSet<T> {
// TODO: Move to using `Fill` class
/**
* the color that is used for filling the line surface
*/
private int mFillColor = Color.rgb(140, 234, 255);
/**
* the drawable to be used for filling the line surface
*/
protected Drawable mFillDrawable;
/**
* transparency used for filling line surface
*/
private int mFillAlpha = 85;
/**
* the width of the drawn data lines
*/
private float mLineWidth = 2.5f;
/**
* if true, the data will also be drawn filled
*/
private boolean mDrawFilled = false;
public LineRadarDataSet(List<T> yVals, String label) {
super(yVals, label);
}
@Override
public int getFillColor() {
return mFillColor;
}
/**
* Sets the color that is used for filling the area below the line.
* Resets an eventually set "fillDrawable".
*
* @param color
*/
public void setFillColor(int color) {
mFillColor = color;
mFillDrawable = null;
}
@Override
public Drawable getFillDrawable() {
return mFillDrawable;
}
/**
* Sets the drawable to be used to fill the area below the line.
*
* @param drawable
*/
@TargetApi(18)
public void setFillDrawable(Drawable drawable) {
this.mFillDrawable = drawable;
}
@Override
public int getFillAlpha() {
return mFillAlpha;
}
/**
* sets the alpha value (transparency) that is used for filling the line
* surface (0-255), default: 85
*
* @param alpha
*/
public void setFillAlpha(int alpha) {
mFillAlpha = alpha;
}
/**
* set the line width of the chart (min = 0.2f, max = 10f); default 1f NOTE:
* thinner line == better performance, thicker line == worse performance
*
* @param width
*/
public void setLineWidth(float width) {
if (width < 0.0f)
width = 0.0f;
if (width > 10.0f)
width = 10.0f;
mLineWidth = Utils.convertDpToPixel(width);
}
@Override
public float getLineWidth() {
return mLineWidth;
}
@Override
public void setDrawFilled(boolean filled) {
mDrawFilled = filled;
}
@Override
public boolean isDrawFilledEnabled() {
return mDrawFilled;
}
protected void copy(LineRadarDataSet lineRadarDataSet) {
super.copy(lineRadarDataSet);
lineRadarDataSet.mDrawFilled = mDrawFilled;
lineRadarDataSet.mFillAlpha = mFillAlpha;
lineRadarDataSet.mFillColor = mFillColor;
lineRadarDataSet.mFillDrawable = mFillDrawable;
lineRadarDataSet.mLineWidth = mLineWidth;
}
}

View File

@@ -0,0 +1,120 @@
package com.github.mikephil.charting.data;
import android.graphics.DashPathEffect;
import com.github.mikephil.charting.interfaces.datasets.ILineScatterCandleRadarDataSet;
import com.github.mikephil.charting.utils.Utils;
import java.util.List;
/**
* Created by Philipp Jahoda on 11/07/15.
*/
public abstract class LineScatterCandleRadarDataSet<T extends Entry> extends BarLineScatterCandleBubbleDataSet<T> implements ILineScatterCandleRadarDataSet<T> {
protected boolean mDrawVerticalHighlightIndicator = true;
protected boolean mDrawHorizontalHighlightIndicator = true;
/** the width of the highlight indicator lines */
protected float mHighlightLineWidth = 0.5f;
/** the path effect for dashed highlight-lines */
protected DashPathEffect mHighlightDashPathEffect = null;
public LineScatterCandleRadarDataSet(List<T> yVals, String label) {
super(yVals, label);
mHighlightLineWidth = Utils.convertDpToPixel(0.5f);
}
/**
* Enables / disables the horizontal highlight-indicator. If disabled, the indicator is not drawn.
* @param enabled
*/
public void setDrawHorizontalHighlightIndicator(boolean enabled) {
this.mDrawHorizontalHighlightIndicator = enabled;
}
/**
* Enables / disables the vertical highlight-indicator. If disabled, the indicator is not drawn.
* @param enabled
*/
public void setDrawVerticalHighlightIndicator(boolean enabled) {
this.mDrawVerticalHighlightIndicator = enabled;
}
/**
* Enables / disables both vertical and horizontal highlight-indicators.
* @param enabled
*/
public void setDrawHighlightIndicators(boolean enabled) {
setDrawVerticalHighlightIndicator(enabled);
setDrawHorizontalHighlightIndicator(enabled);
}
@Override
public boolean isVerticalHighlightIndicatorEnabled() {
return mDrawVerticalHighlightIndicator;
}
@Override
public boolean isHorizontalHighlightIndicatorEnabled() {
return mDrawHorizontalHighlightIndicator;
}
/**
* Sets the width of the highlight line in dp.
* @param width
*/
public void setHighlightLineWidth(float width) {
mHighlightLineWidth = Utils.convertDpToPixel(width);
}
@Override
public float getHighlightLineWidth() {
return mHighlightLineWidth;
}
/**
* Enables the highlight-line to be drawn in dashed mode, e.g. like this "- - - - - -"
*
* @param lineLength the length of the line pieces
* @param spaceLength the length of space inbetween the line-pieces
* @param phase offset, in degrees (normally, use 0)
*/
public void enableDashedHighlightLine(float lineLength, float spaceLength, float phase) {
mHighlightDashPathEffect = new DashPathEffect(new float[] {
lineLength, spaceLength
}, phase);
}
/**
* Disables the highlight-line to be drawn in dashed mode.
*/
public void disableDashedHighlightLine() {
mHighlightDashPathEffect = null;
}
/**
* Returns true if the dashed-line effect is enabled for highlight lines, false if not.
* Default: disabled
*
* @return
*/
public boolean isDashedHighlightLineEnabled() {
return mHighlightDashPathEffect == null ? false : true;
}
@Override
public DashPathEffect getDashPathEffectHighlight() {
return mHighlightDashPathEffect;
}
protected void copy(LineScatterCandleRadarDataSet lineScatterCandleRadarDataSet) {
super.copy(lineScatterCandleRadarDataSet);
lineScatterCandleRadarDataSet.mDrawHorizontalHighlightIndicator = mDrawHorizontalHighlightIndicator;
lineScatterCandleRadarDataSet.mDrawVerticalHighlightIndicator = mDrawVerticalHighlightIndicator;
lineScatterCandleRadarDataSet.mHighlightLineWidth = mHighlightLineWidth;
lineScatterCandleRadarDataSet.mHighlightDashPathEffect = mHighlightDashPathEffect;
}
}

View File

@@ -0,0 +1,100 @@
package com.github.mikephil.charting.data;
import android.util.Log;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.interfaces.datasets.IPieDataSet;
import java.util.ArrayList;
import java.util.List;
/**
* A PieData object can only represent one DataSet. Unlike all other charts, the
* legend labels of the PieChart are created from the x-values array, and not
* from the DataSet labels. Each PieData object can only represent one
* PieDataSet (multiple PieDataSets inside a single PieChart are not possible).
*
* @author Philipp Jahoda
*/
public class PieData extends ChartData<IPieDataSet> {
public PieData() {
super();
}
public PieData(IPieDataSet dataSet) {
super(dataSet);
}
/**
* Sets the PieDataSet this data object should represent.
*
* @param dataSet
*/
public void setDataSet(IPieDataSet dataSet) {
mDataSets.clear();
mDataSets.add(dataSet);
notifyDataChanged();
}
/**
* Returns the DataSet this PieData object represents. A PieData object can
* only contain one DataSet.
*
* @return
*/
public IPieDataSet getDataSet() {
return mDataSets.get(0);
}
@Override
public List<IPieDataSet> getDataSets() {
List<IPieDataSet> dataSets = super.getDataSets();
if (dataSets.size() < 1) {
Log.e("MPAndroidChart",
"Found multiple data sets while pie chart only allows one");
}
return dataSets;
}
/**
* The PieData object can only have one DataSet. Use getDataSet() method instead.
*
* @param index
* @return
*/
@Override
public IPieDataSet getDataSetByIndex(int index) {
return index == 0 ? getDataSet() : null;
}
@Override
public IPieDataSet getDataSetByLabel(String label, boolean ignorecase) {
return ignorecase ? label.equalsIgnoreCase(mDataSets.get(0).getLabel()) ? mDataSets.get(0)
: null : label.equals(mDataSets.get(0).getLabel()) ? mDataSets.get(0) : null;
}
@Override
public Entry getEntryForHighlight(Highlight highlight) {
return getDataSet().getEntryForIndex((int) highlight.getX());
}
/**
* Returns the sum of all values in this PieData object.
*
* @return
*/
public float getYValueSum() {
float sum = 0;
for (int i = 0; i < getDataSet().getEntryCount(); i++)
sum += getDataSet().getEntryForIndex(i).getY();
return sum;
}
}

View File

@@ -0,0 +1,261 @@
package com.github.mikephil.charting.data;
import com.github.mikephil.charting.interfaces.datasets.IPieDataSet;
import com.github.mikephil.charting.utils.Utils;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
public class PieDataSet extends DataSet<PieEntry> implements IPieDataSet {
/**
* the space in pixels between the chart-slices, default 0f
*/
private float mSliceSpace = 0f;
private boolean mAutomaticallyDisableSliceSpacing;
/**
* indicates the selection distance of a pie slice
*/
private float mShift = 18f;
private ValuePosition mXValuePosition = ValuePosition.INSIDE_SLICE;
private ValuePosition mYValuePosition = ValuePosition.INSIDE_SLICE;
private int mValueLineColor = 0xff000000;
private boolean mUseValueColorForLine = false;
private float mValueLineWidth = 1.0f;
private float mValueLinePart1OffsetPercentage = 75.f;
private float mValueLinePart1Length = 0.3f;
private float mValueLinePart2Length = 0.4f;
private boolean mValueLineVariableLength = true;
private Integer mHighlightColor = null;
public PieDataSet(List<PieEntry> yVals, String label) {
super(yVals, label);
// mShift = Utils.convertDpToPixel(12f);
}
@Override
public DataSet<PieEntry> copy() {
List<PieEntry> entries = new ArrayList<>();
for (int i = 0; i < mEntries.size(); i++) {
entries.add(mEntries.get(i).copy());
}
PieDataSet copied = new PieDataSet(entries, getLabel());
copy(copied);
return copied;
}
protected void copy(PieDataSet pieDataSet) {
super.copy(pieDataSet);
}
@Override
protected void calcMinMax(PieEntry e) {
if (e == null)
return;
calcMinMaxY(e);
}
/**
* Sets the space that is left out between the piechart-slices in dp.
* Default: 0 --> no space, maximum 20f
*
* @param spaceDp
*/
public void setSliceSpace(float spaceDp) {
if (spaceDp > 20)
spaceDp = 20f;
if (spaceDp < 0)
spaceDp = 0f;
mSliceSpace = Utils.convertDpToPixel(spaceDp);
}
@Override
public float getSliceSpace() {
return mSliceSpace;
}
/**
* When enabled, slice spacing will be 0.0 when the smallest value is going to be
* smaller than the slice spacing itself.
*
* @param autoDisable
*/
public void setAutomaticallyDisableSliceSpacing(boolean autoDisable) {
mAutomaticallyDisableSliceSpacing = autoDisable;
}
/**
* When enabled, slice spacing will be 0.0 when the smallest value is going to be
* smaller than the slice spacing itself.
*
* @return
*/
@Override
public boolean isAutomaticallyDisableSliceSpacingEnabled() {
return mAutomaticallyDisableSliceSpacing;
}
/**
* sets the distance the highlighted piechart-slice of this DataSet is
* "shifted" away from the center of the chart, default 12f
*
* @param shift
*/
public void setSelectionShift(float shift) {
mShift = Utils.convertDpToPixel(shift);
}
@Override
public float getSelectionShift() {
return mShift;
}
@Override
public ValuePosition getXValuePosition() {
return mXValuePosition;
}
public void setXValuePosition(ValuePosition xValuePosition) {
this.mXValuePosition = xValuePosition;
}
@Override
public ValuePosition getYValuePosition() {
return mYValuePosition;
}
public void setYValuePosition(ValuePosition yValuePosition) {
this.mYValuePosition = yValuePosition;
}
/**
* This method is deprecated.
* Use isUseValueColorForLineEnabled() instead.
*/
@Deprecated
public boolean isUsingSliceColorAsValueLineColor() {
return isUseValueColorForLineEnabled();
}
/**
* This method is deprecated.
* Use setUseValueColorForLine(...) instead.
*
* @param enabled
*/
@Deprecated
public void setUsingSliceColorAsValueLineColor(boolean enabled) {
setUseValueColorForLine(enabled);
}
/**
* When valuePosition is OutsideSlice, indicates line color
*/
@Override
public int getValueLineColor() {
return mValueLineColor;
}
public void setValueLineColor(int valueLineColor) {
this.mValueLineColor = valueLineColor;
}
@Override
public boolean isUseValueColorForLineEnabled()
{
return mUseValueColorForLine;
}
public void setUseValueColorForLine(boolean enabled)
{
mUseValueColorForLine = enabled;
}
/**
* When valuePosition is OutsideSlice, indicates line width
*/
@Override
public float getValueLineWidth() {
return mValueLineWidth;
}
public void setValueLineWidth(float valueLineWidth) {
this.mValueLineWidth = valueLineWidth;
}
/**
* When valuePosition is OutsideSlice, indicates offset as percentage out of the slice size
*/
@Override
public float getValueLinePart1OffsetPercentage() {
return mValueLinePart1OffsetPercentage;
}
public void setValueLinePart1OffsetPercentage(float valueLinePart1OffsetPercentage) {
this.mValueLinePart1OffsetPercentage = valueLinePart1OffsetPercentage;
}
/**
* When valuePosition is OutsideSlice, indicates length of first half of the line
*/
@Override
public float getValueLinePart1Length() {
return mValueLinePart1Length;
}
public void setValueLinePart1Length(float valueLinePart1Length) {
this.mValueLinePart1Length = valueLinePart1Length;
}
/**
* When valuePosition is OutsideSlice, indicates length of second half of the line
*/
@Override
public float getValueLinePart2Length() {
return mValueLinePart2Length;
}
public void setValueLinePart2Length(float valueLinePart2Length) {
this.mValueLinePart2Length = valueLinePart2Length;
}
/**
* When valuePosition is OutsideSlice, this allows variable line length
*/
@Override
public boolean isValueLineVariableLength() {
return mValueLineVariableLength;
}
public void setValueLineVariableLength(boolean valueLineVariableLength) {
this.mValueLineVariableLength = valueLineVariableLength;
}
/** Gets the color for the highlighted sector */
@Override
@Nullable
public Integer getHighlightColor()
{
return mHighlightColor;
}
/** Sets the color for the highlighted sector (null for using entry color) */
public void setHighlightColor(@Nullable Integer color)
{
this.mHighlightColor = color;
}
public enum ValuePosition {
INSIDE_SLICE,
OUTSIDE_SLICE
}
}

View File

@@ -0,0 +1,86 @@
package com.github.mikephil.charting.data;
import android.annotation.SuppressLint;
import android.graphics.drawable.Drawable;
import android.util.Log;
/**
* @author Philipp Jahoda
*/
@SuppressLint("ParcelCreator")
public class PieEntry extends Entry {
private String label;
public PieEntry(float value) {
super(0f, value);
}
public PieEntry(float value, Object data) {
super(0f, value, data);
}
public PieEntry(float value, Drawable icon) {
super(0f, value, icon);
}
public PieEntry(float value, Drawable icon, Object data) {
super(0f, value, icon, data);
}
public PieEntry(float value, String label) {
super(0f, value);
this.label = label;
}
public PieEntry(float value, String label, Object data) {
super(0f, value, data);
this.label = label;
}
public PieEntry(float value, String label, Drawable icon) {
super(0f, value, icon);
this.label = label;
}
public PieEntry(float value, String label, Drawable icon, Object data) {
super(0f, value, icon, data);
this.label = label;
}
/**
* This is the same as getY(). Returns the value of the PieEntry.
*
* @return
*/
public float getValue() {
return getY();
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
@Deprecated
@Override
public void setX(float x) {
super.setX(x);
Log.i("DEPRECATED", "Pie entries do not have x values");
}
@Deprecated
@Override
public float getX() {
Log.i("DEPRECATED", "Pie entries do not have x values");
return super.getX();
}
public PieEntry copy() {
PieEntry e = new PieEntry(getY(), label, getData());
return e;
}
}

View File

@@ -0,0 +1,58 @@
package com.github.mikephil.charting.data;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Data container for the RadarChart.
*
* @author Philipp Jahoda
*/
public class RadarData extends ChartData<IRadarDataSet> {
private List<String> mLabels;
public RadarData() {
super();
}
public RadarData(List<IRadarDataSet> dataSets) {
super(dataSets);
}
public RadarData(IRadarDataSet... dataSets) {
super(dataSets);
}
/**
* Sets the labels that should be drawn around the RadarChart at the end of each web line.
*
* @param labels
*/
public void setLabels(List<String> labels) {
this.mLabels = labels;
}
/**
* Sets the labels that should be drawn around the RadarChart at the end of each web line.
*
* @param labels
*/
public void setLabels(String... labels) {
this.mLabels = Arrays.asList(labels);
}
public List<String> getLabels() {
return mLabels;
}
@Override
public Entry getEntryForHighlight(Highlight highlight) {
return getDataSetByIndex(highlight.getDataSetIndex()).getEntryForIndex((int) highlight.getX());
}
}

View File

@@ -0,0 +1,122 @@
package com.github.mikephil.charting.data;
import android.graphics.Color;
import com.github.mikephil.charting.interfaces.datasets.IRadarDataSet;
import com.github.mikephil.charting.utils.ColorTemplate;
import java.util.ArrayList;
import java.util.List;
public class RadarDataSet extends LineRadarDataSet<RadarEntry> implements IRadarDataSet {
/// flag indicating whether highlight circle should be drawn or not
protected boolean mDrawHighlightCircleEnabled = false;
protected int mHighlightCircleFillColor = Color.WHITE;
/// The stroke color for highlight circle.
/// If Utils.COLOR_NONE, the color of the dataset is taken.
protected int mHighlightCircleStrokeColor = ColorTemplate.COLOR_NONE;
protected int mHighlightCircleStrokeAlpha = (int) (0.3 * 255);
protected float mHighlightCircleInnerRadius = 3.0f;
protected float mHighlightCircleOuterRadius = 4.0f;
protected float mHighlightCircleStrokeWidth = 2.0f;
public RadarDataSet(List<RadarEntry> yVals, String label) {
super(yVals, label);
}
/// Returns true if highlight circle should be drawn, false if not
@Override
public boolean isDrawHighlightCircleEnabled() {
return mDrawHighlightCircleEnabled;
}
/// Sets whether highlight circle should be drawn or not
@Override
public void setDrawHighlightCircleEnabled(boolean enabled) {
mDrawHighlightCircleEnabled = enabled;
}
@Override
public int getHighlightCircleFillColor() {
return mHighlightCircleFillColor;
}
public void setHighlightCircleFillColor(int color) {
mHighlightCircleFillColor = color;
}
/// Returns the stroke color for highlight circle.
/// If Utils.COLOR_NONE, the color of the dataset is taken.
@Override
public int getHighlightCircleStrokeColor() {
return mHighlightCircleStrokeColor;
}
/// Sets the stroke color for highlight circle.
/// Set to Utils.COLOR_NONE in order to use the color of the dataset;
public void setHighlightCircleStrokeColor(int color) {
mHighlightCircleStrokeColor = color;
}
@Override
public int getHighlightCircleStrokeAlpha() {
return mHighlightCircleStrokeAlpha;
}
public void setHighlightCircleStrokeAlpha(int alpha) {
mHighlightCircleStrokeAlpha = alpha;
}
@Override
public float getHighlightCircleInnerRadius() {
return mHighlightCircleInnerRadius;
}
public void setHighlightCircleInnerRadius(float radius) {
mHighlightCircleInnerRadius = radius;
}
@Override
public float getHighlightCircleOuterRadius() {
return mHighlightCircleOuterRadius;
}
public void setHighlightCircleOuterRadius(float radius) {
mHighlightCircleOuterRadius = radius;
}
@Override
public float getHighlightCircleStrokeWidth() {
return mHighlightCircleStrokeWidth;
}
public void setHighlightCircleStrokeWidth(float strokeWidth) {
mHighlightCircleStrokeWidth = strokeWidth;
}
@Override
public DataSet<RadarEntry> copy() {
List<RadarEntry> entries = new ArrayList<RadarEntry>();
for (int i = 0; i < mEntries.size(); i++) {
entries.add(mEntries.get(i).copy());
}
RadarDataSet copied = new RadarDataSet(entries, getLabel());
copy(copied);
return copied;
}
protected void copy(RadarDataSet radarDataSet) {
super.copy(radarDataSet);
radarDataSet.mDrawHighlightCircleEnabled = mDrawHighlightCircleEnabled;
radarDataSet.mHighlightCircleFillColor = mHighlightCircleFillColor;
radarDataSet.mHighlightCircleInnerRadius = mHighlightCircleInnerRadius;
radarDataSet.mHighlightCircleStrokeAlpha = mHighlightCircleStrokeAlpha;
radarDataSet.mHighlightCircleStrokeColor = mHighlightCircleStrokeColor;
radarDataSet.mHighlightCircleStrokeWidth = mHighlightCircleStrokeWidth;
}
}

View File

@@ -0,0 +1,44 @@
package com.github.mikephil.charting.data;
import android.annotation.SuppressLint;
/**
* Created by philipp on 13/06/16.
*/
@SuppressLint("ParcelCreator")
public class RadarEntry extends Entry {
public RadarEntry(float value) {
super(0f, value);
}
public RadarEntry(float value, Object data) {
super(0f, value, data);
}
/**
* This is the same as getY(). Returns the value of the RadarEntry.
*
* @return
*/
public float getValue() {
return getY();
}
public RadarEntry copy() {
RadarEntry e = new RadarEntry(getY(), getData());
return e;
}
@Deprecated
@Override
public void setX(float x) {
super.setX(x);
}
@Deprecated
@Override
public float getX() {
return super.getX();
}
}

View File

@@ -0,0 +1,40 @@
package com.github.mikephil.charting.data;
import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet;
import java.util.List;
public class ScatterData extends BarLineScatterCandleBubbleData<IScatterDataSet> {
public ScatterData() {
super();
}
public ScatterData(List<IScatterDataSet> dataSets) {
super(dataSets);
}
public ScatterData(IScatterDataSet... dataSets) {
super(dataSets);
}
/**
* Returns the maximum shape-size across all DataSets.
*
* @return
*/
public float getGreatestShapeSize() {
float max = 0f;
for (IScatterDataSet set : mDataSets) {
float size = set.getScatterShapeSize();
if (size > max)
max = size;
}
return max;
}
}

View File

@@ -0,0 +1,157 @@
package com.github.mikephil.charting.data;
import com.github.mikephil.charting.charts.ScatterChart;
import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet;
import com.github.mikephil.charting.renderer.scatter.ChevronDownShapeRenderer;
import com.github.mikephil.charting.renderer.scatter.ChevronUpShapeRenderer;
import com.github.mikephil.charting.renderer.scatter.CircleShapeRenderer;
import com.github.mikephil.charting.renderer.scatter.CrossShapeRenderer;
import com.github.mikephil.charting.renderer.scatter.IShapeRenderer;
import com.github.mikephil.charting.renderer.scatter.SquareShapeRenderer;
import com.github.mikephil.charting.renderer.scatter.TriangleShapeRenderer;
import com.github.mikephil.charting.renderer.scatter.XShapeRenderer;
import com.github.mikephil.charting.utils.ColorTemplate;
import java.util.ArrayList;
import java.util.List;
public class ScatterDataSet extends LineScatterCandleRadarDataSet<Entry> implements IScatterDataSet {
/**
* the size the scattershape will have, in density pixels
*/
private float mShapeSize = 15f;
/**
* Renderer responsible for rendering this DataSet, default: square
*/
protected IShapeRenderer mShapeRenderer = new SquareShapeRenderer();
/**
* The radius of the hole in the shape (applies to Square, Circle and Triangle)
* - default: 0.0
*/
private float mScatterShapeHoleRadius = 0f;
/**
* Color for the hole in the shape.
* Setting to `ColorTemplate.COLOR_NONE` will behave as transparent.
* - default: ColorTemplate.COLOR_NONE
*/
private int mScatterShapeHoleColor = ColorTemplate.COLOR_NONE;
public ScatterDataSet(List<Entry> yVals, String label) {
super(yVals, label);
}
@Override
public DataSet<Entry> copy() {
List<Entry> entries = new ArrayList<Entry>();
for (int i = 0; i < mEntries.size(); i++) {
entries.add(mEntries.get(i).copy());
}
ScatterDataSet copied = new ScatterDataSet(entries, getLabel());
copy(copied);
return copied;
}
protected void copy(ScatterDataSet scatterDataSet) {
super.copy(scatterDataSet);
scatterDataSet.mShapeSize = mShapeSize;
scatterDataSet.mShapeRenderer = mShapeRenderer;
scatterDataSet.mScatterShapeHoleRadius = mScatterShapeHoleRadius;
scatterDataSet.mScatterShapeHoleColor = mScatterShapeHoleColor;
}
/**
* Sets the size in density pixels the drawn scattershape will have. This
* only applies for non custom shapes.
*
* @param size
*/
public void setScatterShapeSize(float size) {
mShapeSize = size;
}
@Override
public float getScatterShapeSize() {
return mShapeSize;
}
/**
* Sets the ScatterShape this DataSet should be drawn with. This will search for an available IShapeRenderer and set this
* renderer for the DataSet.
*
* @param shape
*/
public void setScatterShape(ScatterChart.ScatterShape shape) {
mShapeRenderer = getRendererForShape(shape);
}
/**
* Sets a new IShapeRenderer responsible for drawing this DataSet.
* This can also be used to set a custom IShapeRenderer aside from the default ones.
*
* @param shapeRenderer
*/
public void setShapeRenderer(IShapeRenderer shapeRenderer) {
mShapeRenderer = shapeRenderer;
}
@Override
public IShapeRenderer getShapeRenderer() {
return mShapeRenderer;
}
/**
* Sets the radius of the hole in the shape (applies to Square, Circle and Triangle)
* Set this to <= 0 to remove holes.
*
* @param holeRadius
*/
public void setScatterShapeHoleRadius(float holeRadius) {
mScatterShapeHoleRadius = holeRadius;
}
@Override
public float getScatterShapeHoleRadius() {
return mScatterShapeHoleRadius;
}
/**
* Sets the color for the hole in the shape.
*
* @param holeColor
*/
public void setScatterShapeHoleColor(int holeColor) {
mScatterShapeHoleColor = holeColor;
}
@Override
public int getScatterShapeHoleColor() {
return mScatterShapeHoleColor;
}
public static IShapeRenderer getRendererForShape(ScatterChart.ScatterShape shape) {
switch (shape) {
case SQUARE:
return new SquareShapeRenderer();
case CIRCLE:
return new CircleShapeRenderer();
case TRIANGLE:
return new TriangleShapeRenderer();
case CROSS:
return new CrossShapeRenderer();
case X:
return new XShapeRenderer();
case CHEVRON_UP:
return new ChevronUpShapeRenderer();
case CHEVRON_DOWN:
return new ChevronDownShapeRenderer();
}
return null;
}
}

View File

@@ -0,0 +1,102 @@
package com.github.mikephil.charting.data.filter;
import android.annotation.TargetApi;
import android.os.Build;
import java.util.Arrays;
/**
* Implemented according to Wiki-Pseudocode {@link}
* http://en.wikipedia.org/wiki/Ramer<65>Douglas<61>Peucker_algorithm
*
* @author Philipp Baldauf & Phliipp Jahoda
*/
public class Approximator {
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
public float[] reduceWithDouglasPeucker(float[] points, float tolerance) {
int greatestIndex = 0;
float greatestDistance = 0f;
Line line = new Line(points[0], points[1], points[points.length - 2], points[points.length - 1]);
for (int i = 2; i < points.length - 2; i += 2) {
float distance = line.distance(points[i], points[i + 1]);
if (distance > greatestDistance) {
greatestDistance = distance;
greatestIndex = i;
}
}
if (greatestDistance > tolerance) {
float[] reduced1 = reduceWithDouglasPeucker(Arrays.copyOfRange(points, 0, greatestIndex + 2), tolerance);
float[] reduced2 = reduceWithDouglasPeucker(Arrays.copyOfRange(points, greatestIndex, points.length),
tolerance);
float[] result1 = reduced1;
float[] result2 = Arrays.copyOfRange(reduced2, 2, reduced2.length);
return concat(result1, result2);
} else {
return line.getPoints();
}
}
/**
* Combine arrays.
*
* @param arrays
* @return
*/
float[] concat(float[]... arrays) {
int length = 0;
for (float[] array : arrays) {
length += array.length;
}
float[] result = new float[length];
int pos = 0;
for (float[] array : arrays) {
for (float element : array) {
result[pos] = element;
pos++;
}
}
return result;
}
private class Line {
private float[] points;
private float sxey;
private float exsy;
private float dx;
private float dy;
private float length;
public Line(float x1, float y1, float x2, float y2) {
dx = x1 - x2;
dy = y1 - y2;
sxey = x1 * y2;
exsy = x2 * y1;
length = (float) Math.sqrt(dx * dx + dy * dy);
points = new float[]{x1, y1, x2, y2};
}
public float distance(float x, float y) {
return Math.abs(dy * x - dx * y + sxey - exsy) / length;
}
public float[] getPoints() {
return points;
}
}
}

View File

@@ -0,0 +1,146 @@
package com.github.mikephil.charting.data.filter;
import java.util.ArrayList;
/**
* Implemented according to modified Douglas Peucker {@link}
* http://psimpl.sourceforge.net/douglas-peucker.html
*/
public class ApproximatorN
{
public float[] reduceWithDouglasPeucker(float[] points, float resultCount) {
int pointCount = points.length / 2;
// if a shape has 2 or less points it cannot be reduced
if (resultCount <= 2 || resultCount >= pointCount)
return points;
boolean[] keep = new boolean[pointCount];
// first and last always stay
keep[0] = true;
keep[pointCount - 1] = true;
int currentStoredPoints = 2;
ArrayList<Line> queue = new ArrayList<>();
Line line = new Line(0, pointCount - 1, points);
queue.add(line);
do {
line = queue.remove(queue.size() - 1);
// store the key
keep[line.index] = true;
// check point count tolerance
currentStoredPoints += 1;
if (currentStoredPoints == resultCount)
break;
// split the polyline at the key and recurse
Line left = new Line(line.start, line.index, points);
if (left.index > 0) {
int insertionIndex = insertionIndex(left, queue);
queue.add(insertionIndex, left);
}
Line right = new Line(line.index, line.end, points);
if (right.index > 0) {
int insertionIndex = insertionIndex(right, queue);
queue.add(insertionIndex, right);
}
} while (queue.isEmpty());
float[] reducedEntries = new float[currentStoredPoints * 2];
for (int i = 0, i2 = 0, r2 = 0; i < currentStoredPoints; i++, r2 += 2) {
if (keep[i]) {
reducedEntries[i2++] = points[r2];
reducedEntries[i2++] = points[r2 + 1];
}
}
return reducedEntries;
}
private static float distanceToLine(
float ptX, float ptY, float[]
fromLinePoint1, float[] fromLinePoint2) {
float dx = fromLinePoint2[0] - fromLinePoint1[0];
float dy = fromLinePoint2[1] - fromLinePoint1[1];
float dividend = Math.abs(
dy * ptX -
dx * ptY -
fromLinePoint1[0] * fromLinePoint2[1] +
fromLinePoint2[0] * fromLinePoint1[1]);
double divisor = Math.sqrt(dx * dx + dy * dy);
return (float)(dividend / divisor);
}
private static class Line {
int start;
int end;
float distance = 0;
int index = 0;
Line(int start, int end, float[] points) {
this.start = start;
this.end = end;
float[] startPoint = new float[]{points[start * 2], points[start * 2 + 1]};
float[] endPoint = new float[]{points[end * 2], points[end * 2 + 1]};
if (end <= start + 1) return;
for (int i = start + 1, i2 = i * 2; i < end; i++, i2 += 2) {
float distance = distanceToLine(
points[i2], points[i2 + 1],
startPoint, endPoint);
if (distance > this.distance) {
this.index = i;
this.distance = distance;
}
}
}
boolean equals(final Line rhs) {
return (start == rhs.start) && (end == rhs.end) && index == rhs.index;
}
boolean lessThan(final Line rhs) {
return distance < rhs.distance;
}
}
private static int insertionIndex(Line line, ArrayList<Line> queue) {
int min = 0;
int max = queue.size();
while (!queue.isEmpty()) {
int midIndex = min + (max - min) / 2;
Line midLine = queue.get(midIndex);
if (midLine.equals(line)) {
return midIndex;
}
else if (line.lessThan(midLine)) {
// perform search in left half
max = midIndex;
}
else {
// perform search in right half
min = midIndex + 1;
}
}
return min;
}
}

View File

@@ -0,0 +1,14 @@
package com.github.mikephil.charting.exception;
public class DrawingDataSetNotCreatedException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 1L;
public DrawingDataSetNotCreatedException() {
super("Have to create a new drawing set first. Call ChartData's createNewDrawingDataSet() method");
}
}

View File

@@ -0,0 +1,24 @@
package com.github.mikephil.charting.formatter;
import com.github.mikephil.charting.data.DataSet;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.interfaces.datasets.IDataSet;
/**
* Interface that can be used to return a customized color instead of setting
* colors via the setColor(...) method of the DataSet.
*
* @author Philipp Jahoda
*/
public interface ColorFormatter {
/**
* Returns the color to be used for the given Entry at the given index (in the entries array)
*
* @param index index in the entries array
* @param e the entry to color
* @param set the DataSet the entry belongs to
* @return
*/
int getColor(int index, Entry e, IDataSet set);
}

View File

@@ -0,0 +1,56 @@
package com.github.mikephil.charting.formatter;
import com.github.mikephil.charting.components.AxisBase;
import java.text.DecimalFormat;
/**
* Created by philipp on 02/06/16.
*/
public class DefaultAxisValueFormatter implements IAxisValueFormatter
{
/**
* decimalformat for formatting
*/
protected DecimalFormat mFormat;
/**
* the number of decimal digits this formatter uses
*/
protected int digits = 0;
/**
* Constructor that specifies to how many digits the value should be
* formatted.
*
* @param digits
*/
public DefaultAxisValueFormatter(int digits) {
this.digits = digits;
StringBuffer b = new StringBuffer();
for (int i = 0; i < digits; i++) {
if (i == 0)
b.append(".");
b.append("0");
}
mFormat = new DecimalFormat("###,###,###,##0" + b.toString());
}
@Override
public String getFormattedValue(float value, AxisBase axis) {
// avoid memory allocations here (for performance)
return mFormat.format(value);
}
/**
* Returns the number of decimal digits this formatter uses or -1, if unspecified.
*
* @return
*/
public int getDecimalDigits() {
return digits;
}
}

View File

@@ -0,0 +1,45 @@
package com.github.mikephil.charting.formatter;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
/**
* Default formatter that calculates the position of the filled line.
*
* @author Philipp Jahoda
*/
public class DefaultFillFormatter implements IFillFormatter
{
@Override
public float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider) {
float fillMin = 0f;
float chartMaxY = dataProvider.getYChartMax();
float chartMinY = dataProvider.getYChartMin();
LineData data = dataProvider.getLineData();
if (dataSet.getYMax() > 0 && dataSet.getYMin() < 0) {
fillMin = 0f;
} else {
float max, min;
if (data.getYMax() > 0)
max = 0f;
else
max = chartMaxY;
if (data.getYMin() < 0)
min = 0f;
else
min = chartMinY;
fillMin = dataSet.getYMin() >= 0 ? min : max;
}
return fillMin;
}
}

View File

@@ -0,0 +1,71 @@
package com.github.mikephil.charting.formatter;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.utils.ViewPortHandler;
import java.text.DecimalFormat;
/**
* Default formatter used for formatting values inside the chart. Uses a DecimalFormat with
* pre-calculated number of digits (depending on max and min value).
*
* @author Philipp Jahoda
*/
public class DefaultValueFormatter implements IValueFormatter
{
/**
* DecimalFormat for formatting
*/
protected DecimalFormat mFormat;
protected int mDecimalDigits;
/**
* Constructor that specifies to how many digits the value should be
* formatted.
*
* @param digits
*/
public DefaultValueFormatter(int digits) {
setup(digits);
}
/**
* Sets up the formatter with a given number of decimal digits.
*
* @param digits
*/
public void setup(int digits) {
this.mDecimalDigits = digits;
StringBuffer b = new StringBuffer();
for (int i = 0; i < digits; i++) {
if (i == 0)
b.append(".");
b.append("0");
}
mFormat = new DecimalFormat("###,###,###,##0" + b.toString());
}
@Override
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
// put more logic here ...
// avoid memory allocations here (for performance reasons)
return mFormat.format(value);
}
/**
* Returns the number of decimal digits this formatter uses.
*
* @return
*/
public int getDecimalDigits() {
return mDecimalDigits;
}
}

View File

@@ -0,0 +1,23 @@
package com.github.mikephil.charting.formatter;
import com.github.mikephil.charting.components.AxisBase;
/**
* Created by Philipp Jahoda on 20/09/15.
* Custom formatter interface that allows formatting of
* axis labels before they are being drawn.
*/
public interface IAxisValueFormatter
{
/**
* Called when a value from an axis is to be formatted
* before being drawn. For performance reasons, avoid excessive calculations
* and memory allocations inside this method.
*
* @param value the value to be formatted
* @param axis the axis the value belongs to
* @return
*/
String getFormattedValue(float value, AxisBase axis);
}

View File

@@ -0,0 +1,24 @@
package com.github.mikephil.charting.formatter;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
import com.github.mikephil.charting.interfaces.dataprovider.LineDataProvider;
/**
* Interface for providing a custom logic to where the filling line of a LineDataSet
* should end. This of course only works if setFillEnabled(...) is set to true.
*
* @author Philipp Jahoda
*/
public interface IFillFormatter
{
/**
* Returns the vertical (y-axis) position where the filled-line of the
* LineDataSet should end.
*
* @param dataSet the ILineDataSet that is currently drawn
* @param dataProvider
* @return
*/
float getFillLinePosition(ILineDataSet dataSet, LineDataProvider dataProvider);
}

View File

@@ -0,0 +1,29 @@
package com.github.mikephil.charting.formatter;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.utils.ViewPortHandler;
/**
* Interface that allows custom formatting of all values inside the chart before they are
* being drawn to the screen. Simply create your own formatting class and let
* it implement IValueFormatter. Then override the getFormattedValue(...) method
* and return whatever you want.
*
* @author Philipp Jahoda
*/
public interface IValueFormatter
{
/**
* Called when a value (from labels inside the chart) is formatted
* before being drawn. For performance reasons, avoid excessive calculations
* and memory allocations inside this method.
*
* @param value the value to be formatted
* @param entry the entry the value belongs to - in e.g. BarChart, this is of class BarEntry
* @param dataSetIndex the index of the DataSet the entry in focus belongs to
* @param viewPortHandler provides information about the current chart state (scale, translation, ...)
* @return the formatted label ready for being drawn
*/
String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler);
}

View File

@@ -0,0 +1,69 @@
package com.github.mikephil.charting.formatter;
import com.github.mikephil.charting.components.AxisBase;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.utils.ViewPortHandler;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Collection;
/**
* This formatter is used for passing an array of x-axis labels, on whole x steps.
*/
public class IndexAxisValueFormatter implements IAxisValueFormatter
{
private String[] mValues = new String[] {};
private int mValueCount = 0;
/**
* An empty constructor.
* Use `setValues` to set the axis labels.
*/
public IndexAxisValueFormatter() {
}
/**
* Constructor that specifies axis labels.
*
* @param values The values string array
*/
public IndexAxisValueFormatter(String[] values) {
if (values != null)
setValues(values);
}
/**
* Constructor that specifies axis labels.
*
* @param values The values string array
*/
public IndexAxisValueFormatter(Collection<String> values) {
if (values != null)
setValues(values.toArray(new String[values.size()]));
}
public String getFormattedValue(float value, AxisBase axis) {
int index = Math.round(value);
if (index < 0 || index >= mValueCount || index != (int)value)
return "";
return mValues[index];
}
public String[] getValues()
{
return mValues;
}
public void setValues(String[] values)
{
if (values == null)
values = new String[] {};
this.mValues = values;
this.mValueCount = values.length;
}
}

View File

@@ -0,0 +1,103 @@
package com.github.mikephil.charting.formatter;
import com.github.mikephil.charting.components.AxisBase;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.utils.ViewPortHandler;
import java.text.DecimalFormat;
/**
* Predefined value-formatter that formats large numbers in a pretty way.
* Outputs: 856 = 856; 1000 = 1k; 5821 = 5.8k; 10500 = 10k; 101800 = 102k;
* 2000000 = 2m; 7800000 = 7.8m; 92150000 = 92m; 123200000 = 123m; 9999999 =
* 10m; 1000000000 = 1b; Special thanks to Roman Gromov
* (https://github.com/romangromov) for this piece of code.
*
* @author Philipp Jahoda
* @author Oleksandr Tyshkovets <olexandr.tyshkovets@gmail.com>
*/
public class LargeValueFormatter implements IValueFormatter, IAxisValueFormatter
{
private String[] mSuffix = new String[]{
"", "k", "m", "b", "t"
};
private int mMaxLength = 5;
private DecimalFormat mFormat;
private String mText = "";
public LargeValueFormatter() {
mFormat = new DecimalFormat("###E00");
}
/**
* Creates a formatter that appends a specified text to the result string
*
* @param appendix a text that will be appended
*/
public LargeValueFormatter(String appendix) {
this();
mText = appendix;
}
// IValueFormatter
@Override
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
return makePretty(value) + mText;
}
// IAxisValueFormatter
@Override
public String getFormattedValue(float value, AxisBase axis) {
return makePretty(value) + mText;
}
/**
* Set an appendix text to be added at the end of the formatted value.
*
* @param appendix
*/
public void setAppendix(String appendix) {
this.mText = appendix;
}
/**
* Set custom suffix to be appended after the values.
* Default suffix: ["", "k", "m", "b", "t"]
*
* @param suffix new suffix
*/
public void setSuffix(String[] suffix) {
this.mSuffix = suffix;
}
public void setMaxLength(int maxLength) {
this.mMaxLength = maxLength;
}
/**
* Formats each number properly. Special thanks to Roman Gromov
* (https://github.com/romangromov) for this piece of code.
*/
private String makePretty(double number) {
String r = mFormat.format(number);
int numericValue1 = Character.getNumericValue(r.charAt(r.length() - 1));
int numericValue2 = Character.getNumericValue(r.charAt(r.length() - 2));
int combined = Integer.valueOf(numericValue2 + "" + numericValue1);
r = r.replaceAll("E[0-9][0-9]", mSuffix[combined / 3]);
while (r.length() > mMaxLength || r.matches("[0-9]+\\.[a-z]")) {
r = r.substring(0, r.length() - 2) + r.substring(r.length() - 1);
}
return r;
}
public int getDecimalDigits() {
return 0;
}
}

View File

@@ -0,0 +1,49 @@
package com.github.mikephil.charting.formatter;
import com.github.mikephil.charting.components.AxisBase;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.utils.ViewPortHandler;
import java.text.DecimalFormat;
/**
* This IValueFormatter is just for convenience and simply puts a "%" sign after
* each value. (Recommeded for PieChart)
*
* @author Philipp Jahoda
*/
public class PercentFormatter implements IValueFormatter, IAxisValueFormatter
{
protected DecimalFormat mFormat;
public PercentFormatter() {
mFormat = new DecimalFormat("###,###,##0.0");
}
/**
* Allow a custom decimalformat
*
* @param format
*/
public PercentFormatter(DecimalFormat format) {
this.mFormat = format;
}
// IValueFormatter
@Override
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
return mFormat.format(value) + " %";
}
// IAxisValueFormatter
@Override
public String getFormattedValue(float value, AxisBase axis) {
return mFormat.format(value) + " %";
}
public int getDecimalDigits() {
return 1;
}
}

View File

@@ -0,0 +1,75 @@
package com.github.mikephil.charting.formatter;
import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.utils.ViewPortHandler;
import java.text.DecimalFormat;
/**
* Created by Philipp Jahoda on 28/01/16.
* <p/>
* A formatter specifically for stacked BarChart that allows to specify whether the all stack values
* or just the top value should be drawn.
*/
public class StackedValueFormatter implements IValueFormatter
{
/**
* if true, all stack values of the stacked bar entry are drawn, else only top
*/
private boolean mDrawWholeStack;
/**
* a string that should be appended behind the value
*/
private String mAppendix;
private DecimalFormat mFormat;
/**
* Constructor.
*
* @param drawWholeStack if true, all stack values of the stacked bar entry are drawn, else only top
* @param appendix a string that should be appended behind the value
* @param decimals the number of decimal digits to use
*/
public StackedValueFormatter(boolean drawWholeStack, String appendix, int decimals) {
this.mDrawWholeStack = drawWholeStack;
this.mAppendix = appendix;
StringBuffer b = new StringBuffer();
for (int i = 0; i < decimals; i++) {
if (i == 0)
b.append(".");
b.append("0");
}
this.mFormat = new DecimalFormat("###,###,###,##0" + b.toString());
}
@Override
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
if (!mDrawWholeStack && entry instanceof BarEntry) {
BarEntry barEntry = (BarEntry) entry;
float[] vals = barEntry.getYVals();
if (vals != null) {
// find out if we are on top of the stack
if (vals[vals.length - 1] == value) {
// return the "sum" across all stack values
return mFormat.format(barEntry.getY()) + mAppendix;
} else {
return ""; // return empty
}
}
}
// return the "proposed" value
return mFormat.format(value) + mAppendix;
}
}

View File

@@ -0,0 +1,163 @@
package com.github.mikephil.charting.highlight;
import com.github.mikephil.charting.data.BarData;
import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData;
import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider;
import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
import com.github.mikephil.charting.utils.MPPointD;
/**
* Created by Philipp Jahoda on 22/07/15.
*/
public class BarHighlighter extends ChartHighlighter<BarDataProvider> {
public BarHighlighter(BarDataProvider chart) {
super(chart);
}
@Override
public Highlight getHighlight(float x, float y) {
Highlight high = super.getHighlight(x, y);
if(high == null) {
return null;
}
MPPointD pos = getValsForTouch(x, y);
BarData barData = mChart.getBarData();
IBarDataSet set = barData.getDataSetByIndex(high.getDataSetIndex());
if (set.isStacked()) {
return getStackedHighlight(high,
set,
(float) pos.x,
(float) pos.y);
}
MPPointD.recycleInstance(pos);
return high;
}
/**
* This method creates the Highlight object that also indicates which value of a stacked BarEntry has been
* selected.
*
* @param high the Highlight to work with looking for stacked values
* @param set
* @param xVal
* @param yVal
* @return
*/
public Highlight getStackedHighlight(Highlight high, IBarDataSet set, float xVal, float yVal) {
BarEntry entry = set.getEntryForXValue(xVal, yVal);
if (entry == null)
return null;
// not stacked
if (entry.getYVals() == null) {
return high;
} else {
Range[] ranges = entry.getRanges();
if (ranges.length > 0) {
int stackIndex = getClosestStackIndex(ranges, yVal);
MPPointD pixels = mChart.getTransformer(set.getAxisDependency()).getPixelForValues(high.getX(), ranges[stackIndex].to);
Highlight stackedHigh = new Highlight(
entry.getX(),
entry.getY(),
(float) pixels.x,
(float) pixels.y,
high.getDataSetIndex(),
stackIndex,
high.getAxis()
);
MPPointD.recycleInstance(pixels);
return stackedHigh;
}
}
return null;
}
/**
* Returns the index of the closest value inside the values array / ranges (stacked barchart) to the value
* given as
* a parameter.
*
* @param ranges
* @param value
* @return
*/
protected int getClosestStackIndex(Range[] ranges, float value) {
if (ranges == null || ranges.length == 0)
return 0;
int stackIndex = 0;
for (Range range : ranges) {
if (range.contains(value))
return stackIndex;
else
stackIndex++;
}
int length = Math.max(ranges.length - 1, 0);
return (value > ranges[length].to) ? length : 0;
}
// /**
// * Splits up the stack-values of the given bar-entry into Range objects.
// *
// * @param entry
// * @return
// */
// protected Range[] getRanges(BarEntry entry) {
//
// float[] values = entry.getYVals();
//
// if (values == null || values.length == 0)
// return new Range[0];
//
// Range[] ranges = new Range[values.length];
//
// float negRemain = -entry.getNegativeSum();
// float posRemain = 0f;
//
// for (int i = 0; i < ranges.length; i++) {
//
// float value = values[i];
//
// if (value < 0) {
// ranges[i] = new Range(negRemain, negRemain + Math.abs(value));
// negRemain += Math.abs(value);
// } else {
// ranges[i] = new Range(posRemain, posRemain + value);
// posRemain += value;
// }
// }
//
// return ranges;
// }
@Override
protected float getDistance(float x1, float y1, float x2, float y2) {
return Math.abs(x1 - x2);
}
@Override
protected BarLineScatterCandleBubbleData getData() {
return mChart.getBarData();
}
}

View File

@@ -0,0 +1,246 @@
package com.github.mikephil.charting.highlight;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData;
import com.github.mikephil.charting.data.DataSet;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider;
import com.github.mikephil.charting.interfaces.datasets.IDataSet;
import com.github.mikephil.charting.utils.MPPointD;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Philipp Jahoda on 21/07/15.
*/
public class ChartHighlighter<T extends BarLineScatterCandleBubbleDataProvider> implements IHighlighter
{
/**
* instance of the data-provider
*/
protected T mChart;
/**
* buffer for storing previously highlighted values
*/
protected List<Highlight> mHighlightBuffer = new ArrayList<Highlight>();
public ChartHighlighter(T chart) {
this.mChart = chart;
}
@Override
public Highlight getHighlight(float x, float y) {
MPPointD pos = getValsForTouch(x, y);
float xVal = (float) pos.x;
MPPointD.recycleInstance(pos);
Highlight high = getHighlightForX(xVal, x, y);
return high;
}
/**
* Returns a recyclable MPPointD instance.
* Returns the corresponding xPos for a given touch-position in pixels.
*
* @param x
* @param y
* @return
*/
protected MPPointD getValsForTouch(float x, float y) {
// take any transformer to determine the x-axis value
MPPointD pos = mChart.getTransformer(YAxis.AxisDependency.LEFT).getValuesByTouchPoint(x, y);
return pos;
}
/**
* Returns the corresponding Highlight for a given xVal and x- and y-touch position in pixels.
*
* @param xVal
* @param x
* @param y
* @return
*/
protected Highlight getHighlightForX(float xVal, float x, float y) {
List<Highlight> closestValues = getHighlightsAtXValue(xVal, x, y);
if(closestValues.isEmpty()) {
return null;
}
float leftAxisMinDist = getMinimumDistance(closestValues, y, YAxis.AxisDependency.LEFT);
float rightAxisMinDist = getMinimumDistance(closestValues, y, YAxis.AxisDependency.RIGHT);
YAxis.AxisDependency axis = leftAxisMinDist < rightAxisMinDist ? YAxis.AxisDependency.LEFT : YAxis.AxisDependency.RIGHT;
Highlight detail = getClosestHighlightByPixel(closestValues, x, y, axis, mChart.getMaxHighlightDistance());
return detail;
}
/**
* Returns the minimum distance from a touch value (in pixels) to the
* closest value (in pixels) that is displayed in the chart.
*
* @param closestValues
* @param pos
* @param axis
* @return
*/
protected float getMinimumDistance(List<Highlight> closestValues, float pos, YAxis.AxisDependency axis) {
float distance = Float.MAX_VALUE;
for (int i = 0; i < closestValues.size(); i++) {
Highlight high = closestValues.get(i);
if (high.getAxis() == axis) {
float tempDistance = Math.abs(getHighlightPos(high) - pos);
if (tempDistance < distance) {
distance = tempDistance;
}
}
}
return distance;
}
protected float getHighlightPos(Highlight h) {
return h.getYPx();
}
/**
* Returns a list of Highlight objects representing the entries closest to the given xVal.
* The returned list contains two objects per DataSet (closest rounding up, closest rounding down).
*
* @param xVal the transformed x-value of the x-touch position
* @param x touch position
* @param y touch position
* @return
*/
protected List<Highlight> getHighlightsAtXValue(float xVal, float x, float y) {
mHighlightBuffer.clear();
BarLineScatterCandleBubbleData data = getData();
if (data == null)
return mHighlightBuffer;
for (int i = 0, dataSetCount = data.getDataSetCount(); i < dataSetCount; i++) {
IDataSet dataSet = data.getDataSetByIndex(i);
// don't include DataSets that cannot be highlighted
if (!dataSet.isHighlightEnabled())
continue;
mHighlightBuffer.addAll(buildHighlights(dataSet, i, xVal, DataSet.Rounding.CLOSEST));
}
return mHighlightBuffer;
}
/**
* An array of `Highlight` objects corresponding to the selected xValue and dataSetIndex.
*
* @param set
* @param dataSetIndex
* @param xVal
* @param rounding
* @return
*/
protected List<Highlight> buildHighlights(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) {
ArrayList<Highlight> highlights = new ArrayList<>();
//noinspection unchecked
List<Entry> entries = set.getEntriesForXValue(xVal);
if (entries.size() == 0) {
// Try to find closest x-value and take all entries for that x-value
final Entry closest = set.getEntryForXValue(xVal, Float.NaN, rounding);
if (closest != null)
{
//noinspection unchecked
entries = set.getEntriesForXValue(closest.getX());
}
}
if (entries.size() == 0)
return highlights;
for (Entry e : entries) {
MPPointD pixels = mChart.getTransformer(
set.getAxisDependency()).getPixelForValues(e.getX(), e.getY());
highlights.add(new Highlight(
e.getX(), e.getY(),
(float) pixels.x, (float) pixels.y,
dataSetIndex, set.getAxisDependency()));
}
return highlights;
}
/**
* Returns the Highlight of the DataSet that contains the closest value on the
* y-axis.
*
* @param closestValues contains two Highlight objects per DataSet closest to the selected x-position (determined by
* rounding up an down)
* @param x
* @param y
* @param axis the closest axis
* @param minSelectionDistance
* @return
*/
public Highlight getClosestHighlightByPixel(List<Highlight> closestValues, float x, float y,
YAxis.AxisDependency axis, float minSelectionDistance) {
Highlight closest = null;
float distance = minSelectionDistance;
for (int i = 0; i < closestValues.size(); i++) {
Highlight high = closestValues.get(i);
if (axis == null || high.getAxis() == axis) {
float cDistance = getDistance(x, y, high.getXPx(), high.getYPx());
if (cDistance < distance) {
closest = high;
distance = cDistance;
}
}
}
return closest;
}
/**
* Calculates the distance between the two given points.
*
* @param x1
* @param y1
* @param x2
* @param y2
* @return
*/
protected float getDistance(float x1, float y1, float x2, float y2) {
//return Math.abs(y1 - y2);
//return Math.abs(x1 - x2);
return (float) Math.hypot(x1 - x2, y1 - y2);
}
protected BarLineScatterCandleBubbleData getData() {
return mChart.getData();
}
}

View File

@@ -0,0 +1,93 @@
package com.github.mikephil.charting.highlight;
import com.github.mikephil.charting.data.BarData;
import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData;
import com.github.mikephil.charting.data.ChartData;
import com.github.mikephil.charting.data.DataSet;
import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider;
import com.github.mikephil.charting.interfaces.dataprovider.CombinedDataProvider;
import com.github.mikephil.charting.interfaces.datasets.IDataSet;
import java.util.List;
/**
* Created by Philipp Jahoda on 12/09/15.
*/
public class CombinedHighlighter extends ChartHighlighter<CombinedDataProvider> implements IHighlighter
{
/**
* bar highlighter for supporting stacked highlighting
*/
protected BarHighlighter barHighlighter;
public CombinedHighlighter(CombinedDataProvider chart, BarDataProvider barChart) {
super(chart);
// if there is BarData, create a BarHighlighter
barHighlighter = barChart.getBarData() == null ? null : new BarHighlighter(barChart);
}
@Override
protected List<Highlight> getHighlightsAtXValue(float xVal, float x, float y) {
mHighlightBuffer.clear();
List<BarLineScatterCandleBubbleData> dataObjects = mChart.getCombinedData().getAllData();
for (int i = 0; i < dataObjects.size(); i++) {
ChartData dataObject = dataObjects.get(i);
// in case of BarData, let the BarHighlighter take over
if (barHighlighter != null && dataObject instanceof BarData) {
Highlight high = barHighlighter.getHighlight(x, y);
if (high != null) {
high.setDataIndex(i);
mHighlightBuffer.add(high);
}
} else {
for (int j = 0, dataSetCount = dataObject.getDataSetCount(); j < dataSetCount; j++) {
IDataSet dataSet = dataObjects.get(i).getDataSetByIndex(j);
// don't include datasets that cannot be highlighted
if (!dataSet.isHighlightEnabled())
continue;
List<Highlight> highs = buildHighlights(dataSet, j, xVal, DataSet.Rounding.CLOSEST);
for (Highlight high : highs)
{
high.setDataIndex(i);
mHighlightBuffer.add(high);
}
}
}
}
return mHighlightBuffer;
}
// protected Highlight getClosest(float x, float y, Highlight... highs) {
//
// Highlight closest = null;
// float minDistance = Float.MAX_VALUE;
//
// for (Highlight high : highs) {
//
// if (high == null)
// continue;
//
// float tempDistance = getDistance(x, y, high.getXPx(), high.getYPx());
//
// if (tempDistance < minDistance) {
// minDistance = tempDistance;
// closest = high;
// }
// }
//
// return closest;
// }
}

View File

@@ -0,0 +1,243 @@
package com.github.mikephil.charting.highlight;
import com.github.mikephil.charting.components.YAxis;
/**
* Contains information needed to determine the highlighted value.
*
* @author Philipp Jahoda
*/
public class Highlight {
/**
* the x-value of the highlighted value
*/
private float mX = Float.NaN;
/**
* the y-value of the highlighted value
*/
private float mY = Float.NaN;
/**
* the x-pixel of the highlight
*/
private float mXPx;
/**
* the y-pixel of the highlight
*/
private float mYPx;
/**
* the index of the data object - in case it refers to more than one
*/
private int mDataIndex = -1;
/**
* the index of the dataset the highlighted value is in
*/
private int mDataSetIndex;
/**
* index which value of a stacked bar entry is highlighted, default -1
*/
private int mStackIndex = -1;
/**
* the axis the highlighted value belongs to
*/
private YAxis.AxisDependency axis;
/**
* the x-position (pixels) on which this highlight object was last drawn
*/
private float mDrawX;
/**
* the y-position (pixels) on which this highlight object was last drawn
*/
private float mDrawY;
public Highlight(float x, float y, int dataSetIndex, int dataIndex) {
this.mX = x;
this.mY = y;
this.mDataSetIndex = dataSetIndex;
this.mDataIndex = dataIndex;
}
public Highlight(float x, float y, int dataSetIndex) {
this.mX = x;
this.mY = y;
this.mDataSetIndex = dataSetIndex;
this.mDataIndex = -1;
}
public Highlight(float x, int dataSetIndex, int stackIndex) {
this(x, Float.NaN, dataSetIndex);
this.mStackIndex = stackIndex;
}
/**
* constructor
*
* @param x the x-value of the highlighted value
* @param y the y-value of the highlighted value
* @param dataSetIndex the index of the DataSet the highlighted value belongs to
*/
public Highlight(float x, float y, float xPx, float yPx, int dataSetIndex, YAxis.AxisDependency axis) {
this.mX = x;
this.mY = y;
this.mXPx = xPx;
this.mYPx = yPx;
this.mDataSetIndex = dataSetIndex;
this.axis = axis;
}
/**
* Constructor, only used for stacked-barchart.
*
* @param x the index of the highlighted value on the x-axis
* @param y the y-value of the highlighted value
* @param dataSetIndex the index of the DataSet the highlighted value belongs to
* @param stackIndex references which value of a stacked-bar entry has been
* selected
*/
public Highlight(float x, float y, float xPx, float yPx, int dataSetIndex, int stackIndex, YAxis.AxisDependency axis) {
this(x, y, xPx, yPx, dataSetIndex, axis);
this.mStackIndex = stackIndex;
}
/**
* returns the x-value of the highlighted value
*
* @return
*/
public float getX() {
return mX;
}
/**
* returns the y-value of the highlighted value
*
* @return
*/
public float getY() {
return mY;
}
/**
* returns the x-position of the highlight in pixels
*/
public float getXPx() {
return mXPx;
}
/**
* returns the y-position of the highlight in pixels
*/
public float getYPx() {
return mYPx;
}
/**
* the index of the data object - in case it refers to more than one
*
* @return
*/
public int getDataIndex() {
return mDataIndex;
}
public void setDataIndex(int mDataIndex) {
this.mDataIndex = mDataIndex;
}
/**
* returns the index of the DataSet the highlighted value is in
*
* @return
*/
public int getDataSetIndex() {
return mDataSetIndex;
}
/**
* Only needed if a stacked-barchart entry was highlighted. References the
* selected value within the stacked-entry.
*
* @return
*/
public int getStackIndex() {
return mStackIndex;
}
public boolean isStacked() {
return mStackIndex >= 0;
}
/**
* Returns the axis the highlighted value belongs to.
*
* @return
*/
public YAxis.AxisDependency getAxis() {
return axis;
}
/**
* Sets the x- and y-position (pixels) where this highlight was last drawn.
*
* @param x
* @param y
*/
public void setDraw(float x, float y) {
this.mDrawX = x;
this.mDrawY = y;
}
/**
* Returns the x-position in pixels where this highlight object was last drawn.
*
* @return
*/
public float getDrawX() {
return mDrawX;
}
/**
* Returns the y-position in pixels where this highlight object was last drawn.
*
* @return
*/
public float getDrawY() {
return mDrawY;
}
/**
* Returns true if this highlight object is equal to the other (compares
* xIndex and dataSetIndex)
*
* @param h
* @return
*/
public boolean equalTo(Highlight h) {
if (h == null)
return false;
else {
if (this.mDataSetIndex == h.mDataSetIndex && this.mX == h.mX
&& this.mStackIndex == h.mStackIndex && this.mDataIndex == h.mDataIndex)
return true;
else
return false;
}
}
@Override
public String toString() {
return "Highlight, x: " + mX + ", y: " + mY + ", dataSetIndex: " + mDataSetIndex
+ ", stackIndex (only stacked barentry): " + mStackIndex;
}
}

View File

@@ -0,0 +1,85 @@
package com.github.mikephil.charting.highlight;
import com.github.mikephil.charting.data.BarData;
import com.github.mikephil.charting.data.DataSet;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider;
import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
import com.github.mikephil.charting.interfaces.datasets.IDataSet;
import com.github.mikephil.charting.utils.MPPointD;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Philipp Jahoda on 22/07/15.
*/
public class HorizontalBarHighlighter extends BarHighlighter {
public HorizontalBarHighlighter(BarDataProvider chart) {
super(chart);
}
@Override
public Highlight getHighlight(float x, float y) {
BarData barData = mChart.getBarData();
MPPointD pos = getValsForTouch(y, x);
Highlight high = getHighlightForX((float) pos.y, y, x);
if (high == null)
return null;
IBarDataSet set = barData.getDataSetByIndex(high.getDataSetIndex());
if (set.isStacked()) {
return getStackedHighlight(high,
set,
(float) pos.y,
(float) pos.x);
}
MPPointD.recycleInstance(pos);
return high;
}
@Override
protected List<Highlight> buildHighlights(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) {
ArrayList<Highlight> highlights = new ArrayList<>();
//noinspection unchecked
List<Entry> entries = set.getEntriesForXValue(xVal);
if (entries.size() == 0) {
// Try to find closest x-value and take all entries for that x-value
final Entry closest = set.getEntryForXValue(xVal, Float.NaN, rounding);
if (closest != null)
{
//noinspection unchecked
entries = set.getEntriesForXValue(closest.getX());
}
}
if (entries.size() == 0)
return highlights;
for (Entry e : entries) {
MPPointD pixels = mChart.getTransformer(
set.getAxisDependency()).getPixelForValues(e.getY(), e.getX());
highlights.add(new Highlight(
e.getX(), e.getY(),
(float) pixels.x, (float) pixels.y,
dataSetIndex, set.getAxisDependency()));
}
return highlights;
}
@Override
protected float getDistance(float x1, float y1, float x2, float y2) {
return Math.abs(y1 - y2);
}
}

View File

@@ -0,0 +1,17 @@
package com.github.mikephil.charting.highlight;
/**
* Created by philipp on 10/06/16.
*/
public interface IHighlighter
{
/**
* Returns a Highlight object corresponding to the given x- and y- touch positions in pixels.
*
* @param x
* @param y
* @return
*/
Highlight getHighlight(float x, float y);
}

View File

@@ -0,0 +1,25 @@
package com.github.mikephil.charting.highlight;
import com.github.mikephil.charting.charts.PieChart;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.interfaces.datasets.IPieDataSet;
/**
* Created by philipp on 12/06/16.
*/
public class PieHighlighter extends PieRadarHighlighter<PieChart> {
public PieHighlighter(PieChart chart) {
super(chart);
}
@Override
protected Highlight getClosestHighlight(int index, float x, float y) {
IPieDataSet set = mChart.getData().getDataSet();
final Entry entry = set.getEntryForIndex(index);
return new Highlight(index, entry.getY(), x, y, 0, set.getAxisDependency());
}
}

View File

@@ -0,0 +1,66 @@
package com.github.mikephil.charting.highlight;
import com.github.mikephil.charting.charts.PieChart;
import com.github.mikephil.charting.charts.PieRadarChartBase;
import java.util.ArrayList;
import java.util.List;
/**
* Created by philipp on 12/06/16.
*/
public abstract class PieRadarHighlighter<T extends PieRadarChartBase> implements IHighlighter
{
protected T mChart;
/**
* buffer for storing previously highlighted values
*/
protected List<Highlight> mHighlightBuffer = new ArrayList<Highlight>();
public PieRadarHighlighter(T chart) {
this.mChart = chart;
}
@Override
public Highlight getHighlight(float x, float y) {
float touchDistanceToCenter = mChart.distanceToCenter(x, y);
// check if a slice was touched
if (touchDistanceToCenter > mChart.getRadius()) {
// if no slice was touched, highlight nothing
return null;
} else {
float angle = mChart.getAngleForPoint(x, y);
if (mChart instanceof PieChart) {
angle /= mChart.getAnimator().getPhaseY();
}
int index = mChart.getIndexForAngle(angle);
// check if the index could be found
if (index < 0 || index >= mChart.getData().getMaxEntryCountSet().getEntryCount()) {
return null;
} else {
return getClosestHighlight(index, x, y);
}
}
}
/**
* Returns the closest Highlight object of the given objects based on the touch position inside the chart.
*
* @param index
* @param x
* @param y
* @return
*/
protected abstract Highlight getClosestHighlight(int index, float x, float y);
}

View File

@@ -0,0 +1,79 @@
package com.github.mikephil.charting.highlight;
import com.github.mikephil.charting.charts.RadarChart;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.interfaces.datasets.IDataSet;
import com.github.mikephil.charting.utils.MPPointF;
import com.github.mikephil.charting.utils.Utils;
import java.util.List;
/**
* Created by philipp on 12/06/16.
*/
public class RadarHighlighter extends PieRadarHighlighter<RadarChart> {
public RadarHighlighter(RadarChart chart) {
super(chart);
}
@Override
protected Highlight getClosestHighlight(int index, float x, float y) {
List<Highlight> highlights = getHighlightsAtIndex(index);
float distanceToCenter = mChart.distanceToCenter(x, y) / mChart.getFactor();
Highlight closest = null;
float distance = Float.MAX_VALUE;
for (int i = 0; i < highlights.size(); i++) {
Highlight high = highlights.get(i);
float cdistance = Math.abs(high.getY() - distanceToCenter);
if (cdistance < distance) {
closest = high;
distance = cdistance;
}
}
return closest;
}
/**
* Returns an array of Highlight objects for the given index. The Highlight
* objects give information about the value at the selected index and the
* DataSet it belongs to. INFORMATION: This method does calculations at
* runtime. Do not over-use in performance critical situations.
*
* @param index
* @return
*/
protected List<Highlight> getHighlightsAtIndex(int index) {
mHighlightBuffer.clear();
float phaseX = mChart.getAnimator().getPhaseX();
float phaseY = mChart.getAnimator().getPhaseY();
float sliceangle = mChart.getSliceAngle();
float factor = mChart.getFactor();
MPPointF pOut = MPPointF.getInstance(0,0);
for (int i = 0; i < mChart.getData().getDataSetCount(); i++) {
IDataSet<?> dataSet = mChart.getData().getDataSetByIndex(i);
final Entry entry = dataSet.getEntryForIndex(index);
float y = (entry.getY() - mChart.getYChartMin());
Utils.getPosition(
mChart.getCenterOffsets(), y * factor * phaseY,
sliceangle * index * phaseX + mChart.getRotationAngle(), pOut);
mHighlightBuffer.add(new Highlight(index, entry.getY(), pOut.x, pOut.y, i, dataSet.getAxisDependency()));
}
return mHighlightBuffer;
}
}

View File

@@ -0,0 +1,38 @@
package com.github.mikephil.charting.highlight;
/**
* Created by Philipp Jahoda on 24/07/15. Class that represents the range of one value in a stacked bar entry. e.g.
* stack values are -10, 5, 20 -> then ranges are (-10 - 0, 0 - 5, 5 - 25).
*/
public final class Range {
public float from;
public float to;
public Range(float from, float to) {
this.from = from;
this.to = to;
}
/**
* Returns true if this range contains (if the value is in between) the given value, false if not.
*
* @param value
* @return
*/
public boolean contains(float value) {
if (value > from && value <= to)
return true;
else
return false;
}
public boolean isLarger(float value) {
return value > to;
}
public boolean isSmaller(float value) {
return value < from;
}
}

View File

@@ -0,0 +1,11 @@
package com.github.mikephil.charting.interfaces.dataprovider;
import com.github.mikephil.charting.data.BarData;
public interface BarDataProvider extends BarLineScatterCandleBubbleDataProvider {
BarData getBarData();
boolean isDrawBarShadowEnabled();
boolean isDrawValueAboveBarEnabled();
boolean isHighlightFullBarEnabled();
}

View File

@@ -0,0 +1,16 @@
package com.github.mikephil.charting.interfaces.dataprovider;
import com.github.mikephil.charting.components.YAxis.AxisDependency;
import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData;
import com.github.mikephil.charting.utils.Transformer;
public interface BarLineScatterCandleBubbleDataProvider extends ChartInterface {
Transformer getTransformer(AxisDependency axis);
boolean isInverted(AxisDependency axis);
float getLowestVisibleX();
float getHighestVisibleX();
BarLineScatterCandleBubbleData getData();
}

View File

@@ -0,0 +1,8 @@
package com.github.mikephil.charting.interfaces.dataprovider;
import com.github.mikephil.charting.data.BubbleData;
public interface BubbleDataProvider extends BarLineScatterCandleBubbleDataProvider {
BubbleData getBubbleData();
}

View File

@@ -0,0 +1,8 @@
package com.github.mikephil.charting.interfaces.dataprovider;
import com.github.mikephil.charting.data.CandleData;
public interface CandleDataProvider extends BarLineScatterCandleBubbleDataProvider {
CandleData getCandleData();
}

View File

@@ -0,0 +1,69 @@
package com.github.mikephil.charting.interfaces.dataprovider;
import android.graphics.RectF;
import com.github.mikephil.charting.data.ChartData;
import com.github.mikephil.charting.formatter.IValueFormatter;
import com.github.mikephil.charting.utils.MPPointF;
/**
* Interface that provides everything there is to know about the dimensions,
* bounds, and range of the chart.
*
* @author Philipp Jahoda
*/
public interface ChartInterface {
/**
* Returns the minimum x value of the chart, regardless of zoom or translation.
*
* @return
*/
float getXChartMin();
/**
* Returns the maximum x value of the chart, regardless of zoom or translation.
*
* @return
*/
float getXChartMax();
float getXRange();
/**
* Returns the minimum y value of the chart, regardless of zoom or translation.
*
* @return
*/
float getYChartMin();
/**
* Returns the maximum y value of the chart, regardless of zoom or translation.
*
* @return
*/
float getYChartMax();
/**
* Returns the maximum distance in scren dp a touch can be away from an entry to cause it to get highlighted.
*
* @return
*/
float getMaxHighlightDistance();
int getWidth();
int getHeight();
MPPointF getCenterOfView();
MPPointF getCenterOffsets();
RectF getContentRect();
IValueFormatter getDefaultValueFormatter();
ChartData getData();
int getMaxVisibleCount();
}

View File

@@ -0,0 +1,11 @@
package com.github.mikephil.charting.interfaces.dataprovider;
import com.github.mikephil.charting.data.CombinedData;
/**
* Created by philipp on 11/06/16.
*/
public interface CombinedDataProvider extends LineDataProvider, BarDataProvider, BubbleDataProvider, CandleDataProvider, ScatterDataProvider {
CombinedData getCombinedData();
}

View File

@@ -0,0 +1,11 @@
package com.github.mikephil.charting.interfaces.dataprovider;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.LineData;
public interface LineDataProvider extends BarLineScatterCandleBubbleDataProvider {
LineData getLineData();
YAxis getAxis(YAxis.AxisDependency dependency);
}

View File

@@ -0,0 +1,8 @@
package com.github.mikephil.charting.interfaces.dataprovider;
import com.github.mikephil.charting.data.ScatterData;
public interface ScatterDataProvider extends BarLineScatterCandleBubbleDataProvider {
ScatterData getScatterData();
}

View File

@@ -0,0 +1,71 @@
package com.github.mikephil.charting.interfaces.datasets;
import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.utils.Fill;
import java.util.List;
/**
* Created by philipp on 21/10/15.
*/
public interface IBarDataSet extends IBarLineScatterCandleBubbleDataSet<BarEntry> {
List<Fill> getFills();
Fill getFill(int index);
/**
* Returns true if this DataSet is stacked (stacksize > 1) or not.
*
* @return
*/
boolean isStacked();
/**
* Returns the maximum number of bars that can be stacked upon another in
* this DataSet. This should return 1 for non stacked bars, and > 1 for stacked bars.
*
* @return
*/
int getStackSize();
/**
* Returns the color used for drawing the bar-shadows. The bar shadows is a
* surface behind the bar that indicates the maximum value.
*
* @return
*/
int getBarShadowColor();
/**
* Returns the width used for drawing borders around the bars.
* If borderWidth == 0, no border will be drawn.
*
* @return
*/
float getBarBorderWidth();
/**
* Returns the color drawing borders around the bars.
*
* @return
*/
int getBarBorderColor();
/**
* Returns the alpha value (transparency) that is used for drawing the
* highlight indicator.
*
* @return
*/
int getHighLightAlpha();
/**
* Returns the labels used for the different value-stacks in the legend.
* This is only relevant for stacked bar entries.
*
* @return
*/
String[] getStackLabels();
}

View File

@@ -0,0 +1,16 @@
package com.github.mikephil.charting.interfaces.datasets;
import com.github.mikephil.charting.data.Entry;
/**
* Created by philipp on 21/10/15.
*/
public interface IBarLineScatterCandleBubbleDataSet<T extends Entry> extends IDataSet<T> {
/**
* Returns the color that is used for drawing the highlight indicators.
*
* @return
*/
int getHighLightColor();
}

View File

@@ -0,0 +1,27 @@
package com.github.mikephil.charting.interfaces.datasets;
import com.github.mikephil.charting.data.BubbleEntry;
/**
* Created by philipp on 21/10/15.
*/
public interface IBubbleDataSet extends IBarLineScatterCandleBubbleDataSet<BubbleEntry> {
/**
* Sets the width of the circle that surrounds the bubble when highlighted,
* in dp.
*
* @param width
*/
void setHighlightCircleWidth(float width);
float getMaxSize();
boolean isNormalizeSizeEnabled();
/**
* Returns the width of the highlight-circle that surrounds the bubble
* @return
*/
float getHighlightCircleWidth();
}

View File

@@ -0,0 +1,85 @@
package com.github.mikephil.charting.interfaces.datasets;
import android.graphics.Paint;
import com.github.mikephil.charting.data.CandleEntry;
/**
* Created by philipp on 21/10/15.
*/
public interface ICandleDataSet extends ILineScatterCandleRadarDataSet<CandleEntry> {
/**
* Returns the space that is left out on the left and right side of each
* candle.
*
* @return
*/
float getBarSpace();
/**
* Returns whether the candle bars should show?
* When false, only "ticks" will show
*
* - default: true
*
* @return
*/
boolean getShowCandleBar();
/**
* Returns the width of the candle-shadow-line in pixels.
*
* @return
*/
float getShadowWidth();
/**
* Returns shadow color for all entries
*
* @return
*/
int getShadowColor();
/**
* Returns the neutral color (for open == close)
*
* @return
*/
int getNeutralColor();
/**
* Returns the increasing color (for open < close).
*
* @return
*/
int getIncreasingColor();
/**
* Returns the decreasing color (for open > close).
*
* @return
*/
int getDecreasingColor();
/**
* Returns paint style when open < close
*
* @return
*/
Paint.Style getIncreasingPaintStyle();
/**
* Returns paint style when open > close
*
* @return
*/
Paint.Style getDecreasingPaintStyle();
/**
* Is the shadow color same as the candle color?
*
* @return
*/
boolean getShadowColorSameAsCandle();
}

View File

@@ -0,0 +1,486 @@
package com.github.mikephil.charting.interfaces.datasets;
import android.graphics.DashPathEffect;
import android.graphics.PointF;
import android.graphics.Typeface;
import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.DataSet;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.formatter.IValueFormatter;
import com.github.mikephil.charting.utils.MPPointF;
import java.util.List;
/**
* Created by Philipp Jahoda on 21/10/15.
*/
public interface IDataSet<T extends Entry> {
/** ###### ###### DATA RELATED METHODS ###### ###### */
/**
* returns the minimum y-value this DataSet holds
*
* @return
*/
float getYMin();
/**
* returns the maximum y-value this DataSet holds
*
* @return
*/
float getYMax();
/**
* returns the minimum x-value this DataSet holds
*
* @return
*/
float getXMin();
/**
* returns the maximum x-value this DataSet holds
*
* @return
*/
float getXMax();
/**
* Returns the number of y-values this DataSet represents -> the size of the y-values array
* -> yvals.size()
*
* @return
*/
int getEntryCount();
/**
* Calculates the minimum and maximum x and y values (mXMin, mXMax, mYMin, mYMax).
*/
void calcMinMax();
/**
* Calculates the min and max y-values from the Entry closest to the given fromX to the Entry closest to the given toX value.
* This is only needed for the autoScaleMinMax feature.
*
* @param fromX
* @param toX
*/
void calcMinMaxY(float fromX, float toX);
/**
* Returns the first Entry object found at the given x-value with binary
* search.
* If the no Entry at the specified x-value is found, this method
* returns the Entry at the closest x-value according to the rounding.
* INFORMATION: This method does calculations at runtime. Do
* not over-use in performance critical situations.
*
* @param xValue the x-value
* @param closestToY If there are multiple y-values for the specified x-value,
* @param rounding determine whether to round up/down/closest
* if there is no Entry matching the provided x-value
* @return
*
*
*/
T getEntryForXValue(float xValue, float closestToY, DataSet.Rounding rounding);
/**
* Returns the first Entry object found at the given x-value with binary
* search.
* If the no Entry at the specified x-value is found, this method
* returns the Entry at the closest x-value.
* INFORMATION: This method does calculations at runtime. Do
* not over-use in performance critical situations.
*
*
* @param xValue the x-value
* @param closestToY If there are multiple y-values for the specified x-value,
* @return
*/
T getEntryForXValue(float xValue, float closestToY);
/**
* Returns all Entry objects found at the given x-value with binary
* search. An empty array if no Entry object at that x-value.
* INFORMATION: This method does calculations at runtime. Do
* not over-use in performance critical situations.
*
* @param xValue
* @return
*/
List<T> getEntriesForXValue(float xValue);
/**
* Returns the Entry object found at the given index (NOT xIndex) in the values array.
*
* @param index
* @return
*/
T getEntryForIndex(int index);
/**
* Returns the first Entry index found at the given x-value with binary
* search.
* If the no Entry at the specified x-value is found, this method
* returns the Entry at the closest x-value according to the rounding.
* INFORMATION: This method does calculations at runtime. Do
* not over-use in performance critical situations.
*
* @param xValue the x-value
* @param closestToY If there are multiple y-values for the specified x-value,
* @param rounding determine whether to round up/down/closest
* if there is no Entry matching the provided x-value
* @return
*/
int getEntryIndex(float xValue, float closestToY, DataSet.Rounding rounding);
/**
* Returns the position of the provided entry in the DataSets Entry array.
* Returns -1 if doesn't exist.
*
* @param e
* @return
*/
int getEntryIndex(T e);
/**
* This method returns the actual
* index in the Entry array of the DataSet for a given xIndex. IMPORTANT: This method does
* calculations at runtime, do not over-use in performance critical
* situations.
*
* @param xIndex
* @return
*/
int getIndexInEntries(int xIndex);
/**
* Adds an Entry to the DataSet dynamically.
* Entries are added to the end of the list.
* This will also recalculate the current minimum and maximum
* values of the DataSet and the value-sum.
*
* @param e
*/
boolean addEntry(T e);
/**
* Adds an Entry to the DataSet dynamically.
* Entries are added to their appropriate index in the values array respective to their x-position.
* This will also recalculate the current minimum and maximum
* values of the DataSet and the value-sum.
*
* @param e
*/
void addEntryOrdered(T e);
/**
* Removes the first Entry (at index 0) of this DataSet from the entries array.
* Returns true if successful, false if not.
*
* @return
*/
boolean removeFirst();
/**
* Removes the last Entry (at index size-1) of this DataSet from the entries array.
* Returns true if successful, false if not.
*
* @return
*/
boolean removeLast();
/**
* Removes an Entry from the DataSets entries array. This will also
* recalculate the current minimum and maximum values of the DataSet and the
* value-sum. Returns true if an Entry was removed, false if no Entry could
* be removed.
*
* @param e
*/
boolean removeEntry(T e);
/**
* Removes the Entry object closest to the given x-value from the DataSet.
* Returns true if an Entry was removed, false if no Entry could be removed.
*
* @param xValue
*/
boolean removeEntryByXValue(float xValue);
/**
* Removes the Entry object at the given index in the values array from the DataSet.
* Returns true if an Entry was removed, false if no Entry could be removed.
*
* @param index
* @return
*/
boolean removeEntry(int index);
/**
* Checks if this DataSet contains the specified Entry. Returns true if so,
* false if not. NOTE: Performance is pretty bad on this one, do not
* over-use in performance critical situations.
*
* @param entry
* @return
*/
boolean contains(T entry);
/**
* Removes all values from this DataSet and does all necessary recalculations.
*/
void clear();
/** ###### ###### STYLING RELATED (& OTHER) METHODS ###### ###### */
/**
* Returns the label string that describes the DataSet.
*
* @return
*/
String getLabel();
/**
* Sets the label string that describes the DataSet.
*
* @param label
*/
void setLabel(String label);
/**
* Returns the axis this DataSet should be plotted against.
*
* @return
*/
YAxis.AxisDependency getAxisDependency();
/**
* Set the y-axis this DataSet should be plotted against (either LEFT or
* RIGHT). Default: LEFT
*
* @param dependency
*/
void setAxisDependency(YAxis.AxisDependency dependency);
/**
* returns all the colors that are set for this DataSet
*
* @return
*/
List<Integer> getColors();
/**
* Returns the first color (index 0) of the colors-array this DataSet
* contains. This is only used for performance reasons when only one color is in the colors array (size == 1)
*
* @return
*/
int getColor();
/**
* Returns the color at the given index of the DataSet's color array.
* Performs a IndexOutOfBounds check by modulus.
*
* @param index
* @return
*/
int getColor(int index);
/**
* returns true if highlighting of values is enabled, false if not
*
* @return
*/
boolean isHighlightEnabled();
/**
* If set to true, value highlighting is enabled which means that values can
* be highlighted programmatically or by touch gesture.
*
* @param enabled
*/
void setHighlightEnabled(boolean enabled);
/**
* Sets the formatter to be used for drawing the values inside the chart. If
* no formatter is set, the chart will automatically determine a reasonable
* formatting (concerning decimals) for all the values that are drawn inside
* the chart. Use chart.getDefaultValueFormatter() to use the formatter
* calculated by the chart.
*
* @param f
*/
void setValueFormatter(IValueFormatter f);
/**
* Returns the formatter used for drawing the values inside the chart.
*
* @return
*/
IValueFormatter getValueFormatter();
/**
* Returns true if the valueFormatter object of this DataSet is null.
*
* @return
*/
boolean needsFormatter();
/**
* Sets the color the value-labels of this DataSet should have.
*
* @param color
*/
void setValueTextColor(int color);
/**
* Sets a list of colors to be used as the colors for the drawn values.
*
* @param colors
*/
void setValueTextColors(List<Integer> colors);
/**
* Sets a Typeface for the value-labels of this DataSet.
*
* @param tf
*/
void setValueTypeface(Typeface tf);
/**
* Sets the text-size of the value-labels of this DataSet in dp.
*
* @param size
*/
void setValueTextSize(float size);
/**
* Returns only the first color of all colors that are set to be used for the values.
*
* @return
*/
int getValueTextColor();
/**
* Returns the color at the specified index that is used for drawing the values inside the chart.
* Uses modulus internally.
*
* @param index
* @return
*/
int getValueTextColor(int index);
/**
* Returns the typeface that is used for drawing the values inside the chart
*
* @return
*/
Typeface getValueTypeface();
/**
* Returns the text size that is used for drawing the values inside the chart
*
* @return
*/
float getValueTextSize();
/**
* The form to draw for this dataset in the legend.
* <p/>
* Return `DEFAULT` to use the default legend form.
*/
Legend.LegendForm getForm();
/**
* The form size to draw for this dataset in the legend.
* <p/>
* Return `Float.NaN` to use the default legend form size.
*/
float getFormSize();
/**
* The line width for drawing the form of this dataset in the legend
* <p/>
* Return `Float.NaN` to use the default legend form line width.
*/
float getFormLineWidth();
/**
* The line dash path effect used for shapes that consist of lines.
* <p/>
* Return `null` to use the default legend form line dash effect.
*/
DashPathEffect getFormLineDashEffect();
/**
* set this to true to draw y-values on the chart.
*
* NOTE (for bar and line charts): if `maxVisibleCount` is reached, no values will be drawn even
* if this is enabled
* @param enabled
*/
void setDrawValues(boolean enabled);
/**
* Returns true if y-value drawing is enabled, false if not
*
* @return
*/
boolean isDrawValuesEnabled();
/**
* Set this to true to draw y-icons on the chart.
*
* NOTE (for bar and line charts): if `maxVisibleCount` is reached, no icons will be drawn even
* if this is enabled
*
* @param enabled
*/
void setDrawIcons(boolean enabled);
/**
* Returns true if y-icon drawing is enabled, false if not
*
* @return
*/
boolean isDrawIconsEnabled();
/**
* Offset of icons drawn on the chart.
*
* For all charts except Pie and Radar it will be ordinary (x offset,y offset).
*
* For Pie and Radar chart it will be (y offset, distance from center offset); so if you want icon to be rendered under value, you should increase X component of CGPoint, and if you want icon to be rendered closet to center, you should decrease height component of CGPoint.
* @param offset
*/
void setIconsOffset(MPPointF offset);
/**
* Get the offset for drawing icons.
*/
MPPointF getIconsOffset();
/**
* Set the visibility of this DataSet. If not visible, the DataSet will not
* be drawn to the chart upon refreshing it.
*
* @param visible
*/
void setVisible(boolean visible);
/**
* Returns true if this DataSet is visible inside the chart, or false if it
* is currently hidden.
*
* @return
*/
boolean isVisible();
}

View File

@@ -0,0 +1,103 @@
package com.github.mikephil.charting.interfaces.datasets;
import android.graphics.DashPathEffect;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineDataSet;
import com.github.mikephil.charting.formatter.IFillFormatter;
/**
* Created by Philpp Jahoda on 21/10/15.
*/
public interface ILineDataSet extends ILineRadarDataSet<Entry> {
/**
* Returns the drawing mode for this line dataset
*
* @return
*/
LineDataSet.Mode getMode();
/**
* Returns the intensity of the cubic lines (the effect intensity).
* Max = 1f = very cubic, Min = 0.05f = low cubic effect, Default: 0.2f
*
* @return
*/
float getCubicIntensity();
@Deprecated
boolean isDrawCubicEnabled();
@Deprecated
boolean isDrawSteppedEnabled();
/**
* Returns the size of the drawn circles.
*/
float getCircleRadius();
/**
* Returns the hole radius of the drawn circles.
*/
float getCircleHoleRadius();
/**
* Returns the color at the given index of the DataSet's circle-color array.
* Performs a IndexOutOfBounds check by modulus.
*
* @param index
* @return
*/
int getCircleColor(int index);
/**
* Returns the number of colors in this DataSet's circle-color array.
*
* @return
*/
int getCircleColorCount();
/**
* Returns true if drawing circles for this DataSet is enabled, false if not
*
* @return
*/
boolean isDrawCirclesEnabled();
/**
* Returns the color of the inner circle (the circle-hole).
*
* @return
*/
int getCircleHoleColor();
/**
* Returns true if drawing the circle-holes is enabled, false if not.
*
* @return
*/
boolean isDrawCircleHoleEnabled();
/**
* Returns the DashPathEffect that is used for drawing the lines.
*
* @return
*/
DashPathEffect getDashPathEffect();
/**
* Returns true if the dashed-line effect is enabled, false if not.
* If the DashPathEffect object is null, also return false here.
*
* @return
*/
boolean isDashedLineEnabled();
/**
* Returns the IFillFormatter that is set for this DataSet.
*
* @return
*/
IFillFormatter getFillFormatter();
}

View File

@@ -0,0 +1,58 @@
package com.github.mikephil.charting.interfaces.datasets;
import android.graphics.drawable.Drawable;
import com.github.mikephil.charting.data.Entry;
/**
* Created by Philipp Jahoda on 21/10/15.
*/
public interface ILineRadarDataSet<T extends Entry> extends ILineScatterCandleRadarDataSet<T> {
/**
* Returns the color that is used for filling the line surface area.
*
* @return
*/
int getFillColor();
/**
* Returns the drawable used for filling the area below the line.
*
* @return
*/
Drawable getFillDrawable();
/**
* Returns the alpha value that is used for filling the line surface,
* default: 85
*
* @return
*/
int getFillAlpha();
/**
* Returns the stroke-width of the drawn line
*
* @return
*/
float getLineWidth();
/**
* Returns true if filled drawing is enabled, false if not
*
* @return
*/
boolean isDrawFilledEnabled();
/**
* Set to true if the DataSet should be drawn filled (surface), and not just
* as a line, disabling this will give great performance boost. Please note that this method
* uses the canvas.clipPath(...) method for drawing the filled area.
* For devices with API level < 18 (Android 4.3), hardware acceleration of the chart should
* be turned off. Default: false
*
* @param enabled
*/
void setDrawFilled(boolean enabled);
}

View File

@@ -0,0 +1,35 @@
package com.github.mikephil.charting.interfaces.datasets;
import android.graphics.DashPathEffect;
import com.github.mikephil.charting.data.Entry;
/**
* Created by Philipp Jahoda on 21/10/15.
*/
public interface ILineScatterCandleRadarDataSet<T extends Entry> extends IBarLineScatterCandleBubbleDataSet<T> {
/**
* Returns true if vertical highlight indicator lines are enabled (drawn)
* @return
*/
boolean isVerticalHighlightIndicatorEnabled();
/**
* Returns true if vertical highlight indicator lines are enabled (drawn)
* @return
*/
boolean isHorizontalHighlightIndicatorEnabled();
/**
* Returns the line-width in which highlight lines are to be drawn.
* @return
*/
float getHighlightLineWidth();
/**
* Returns the DashPathEffect that is used for highlighting.
* @return
*/
DashPathEffect getDashPathEffectHighlight();
}

View File

@@ -0,0 +1,82 @@
package com.github.mikephil.charting.interfaces.datasets;
import androidx.annotation.Nullable;
import com.github.mikephil.charting.data.PieDataSet;
import com.github.mikephil.charting.data.PieEntry;
/**
* Created by Philipp Jahoda on 03/11/15.
*/
public interface IPieDataSet extends IDataSet<PieEntry> {
/**
* Returns the space that is set to be between the piechart-slices of this
* DataSet, in pixels.
*
* @return
*/
float getSliceSpace();
/**
* When enabled, slice spacing will be 0.0 when the smallest value is going to be
* smaller than the slice spacing itself.
*
* @return
*/
boolean isAutomaticallyDisableSliceSpacingEnabled();
/**
* Returns the distance a highlighted piechart slice is "shifted" away from
* the chart-center in dp.
*
* @return
*/
float getSelectionShift();
PieDataSet.ValuePosition getXValuePosition();
PieDataSet.ValuePosition getYValuePosition();
/**
* When valuePosition is OutsideSlice, indicates line color
* */
int getValueLineColor();
/**
* When valuePosition is OutsideSlice and enabled, line will have the same color as the slice
* */
boolean isUseValueColorForLineEnabled();
/**
* When valuePosition is OutsideSlice, indicates line width
* */
float getValueLineWidth();
/**
* When valuePosition is OutsideSlice, indicates offset as percentage out of the slice size
* */
float getValueLinePart1OffsetPercentage();
/**
* When valuePosition is OutsideSlice, indicates length of first half of the line
* */
float getValueLinePart1Length();
/**
* When valuePosition is OutsideSlice, indicates length of second half of the line
* */
float getValueLinePart2Length();
/**
* When valuePosition is OutsideSlice, this allows variable line length
* */
boolean isValueLineVariableLength();
/**
* Gets the color for the highlighted sector
* */
@Nullable
Integer getHighlightColor();
}

View File

@@ -0,0 +1,30 @@
package com.github.mikephil.charting.interfaces.datasets;
import com.github.mikephil.charting.data.RadarEntry;
/**
* Created by Philipp Jahoda on 03/11/15.
*/
public interface IRadarDataSet extends ILineRadarDataSet<RadarEntry> {
/// flag indicating whether highlight circle should be drawn or not
boolean isDrawHighlightCircleEnabled();
/// Sets whether highlight circle should be drawn or not
void setDrawHighlightCircleEnabled(boolean enabled);
int getHighlightCircleFillColor();
/// The stroke color for highlight circle.
/// If Utils.COLOR_NONE, the color of the dataset is taken.
int getHighlightCircleStrokeColor();
int getHighlightCircleStrokeAlpha();
float getHighlightCircleInnerRadius();
float getHighlightCircleOuterRadius();
float getHighlightCircleStrokeWidth();
}

Some files were not shown because too many files have changed in this diff Show More