Work on improving pie and radar highlighting

This commit is contained in:
Philipp Jahoda 2016-06-12 16:11:37 +02:00
parent 0d9c8545f2
commit 2b886a463e
12 changed files with 259 additions and 190 deletions

View file

@ -8,6 +8,7 @@ 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.data.Entry;
import com.github.mikephil.charting.highlight.BarHighlighter;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider;

View file

@ -516,23 +516,24 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
prepareValuePxMatrix();
}
@Override
protected float[] getMarkerPosition(Entry e, Highlight highlight) {
int dataSetIndex = highlight.getDataSetIndex();
float xPos = e.getX();
float yPos = e.getY() * mAnimator.getPhaseY();
// position of the marker depends on selected value index and value
float[] pts = new float[]{
xPos, yPos
};
getTransformer(mData.getDataSetByIndex(dataSetIndex).getAxisDependency())
.pointValuesToPixel(pts);
return pts;
}
// @Override
// protected float[] getMarkerPosition(Highlight high) {
// return new float[] { high.getXPx(), high.getYPx() };
//
// int dataSetIndex = highlight.getDataSetIndex();
// float xPos = e.getX();
// float yPos = e.getY() * mAnimator.getPhaseY();
//
// // position of the marker depends on selected value index and value
// float[] pts = new float[]{
// xPos, yPos
// };
//
// getTransformer(mData.getDataSetByIndex(dataSetIndex).getAxisDependency())
// .pointValuesToPixel(pts);
//
// return pts;
// }
/**
* draws the grid background
@ -1180,24 +1181,6 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
mKeepPositionOnRotation = keepPositionOnRotation;
}
/**
* Returns the Highlight object (contains x-index and DataSet index) of the
* selected value at the given touch point inside the Line-, Scatter-, or
* CandleStick-Chart.
*
* @param x
* @param y
* @return
*/
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
return getHighlighter().getHighlight(x, y);
}
/**
* Returns the x and y values in the chart at the given touch point
* (encapsulated in a PointD). This method transforms pixel coordinates to

View file

@ -664,6 +664,24 @@ public abstract class Chart<T extends ChartData<? extends IDataSet<? extends Ent
highlightValue(high, true);
}
/**
* Returns the Highlight object (contains x-index and DataSet index) of the
* selected value at the given touch point inside the Line-, Scatter-, or
* CandleStick-Chart.
*
* @param x
* @param y
* @return
*/
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
return getHighlighter().getHighlight(x, y);
}
/**
* Set a new (e.g. custom) ChartTouchListener NOTE: make sure to
* setTouchEnabled(true); if you need touch gestures on the chart
@ -711,7 +729,7 @@ public abstract class Chart<T extends ChartData<? extends IDataSet<? extends Ent
if (e == null || entryIndex > set.getEntryCount() * mAnimator.getPhaseX())
continue;
float[] pos = getMarkerPosition(e, highlight);
float[] pos = getMarkerPosition(highlight);
// check bounds
if (!mViewPortHandler.isInBounds(pos[0], pos[1]))
@ -736,13 +754,14 @@ public abstract class Chart<T extends ChartData<? extends IDataSet<? extends Ent
/**
* Returns the actual position in pixels of the MarkerView for the given
* Entry in the given DataSet.
* Highlight object.
*
* @param e
* @param highlight
* @param high
* @return
*/
protected abstract float[] getMarkerPosition(Entry e, Highlight highlight);
protected float[] getMarkerPosition(Highlight high) {
return new float[]{high.getXPx(), high.getYPx()};
}
/**
* ################ ################ ################ ################

View file

@ -14,6 +14,7 @@ import com.github.mikephil.charting.data.DataSet;
import com.github.mikephil.charting.data.Entry;
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.Utils;
@ -111,6 +112,8 @@ public class PieChart extends PieRadarChartBase<PieData> {
mRenderer = new PieChartRenderer(this, mAnimator, mViewPortHandler);
mXAxis = null;
mHighlighter = new PieHighlighter(this);
}
@Override
@ -165,7 +168,7 @@ public class PieChart extends PieRadarChartBase<PieData> {
}
@Override
protected float[] getMarkerPosition(Entry e, Highlight highlight) {
protected float[] getMarkerPosition(Highlight highlight) {
PointF center = getCenterCircleBox();
float r = getRadius();
@ -233,24 +236,21 @@ public class PieChart extends PieRadarChartBase<PieData> {
}
/**
* checks if the given index in the given DataSet is set for highlighting or
* not
* Checks if the given index is set to be highlighted.
*
* @param xIndex
* @param dataSetIndex
* @param index
* @return
*/
public boolean needsHighlight(int xIndex, int dataSetIndex) {
public boolean needsHighlight(int index) {
// no highlight
if (!valuesToHighlight() || dataSetIndex < 0)
if (!valuesToHighlight())
return false;
for (int i = 0; i < mIndicesToHighlight.length; i++)
// check if the xvalue for the given dataset needs highlight
if (mIndicesToHighlight[i].getX() == xIndex
&& mIndicesToHighlight[i].getDataSetIndex() == dataSetIndex)
if ((int) mIndicesToHighlight[i].getX() == index)
return true;
return false;

View file

@ -22,9 +22,6 @@ import com.github.mikephil.charting.interfaces.datasets.IDataSet;
import com.github.mikephil.charting.listener.PieRadarChartTouchListener;
import com.github.mikephil.charting.utils.Utils;
import java.util.ArrayList;
import java.util.List;
/**
* Baseclass of PieChart and RadarChart.
*
@ -291,7 +288,7 @@ public abstract class PieRadarChartBase<T extends ChartData<? extends IDataSet<?
* @param angle in degrees, converted to radians internally
* @return
*/
protected PointF getPosition(PointF center, float dist, float angle) {
public PointF getPosition(PointF center, float dist, float angle) {
PointF p = new PointF((float) (center.x + dist * Math.cos(Math.toRadians(angle))),
(float) (center.y + dist * Math.sin(Math.toRadians(angle))));
@ -456,33 +453,6 @@ public abstract class PieRadarChartBase<T extends ChartData<? extends IDataSet<?
return 0;
}
/**
* Returns an array of Highlight objects for the given x-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.
*
* @return
*/
public List<Highlight> getSelectionDetailsAtIndex(int xIndex) {
List<Highlight> vals = new ArrayList<Highlight>();
for (int i = 0; i < mData.getDataSetCount(); i++) {
IDataSet<?> dataSet = mData.getDataSetByIndex(i);
// extract all y-values from all DataSets at the given x-index
final float yVal = dataSet.getYValueForXValue(xIndex);
if (Float.isNaN(yVal))
continue;
vals.add(new Highlight(0f, yVal, 0f, 0f, i, dataSet.getAxisDependency()));
}
return vals;
}
/**
* ################ ################ ################ ################
*/

View file

@ -13,6 +13,7 @@ import com.github.mikephil.charting.components.YAxis.AxisDependency;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.RadarData;
import com.github.mikephil.charting.highlight.Highlight;
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;
@ -93,6 +94,8 @@ public class RadarChart extends PieRadarChartBase<RadarData> {
mRenderer = new RadarChartRenderer(this, mAnimator, mViewPortHandler);
mYAxisRenderer = new YAxisRendererRadarChart(mViewPortHandler, mYAxis, this);
mXAxisRenderer = new XAxisRendererRadarChart(mViewPortHandler, mXAxis, this);
mHighlighter = new RadarHighlighter(this);
}
@Override
@ -103,20 +106,21 @@ public class RadarChart extends PieRadarChartBase<RadarData> {
mXAxis.calculate(0, mData.getMaxEntryCountSet().getEntryCount());
}
@Override
protected float[] getMarkerPosition(Entry e, Highlight highlight) {
float angle = getSliceAngle() * e.getX() * mAnimator.getPhaseX() + getRotationAngle();
float val = e.getY() * getFactor() * mAnimator.getPhaseY();
PointF c = getCenterOffsets();
PointF p = new PointF((float) (c.x + val * Math.cos(Math.toRadians(angle))),
(float) (c.y + val * Math.sin(Math.toRadians(angle))));
return new float[]{
p.x, p.y
};
}
// @Override
// protected float[] getMarkerPosition(Highlight highlight) {
// return null;
//
//// float angle = getSliceAngle() * e.getX() * mAnimator.getPhaseX() + getRotationAngle();
//// float val = e.getY() * getFactor() * mAnimator.getPhaseY();
//// PointF c = getCenterOffsets();
////
//// PointF p = new PointF((float) (c.x + val * Math.cos(Math.toRadians(angle))),
//// (float) (c.y + val * Math.sin(Math.toRadians(angle))));
////
//// return new float[]{
//// p.x, p.y
//// };
// }
@Override
public void notifyDataSetChanged() {
@ -197,7 +201,7 @@ public class RadarChart extends PieRadarChartBase<RadarData> {
float sliceangle = getSliceAngle();
for (int i = 0; i < mData.getEntryCount(); i++) {
for (int i = 0; i < mData.getMaxEntryCountSet().getEntryCount(); i++) {
if (sliceangle * (i + 1) - sliceangle / 2f > a)
return i;
}

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 android.graphics.PointF;
import com.github.mikephil.charting.charts.PieChart;
import com.github.mikephil.charting.charts.PieRadarChartBase;
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.Utils;
import java.util.ArrayList;
import java.util.List;
/**
* Created by philipp on 12/06/16.
*/
public abstract class PieRadarHighlighter<T extends PieRadarChartBase> implements Highlighter {
protected T mChart;
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,85 @@
package com.github.mikephil.charting.highlight;
import android.graphics.PointF;
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.Utils;
import java.util.ArrayList;
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) {
List<Highlight> vals = new ArrayList<Highlight>();
float phaseX = mChart.getAnimator().getPhaseX();
float phaseY = mChart.getAnimator().getPhaseY();
float sliceangle = mChart.getSliceAngle();
float factor = mChart.getFactor();
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());
if (Float.isNaN(y))
continue;
PointF p = Utils.getPosition(
mChart.getCenterOffsets(),
y * factor * phaseY,
sliceangle * index * phaseX + mChart.getRotationAngle());
vals.add(new Highlight(entry.getX(), entry.getY(), p.x, p.y, i, dataSet.getAxisDependency()));
}
return vals;
}
}

View file

@ -7,14 +7,11 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AnimationUtils;
import com.github.mikephil.charting.charts.PieChart;
import com.github.mikephil.charting.charts.PieRadarChartBase;
import com.github.mikephil.charting.charts.RadarChart;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.utils.Utils;
import java.util.ArrayList;
import java.util.List;
/**
* Touchlistener for the PieChart.
@ -150,62 +147,8 @@ public class PieRadarChartTouchListener extends ChartTouchListener<PieRadarChart
return false;
}
float distance = mChart.distanceToCenter(e.getX(), e.getY());
// check if a slice was touched
if (distance > mChart.getRadius()) {
// if no slice was touched, highlight nothing
if (mLastHighlighted == null)
mChart.highlightValues(null); // no listener callback
else
mChart.highlightTouch(null); // listener callback
mLastHighlighted = null;
} else {
float angle = mChart.getAngleForPoint(e.getX(), e.getY());
if (mChart instanceof PieChart) {
angle /= mChart.getAnimator().getPhaseY();
}
int index = mChart.getIndexForAngle(angle);
// check if the index could be found
if (index < 0) {
mChart.highlightValues(null);
mLastHighlighted = null;
} else {
List<Highlight> valsAtIndex = mChart.getSelectionDetailsAtIndex(index);
int dataSetIndex = 0;
// get the dataset that is closest to the selection (PieChart
// only
// has one DataSet)
if (mChart instanceof RadarChart) {
dataSetIndex = Utils.getClosestDataSetIndexByValue(
valsAtIndex,
distance / ((RadarChart) mChart).getFactor(),
null);
}
if (dataSetIndex < 0) {
mChart.highlightValues(null);
mLastHighlighted = null;
} else {
Highlight h = new Highlight(index, dataSetIndex);
performHighlight(h, e);
}
}
}
Highlight high = mChart.getHighlightByTouchPoint(e.getX(), e.getY());
performHighlight(high, e);
return true;
}

View file

@ -233,8 +233,7 @@ public class PieChartRenderer extends DataRenderer {
// draw only if the value is greater than zero
if ((Math.abs(e.getY()) > 0.000001)) {
if (!mChart.needsHighlight((int) e.getX(),
mChart.getData().getIndexOfDataSet(dataSet))) {
if (!mChart.needsHighlight(j)) {
final boolean accountForSliceSpacing = sliceSpace > 0.f && sliceAngle <= 180.f;
@ -712,17 +711,15 @@ public class PieChartRenderer extends DataRenderer {
for (int i = 0; i < indices.length; i++) {
// get the index to highlight
float x = indices[i].getX();
int index = (int) indices[i].getX();
if (x >= drawAngles.length)
if (index >= drawAngles.length)
continue;
IPieDataSet set = mChart.getData()
.getDataSetByIndex(indices[i]
.getDataSetIndex());
int entryIndex = set.getEntryIndex(x, DataSet.Rounding.CLOSEST);
if (set == null || !set.isHighlightEnabled())
continue;
@ -735,14 +732,14 @@ public class PieChartRenderer extends DataRenderer {
}
}
if (x == 0)
if (index == 0)
angle = 0.f;
else
angle = absoluteAngles[entryIndex - 1] * phaseX;
angle = absoluteAngles[index - 1] * phaseX;
final float sliceSpace = visibleAngleCount <= 1 ? 0.f : set.getSliceSpace();
float sliceAngle = drawAngles[entryIndex];
float sliceAngle = drawAngles[index];
float innerRadius = userInnerRadius;
float shift = set.getSelectionShift();
@ -752,7 +749,7 @@ public class PieChartRenderer extends DataRenderer {
final boolean accountForSliceSpacing = sliceSpace > 0.f && sliceAngle <= 180.f;
mRenderPaint.setColor(set.getColor(entryIndex));
mRenderPaint.setColor(set.getColor(index));
final float sliceSpaceAngleOuter = visibleAngleCount == 1 ?
0.f :

View file

@ -239,14 +239,6 @@ public class RadarChartRenderer extends LineRadarRenderer {
@Override
public void drawHighlighted(Canvas c, Highlight[] indices) {
float phaseX = mAnimator.getPhaseX();
float phaseY = mAnimator.getPhaseY();
float sliceangle = mChart.getSliceAngle();
float factor = mChart.getFactor();
PointF center = mChart.getCenterOffsets();
for (int i = 0; i < indices.length; i++) {
IRadarDataSet set = mChart.getData()
@ -256,24 +248,8 @@ public class RadarChartRenderer extends LineRadarRenderer {
if (set == null || !set.isHighlightEnabled())
continue;
// get the index to highlight
float x = indices[i].getX();
Entry e = set.getEntryForXPos(x);
if (e == null || e.getX() != x)
continue;
int j = set.getEntryIndex(e);
float y = (e.getY() - mChart.getYChartMin());
if (Float.isNaN(y))
continue;
PointF p = Utils.getPosition(
center,
y * factor * phaseY,
sliceangle * j * phaseX + mChart.getRotationAngle());
Highlight high = indices[i];
PointF p = new PointF(high.getXPx(), high.getYPx());
// draw the lines
drawHighlightLines(c, p.x, p.y, set);