Merge pull request #4787 from danielgindi/feature/catching_up_to_ios

Feature/catching up to ios
This commit is contained in:
Daniel Cohen Gindi 2020-01-23 15:38:53 +02:00 committed by GitHub
commit f8d068d377
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 1083 additions and 232 deletions

View file

@ -0,0 +1,288 @@
package com.xxmassdeveloper.mpchartexample;
import android.annotation.SuppressLint;
import android.graphics.RectF;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.WindowManager;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;
import com.github.mikephil.charting.charts.HorizontalBarChart;
import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.XAxis.XAxisPosition;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.BarData;
import com.github.mikephil.charting.data.BarDataSet;
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.interfaces.datasets.IBarDataSet;
import com.github.mikephil.charting.listener.OnChartValueSelectedListener;
import com.github.mikephil.charting.utils.MPPointF;
import com.xxmassdeveloper.mpchartexample.notimportant.DemoBase;
import java.util.ArrayList;
import java.util.List;
public class HorizontalBarNegativeChartActivity extends DemoBase implements OnSeekBarChangeListener,
OnChartValueSelectedListener {
protected HorizontalBarChart mChart;
private SeekBar mSeekBarX, mSeekBarY;
private TextView tvX, tvY;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_horizontalbarchart);
tvX = findViewById(R.id.tvXMax);
tvY = (TextView) findViewById(R.id.tvYMax);
mSeekBarX = (SeekBar) findViewById(R.id.seekBar1);
mSeekBarY = (SeekBar) findViewById(R.id.seekBar2);
mChart = (HorizontalBarChart) findViewById(R.id.chart1);
mChart.setOnChartValueSelectedListener(this);
// mChart.setHighlightEnabled(false);
mChart.setDrawBarShadow(false);
mChart.setDrawValueAboveBar(true);
mChart.getDescription().setEnabled(false);
// if more than 60 entries are displayed in the chart, no values will be
// drawn
mChart.setMaxVisibleValueCount(60);
// scaling can now only be done on x- and y-axis separately
mChart.setPinchZoom(false);
// draw shadows for each bar that show the maximum value
// mChart.setDrawBarShadow(true);
mChart.setDrawGridBackground(false);
XAxis xl = mChart.getXAxis();
xl.setPosition(XAxisPosition.BOTTOM);
xl.setTypeface(mTfLight);
xl.setDrawAxisLine(true);
xl.setDrawGridLines(false);
xl.setGranularity(10f);
YAxis yl = mChart.getAxisLeft();
yl.setTypeface(mTfLight);
yl.setDrawAxisLine(true);
yl.setDrawGridLines(true);
yl.setDrawZeroLine(true); // draw a zero line
// yl.setInverted(true);
YAxis yr = mChart.getAxisRight();
yr.setTypeface(mTfLight);
yr.setDrawAxisLine(true);
yr.setDrawGridLines(false);
// yr.setInverted(true);
setData(12, 50);
mChart.setFitBars(true);
mChart.animateY(2500);
// setting data
mSeekBarY.setProgress(50);
mSeekBarX.setProgress(12);
mSeekBarY.setOnSeekBarChangeListener(this);
mSeekBarX.setOnSeekBarChangeListener(this);
Legend l = mChart.getLegend();
l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM);
l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT);
l.setOrientation(Legend.LegendOrientation.HORIZONTAL);
l.setDrawInside(false);
l.setFormSize(8f);
l.setXEntrySpace(4f);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.bar, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.actionToggleValues: {
List<IBarDataSet> sets = mChart.getData()
.getDataSets();
for (IBarDataSet iSet : sets) {
IBarDataSet set = (BarDataSet) iSet;
set.setDrawValues(!set.isDrawValuesEnabled());
}
mChart.invalidate();
break;
}
case R.id.actionToggleIcons: {
List<IBarDataSet> sets = mChart.getData()
.getDataSets();
for (IBarDataSet iSet : sets) {
IBarDataSet set = (BarDataSet) iSet;
set.setDrawIcons(!set.isDrawIconsEnabled());
}
mChart.invalidate();
break;
}
case R.id.actionToggleHighlight: {
if(mChart.getData() != null) {
mChart.getData().setHighlightEnabled(!mChart.getData().isHighlightEnabled());
mChart.invalidate();
}
break;
}
case R.id.actionTogglePinch: {
if (mChart.isPinchZoomEnabled())
mChart.setPinchZoom(false);
else
mChart.setPinchZoom(true);
mChart.invalidate();
break;
}
case R.id.actionToggleAutoScaleMinMax: {
mChart.setAutoScaleMinMaxEnabled(!mChart.isAutoScaleMinMaxEnabled());
mChart.notifyDataSetChanged();
break;
}
case R.id.actionToggleBarBorders: {
for (IBarDataSet set : mChart.getData().getDataSets())
((BarDataSet)set).setBarBorderWidth(set.getBarBorderWidth() == 1.f ? 0.f : 1.f);
mChart.invalidate();
break;
}
case R.id.animateX: {
mChart.animateX(3000);
break;
}
case R.id.animateY: {
mChart.animateY(3000);
break;
}
case R.id.animateXY: {
mChart.animateXY(3000, 3000);
break;
}
case R.id.actionSave: {
if (mChart.saveToGallery("title" + System.currentTimeMillis(), 50)) {
Toast.makeText(getApplicationContext(), "Saving SUCCESSFUL!",
Toast.LENGTH_SHORT).show();
} else
Toast.makeText(getApplicationContext(), "Saving FAILED!", Toast.LENGTH_SHORT)
.show();
break;
}
}
return true;
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
tvX.setText("" + (mSeekBarX.getProgress() + 1));
tvY.setText("" + (mSeekBarY.getProgress()));
setData(mSeekBarX.getProgress() + 1, mSeekBarY.getProgress());
mChart.setFitBars(true);
mChart.invalidate();
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
private void setData(int count, float range) {
float barWidth = 9f;
float spaceForBar = 10f;
ArrayList<BarEntry> yVals1 = new ArrayList<BarEntry>();
for (int i = 0; i < count; i++) {
float val = (float) (Math.random() * range - range / 2);
yVals1.add(new BarEntry(i * spaceForBar, val,
getResources().getDrawable(R.drawable.star)));
}
BarDataSet set1;
if (mChart.getData() != null &&
mChart.getData().getDataSetCount() > 0) {
set1 = (BarDataSet)mChart.getData().getDataSetByIndex(0);
set1.setValues(yVals1);
mChart.getData().notifyDataChanged();
mChart.notifyDataSetChanged();
} else {
set1 = new BarDataSet(yVals1, "DataSet 1");
set1.setDrawIcons(false);
ArrayList<IBarDataSet> dataSets = new ArrayList<IBarDataSet>();
dataSets.add(set1);
BarData data = new BarData(dataSets);
data.setValueTextSize(10f);
data.setValueTypeface(mTfLight);
data.setBarWidth(barWidth);
mChart.setData(data);
}
}
protected RectF mOnValueSelectedRectF = new RectF();
@SuppressLint("NewApi")
@Override
public void onValueSelected(Entry e, Highlight h) {
if (e == null)
return;
RectF bounds = mOnValueSelectedRectF;
mChart.getBarBounds((BarEntry) e, bounds);
MPPointF position = mChart.getPosition(e, mChart.getData().getDataSetByIndex(h.getDataSetIndex())
.getAxisDependency());
Log.i("bounds", bounds.toString());
Log.i("position", position.toString());
MPPointF.recycleInstance(position);
}
@Override
public void onNothingSelected() {
};
}

View file

@ -24,6 +24,7 @@
<activity android:name="LineChartTime" />
<activity android:name="BarChartActivity" />
<activity android:name="HorizontalBarChartActivity" />
<activity android:name="HorizontalBarNegativeChartActivity" />
<activity android:name="PieChartActivity" />
<activity android:name="PiePolylineChartActivity" />
<activity android:name="MultiLineChartActivity" />

View file

@ -156,7 +156,6 @@ public class PiePolylineChartActivity extends DemoBase implements OnSeekBarChang
dataSet.setValueLinePart1OffsetPercentage(80.f);
dataSet.setValueLinePart1Length(0.2f);
dataSet.setValueLinePart2Length(0.4f);
//dataSet.setUsingSliceColorAsValueLineColor(true);
//dataSet.setXValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE);
dataSet.setYValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE);

View file

@ -26,6 +26,7 @@ import com.xxmassdeveloper.mpchartexample.DynamicalAddingActivity;
import com.xxmassdeveloper.mpchartexample.FilledLineActivity;
import com.xxmassdeveloper.mpchartexample.HalfPieChartActivity;
import com.xxmassdeveloper.mpchartexample.HorizontalBarChartActivity;
import com.xxmassdeveloper.mpchartexample.HorizontalBarNegativeChartActivity;
import com.xxmassdeveloper.mpchartexample.InvertedLineChartActivity;
import com.xxmassdeveloper.mpchartexample.LineChartActivity1;
import com.xxmassdeveloper.mpchartexample.LineChartActivity2;
@ -87,40 +88,41 @@ public class MainActivity extends AppCompatActivity implements OnItemClickListen
objects.add(13, new ContentItem("Horizontal", "Render bar chart horizontally."));
objects.add(14, new ContentItem("Stacked", "Stacked bar chart."));
objects.add(15, new ContentItem("Negative", "Positive and negative values with unique colors."));
objects.add(16, new ContentItem("Stacked 2", "Stacked bar chart with negative values."));
objects.add(17, new ContentItem("Sine", "Sine function in bar chart format."));
objects.add(16, new ContentItem("Negative Horizontal", "demonstrates how to create a HorizontalBarChart with positive and negative values."));
objects.add(17, new ContentItem("Stacked 2", "Stacked bar chart with negative values."));
objects.add(18, new ContentItem("Sine", "Sine function in bar chart format."));
////
objects.add(18, new ContentItem("Pie Charts"));
objects.add(19, new ContentItem("Pie Charts"));
objects.add(19, new ContentItem("Basic", "Simple pie chart."));
objects.add(20, new ContentItem("Value Lines", "Stylish lines drawn outward from slices."));
objects.add(21, new ContentItem("Half Pie", "180° (half) pie chart."));
objects.add(20, new ContentItem("Basic", "Simple pie chart."));
objects.add(21, new ContentItem("Value Lines", "Stylish lines drawn outward from slices."));
objects.add(22, new ContentItem("Half Pie", "180° (half) pie chart."));
////
objects.add(22, new ContentItem("Other Charts"));
objects.add(23, new ContentItem("Other Charts"));
objects.add(23, new ContentItem("Combined Chart", "Bar and line chart together."));
objects.add(24, new ContentItem("Scatter Plot", "Simple scatter plot."));
objects.add(25, new ContentItem("Bubble Chart", "Simple bubble chart."));
objects.add(26, new ContentItem("Candlestick", "Simple financial chart."));
objects.add(27, new ContentItem("Radar Chart", "Simple web chart."));
objects.add(24, new ContentItem("Combined Chart", "Bar and line chart together."));
objects.add(25, new ContentItem("Scatter Plot", "Simple scatter plot."));
objects.add(26, new ContentItem("Bubble Chart", "Simple bubble chart."));
objects.add(27, new ContentItem("Candlestick", "Simple financial chart."));
objects.add(28, new ContentItem("Radar Chart", "Simple web chart."));
////
objects.add(28, new ContentItem("Scrolling Charts"));
objects.add(29, new ContentItem("Scrolling Charts"));
objects.add(29, new ContentItem("Multiple", "Various types of charts as fragments."));
objects.add(30, new ContentItem("View Pager", "Swipe through different charts."));
objects.add(31, new ContentItem("Tall Bar Chart", "Bars bigger than your screen!"));
objects.add(32, new ContentItem("Many Bar Charts", "More bars than your screen can handle!"));
objects.add(30, new ContentItem("Multiple", "Various types of charts as fragments."));
objects.add(31, new ContentItem("View Pager", "Swipe through different charts."));
objects.add(32, new ContentItem("Tall Bar Chart", "Bars bigger than your screen!"));
objects.add(33, new ContentItem("Many Bar Charts", "More bars than your screen can handle!"));
////
objects.add(33, new ContentItem("Even More Line Charts"));
objects.add(34, new ContentItem("Even More Line Charts"));
objects.add(34, new ContentItem("Dynamic", "Build a line chart by adding points and sets."));
objects.add(35, new ContentItem("Realtime", "Add data points in realtime."));
objects.add(36, new ContentItem("Hourly", "Uses the current time to add a data point for each hour."));
//objects.add(37, new ContentItem("Realm.io Examples", "See more examples that use Realm.io mobile database."));
objects.add(35, new ContentItem("Dynamic", "Build a line chart by adding points and sets."));
objects.add(36, new ContentItem("Realtime", "Add data points in realtime."));
objects.add(37, new ContentItem("Hourly", "Uses the current time to add a data point for each hour."));
//objects.add(38, new ContentItem("Realm.io Examples", "See more examples that use Realm.io mobile database."));
MyAdapter adapter = new MyAdapter(this, objects);
@ -179,57 +181,60 @@ public class MainActivity extends AppCompatActivity implements OnItemClickListen
i = new Intent(this, BarChartPositiveNegative.class);
break;
case 16:
i = new Intent(this, StackedBarActivityNegative.class);
i = new Intent(this, HorizontalBarNegativeChartActivity.class);
break;
case 17:
i = new Intent(this, StackedBarActivityNegative.class);
break;
case 18:
i = new Intent(this, BarChartActivitySinus.class);
break;
case 19:
case 20:
i = new Intent(this, PieChartActivity.class);
break;
case 20:
case 21:
i = new Intent(this, PiePolylineChartActivity.class);
break;
case 21:
case 22:
i = new Intent(this, HalfPieChartActivity.class);
break;
case 23:
case 24:
i = new Intent(this, CombinedChartActivity.class);
break;
case 24:
case 25:
i = new Intent(this, ScatterChartActivity.class);
break;
case 25:
case 26:
i = new Intent(this, BubbleChartActivity.class);
break;
case 26:
case 27:
i = new Intent(this, CandleStickChartActivity.class);
break;
case 27:
case 28:
i = new Intent(this, RadarChartActivity.class);
break;
case 29:
case 30:
i = new Intent(this, ListViewMultiChartActivity.class);
break;
case 30:
case 31:
i = new Intent(this, SimpleChartDemo.class);
break;
case 31:
case 32:
i = new Intent(this, ScrollViewActivity.class);
break;
case 32:
case 33:
i = new Intent(this, ListViewBarChartActivity.class);
break;
case 34:
case 35:
i = new Intent(this, DynamicalAddingActivity.class);
break;
case 35:
case 36:
i = new Intent(this, RealtimeLineChartActivity.class);
break;
case 36:
case 37:
i = new Intent(this, LineChartTime.class);
break;
/*case 37:
/*case 38:
i = new Intent(this, RealmMainActivity.class);
break;*/
}

View file

@ -100,6 +100,8 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
protected boolean mClipValuesToContent = false;
protected boolean mClipDataToContent = true;
/**
* Sets the minimum offset (padding) around the chart, defaults to 15
*/
@ -230,9 +232,12 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
if (mAxisRight.isEnabled() && mAxisRight.isDrawLimitLinesBehindDataEnabled())
mAxisRendererRight.renderLimitLines(canvas);
// make sure the data cannot be drawn outside the content-rect
int clipRestoreCount = canvas.save();
canvas.clipRect(mViewPortHandler.getContentRect());
if (isClipDataToContentEnabled()) {
// make sure the data cannot be drawn outside the content-rect
canvas.clipRect(mViewPortHandler.getContentRect());
}
mRenderer.drawData(canvas);
@ -394,66 +399,70 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
offsets.top = 0.f;
offsets.bottom = 0.f;
// setup offsets for legend
if (mLegend != null && mLegend.isEnabled() && !mLegend.isDrawInsideEnabled()) {
switch (mLegend.getOrientation()) {
case VERTICAL:
if (mLegend == null || !mLegend.isEnabled() || mLegend.isDrawInsideEnabled())
return;
switch (mLegend.getHorizontalAlignment()) {
case LEFT:
offsets.left += Math.min(mLegend.mNeededWidth,
mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent())
+ mLegend.getXOffset();
break;
switch (mLegend.getOrientation()) {
case VERTICAL:
case RIGHT:
offsets.right += Math.min(mLegend.mNeededWidth,
mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent())
+ mLegend.getXOffset();
break;
switch (mLegend.getHorizontalAlignment()) {
case LEFT:
offsets.left += Math.min(mLegend.mNeededWidth,
mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent())
+ mLegend.getXOffset();
break;
case CENTER:
case RIGHT:
offsets.right += Math.min(mLegend.mNeededWidth,
mViewPortHandler.getChartWidth() * mLegend.getMaxSizePercent())
+ mLegend.getXOffset();
break;
switch (mLegend.getVerticalAlignment()) {
case TOP:
offsets.top += Math.min(mLegend.mNeededHeight,
mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent())
+ mLegend.getYOffset();
break;
case CENTER:
case BOTTOM:
offsets.bottom += Math.min(mLegend.mNeededHeight,
mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent())
+ mLegend.getYOffset();
break;
switch (mLegend.getVerticalAlignment()) {
case TOP:
offsets.top += Math.min(mLegend.mNeededHeight,
mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent())
+ mLegend.getYOffset();
break;
default:
break;
}
}
case BOTTOM:
offsets.bottom += Math.min(mLegend.mNeededHeight,
mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent())
+ mLegend.getYOffset();
break;
break;
default:
break;
}
}
case HORIZONTAL:
break;
switch (mLegend.getVerticalAlignment()) {
case TOP:
offsets.top += Math.min(mLegend.mNeededHeight,
mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent())
+ mLegend.getYOffset();
break;
case HORIZONTAL:
case BOTTOM:
offsets.bottom += Math.min(mLegend.mNeededHeight,
mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent())
+ mLegend.getYOffset();
break;
switch (mLegend.getVerticalAlignment()) {
case TOP:
offsets.top += Math.min(mLegend.mNeededHeight,
mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent())
+ mLegend.getYOffset();
default:
break;
}
break;
}
break;
case BOTTOM:
offsets.bottom += Math.min(mLegend.mNeededHeight,
mViewPortHandler.getChartHeight() * mLegend.getMaxSizePercent())
+ mLegend.getYOffset();
break;
default:
break;
}
break;
}
}
@ -1224,6 +1233,17 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
mClipValuesToContent = enabled;
}
/**
* When disabled, the data and/or highlights will not be clipped to contentRect. Disabling this option can
* be useful, when the data lies fully within the content rect, but is drawn in such a way (such as thick lines)
* that there is unwanted clipping.
*
* @param enabled
*/
public void setClipDataToContent(boolean enabled) {
mClipDataToContent = enabled;
}
/**
* When enabled, the values will be clipped to contentRect,
* otherwise they can bleed outside the content rect.
@ -1234,6 +1254,17 @@ public abstract class BarLineChartBase<T extends BarLineScatterCandleBubbleData<
return mClipValuesToContent;
}
/**
* When disabled, the data and/or highlights will not be clipped to contentRect. Disabling this option can
* be useful, when the data lies fully within the content rect, but is drawn in such a way (such as thick lines)
* that there is unwanted clipping.
*
* @return
*/
public boolean isClipDataToContentEnabled() {
return mClipDataToContent;
}
/**
* Sets the width of the border lines in dp.
*

View file

@ -398,8 +398,23 @@ public abstract class Chart<T extends ChartData<? extends IDataSet<? extends Ent
boolean hasText = !TextUtils.isEmpty(mNoDataText);
if (hasText) {
MPPointF c = getCenter();
canvas.drawText(mNoDataText, c.x, c.y, mInfoPaint);
MPPointF pt = getCenter();
switch (mInfoPaint.getTextAlign()) {
case LEFT:
pt.x = 0;
canvas.drawText(mNoDataText, pt.x, pt.y, mInfoPaint);
break;
case RIGHT:
pt.x *= 2.0;
canvas.drawText(mNoDataText, pt.x, pt.y, mInfoPaint);
break;
default:
canvas.drawText(mNoDataText, pt.x, pt.y, mInfoPaint);
break;
}
}
return;
@ -548,6 +563,18 @@ public abstract class Chart<T extends ChartData<? extends IDataSet<? extends Ent
invalidate();
}
/**
* Highlights any y-value at the given x-value in the given DataSet.
* Provide -1 as the dataSetIndex to undo all highlighting.
* This method will call the listener.
* @param x The x-value to highlight
* @param dataSetIndex The dataset index to search in
* @param dataIndex The data index to search in (only used in CombinedChartView currently)
*/
public void highlightValue(float x, int dataSetIndex, int dataIndex) {
highlightValue(x, dataSetIndex, dataIndex, true);
}
/**
* Highlights any y-value at the given x-value in the given DataSet.
* Provide -1 as the dataSetIndex to undo all highlighting.
@ -556,7 +583,20 @@ public abstract class Chart<T extends ChartData<? extends IDataSet<? extends Ent
* @param dataSetIndex The dataset index to search in
*/
public void highlightValue(float x, int dataSetIndex) {
highlightValue(x, dataSetIndex, true);
highlightValue(x, dataSetIndex, -1, true);
}
/**
* Highlights the value at the given x-value and y-value in the given DataSet.
* Provide -1 as the dataSetIndex to undo all highlighting.
* This method will call the listener.
* @param x The x-value to highlight
* @param y The y-value to highlight. Supply `NaN` for "any"
* @param dataSetIndex The dataset index to search in
* @param dataIndex The data index to search in (only used in CombinedChartView currently)
*/
public void highlightValue(float x, float y, int dataSetIndex, int dataIndex) {
highlightValue(x, y, dataSetIndex, dataIndex, true);
}
/**
@ -568,7 +608,19 @@ public abstract class Chart<T extends ChartData<? extends IDataSet<? extends Ent
* @param dataSetIndex The dataset index to search in
*/
public void highlightValue(float x, float y, int dataSetIndex) {
highlightValue(x, y, dataSetIndex, true);
highlightValue(x, y, dataSetIndex, -1, true);
}
/**
* Highlights any y-value at the given x-value in the given DataSet.
* Provide -1 as the dataSetIndex to undo all highlighting.
* @param x The x-value to highlight
* @param dataSetIndex The dataset index to search in
* @param dataIndex The data index to search in (only used in CombinedChartView currently)
* @param callListener Should the listener be called for this change
*/
public void highlightValue(float x, int dataSetIndex, int dataIndex, boolean callListener) {
highlightValue(x, Float.NaN, dataSetIndex, dataIndex, callListener);
}
/**
@ -579,7 +631,25 @@ public abstract class Chart<T extends ChartData<? extends IDataSet<? extends Ent
* @param callListener Should the listener be called for this change
*/
public void highlightValue(float x, int dataSetIndex, boolean callListener) {
highlightValue(x, Float.NaN, dataSetIndex, callListener);
highlightValue(x, Float.NaN, dataSetIndex, -1, callListener);
}
/**
* Highlights any y-value at the given x-value in the given DataSet.
* Provide -1 as the dataSetIndex to undo all highlighting.
* @param x The x-value to highlight
* @param y The y-value to highlight. Supply `NaN` for "any"
* @param dataSetIndex The dataset index to search in
* @param dataIndex The data index to search in (only used in CombinedChartView currently)
* @param callListener Should the listener be called for this change
*/
public void highlightValue(float x, float y, int dataSetIndex, int dataIndex, boolean callListener) {
if (dataSetIndex < 0 || dataSetIndex >= mData.getDataSetCount()) {
highlightValue(null, callListener);
} else {
highlightValue(new Highlight(x, y, dataSetIndex, dataIndex), callListener);
}
}
/**
@ -591,12 +661,7 @@ public abstract class Chart<T extends ChartData<? extends IDataSet<? extends Ent
* @param callListener Should the listener be called for this change
*/
public void highlightValue(float x, float y, int dataSetIndex, boolean callListener) {
if (dataSetIndex < 0 || dataSetIndex >= mData.getDataSetCount()) {
highlightValue(null, callListener);
} else {
highlightValue(new Highlight(x, y, dataSetIndex), callListener);
}
highlightValue(x, y, dataSetIndex, -1, callListener);
}
/**
@ -1162,6 +1227,15 @@ public abstract class Chart<T extends ChartData<? extends IDataSet<? extends Ent
mInfoPaint.setTypeface(tf);
}
/**
* alignment of the no data text
*
* @param align
*/
public void setNoDataTextAlignment(Align align) {
mInfoPaint.setTextAlign(align);
}
/**
* Set this to false to disable all gestures and touches on the chart,
* default: true

View file

@ -60,6 +60,84 @@ public class HorizontalBarChart extends BarChart {
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() {

View file

@ -84,6 +84,7 @@ public class RadarChart extends PieRadarChartBase<RadarData> {
super.init();
mYAxis = new YAxis(AxisDependency.LEFT);
mYAxis.setLabelXOffset(10f);
mWebLineWidth = Utils.convertDpToPixel(1.5f);
mInnerWebLineWidth = Utils.convertDpToPixel(0.75f);

View file

@ -151,6 +151,39 @@ public abstract class AxisBase extends ComponentBase {
*/
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
*/
@ -314,10 +347,10 @@ public abstract class AxisBase extends ComponentBase {
*/
public void setLabelCount(int count) {
if (count > 25)
count = 25;
if (count < 2)
count = 2;
if (count > getAxisMaxLabels())
count = getAxisMaxLabels();
if (count < getAxisMinLabels())
count = getAxisMinLabels();
mLabelCount = count;
mForceLabels = false;

View file

@ -703,8 +703,7 @@ public class Legend extends ComponentBase {
width += Utils.calcTextWidth(labelpaint, label);
if (i < entryCount - 1)
maxHeight += labelLineHeight + yEntrySpace;
maxHeight += labelLineHeight + yEntrySpace;
} else {
wasStacked = true;
width += formSize;

View file

@ -73,6 +73,11 @@ public class YAxis extends AxisBase {
*/
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
*/
@ -174,6 +179,22 @@ public class YAxis extends AxisBase {
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
*
@ -406,6 +427,26 @@ public class YAxis extends AxisBase {
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

View file

@ -38,9 +38,7 @@ public class BarDataSet extends BarLineScatterCandleBubbleDataSet<BarEntry> impl
/**
* array of labels used to describe the different values of the stacked bars
*/
private String[] mStackLabels = new String[]{
"Stack"
};
private String[] mStackLabels = new String[]{};
public BarDataSet(List<BarEntry> yVals, String label) {
super(yVals, label);
@ -54,8 +52,8 @@ public class BarDataSet extends BarLineScatterCandleBubbleDataSet<BarEntry> impl
@Override
public DataSet<BarEntry> copy() {
List<BarEntry> entries = new ArrayList<BarEntry>();
for (int i = 0; i < mValues.size(); i++) {
entries.add(mValues.get(i).copy());
for (int i = 0; i < mEntries.size(); i++) {
entries.add(mEntries.get(i).copy());
}
BarDataSet copied = new BarDataSet(entries, getLabel());
copy(copied);

View file

@ -42,8 +42,8 @@ public class BubbleDataSet extends BarLineScatterCandleBubbleDataSet<BubbleEntry
@Override
public DataSet<BubbleEntry> copy() {
List<BubbleEntry> entries = new ArrayList<BubbleEntry>();
for (int i = 0; i < mValues.size(); i++) {
entries.add(mValues.get(i).copy());
for (int i = 0; i < mEntries.size(); i++) {
entries.add(mEntries.get(i).copy());
}
BubbleDataSet copied = new BubbleDataSet(entries, getLabel());
copy(copied);

View file

@ -80,8 +80,8 @@ public class CandleDataSet extends LineScatterCandleRadarDataSet<CandleEntry> im
@Override
public DataSet<CandleEntry> copy() {
List<CandleEntry> entries = new ArrayList<CandleEntry>();
for (int i = 0; i < mValues.size(); i++) {
entries.add(mValues.get(i).copy());
for (int i = 0; i < mEntries.size(); i++) {
entries.add(mEntries.get(i).copy());
}
CandleDataSet copied = new CandleDataSet(entries, getLabel());
copy(copied);

View file

@ -399,7 +399,7 @@ public abstract class ChartData<T extends IDataSet<? extends Entry>> {
// if a DataSet was removed
if (removed) {
calcMinMax();
notifyDataChanged();
}
return removed;
@ -526,7 +526,7 @@ public abstract class ChartData<T extends IDataSet<? extends Entry>> {
boolean removed = set.removeEntry(e);
if (removed) {
calcMinMax();
notifyDataChanged();
}
return removed;

View file

@ -3,6 +3,7 @@ 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;
@ -91,18 +92,26 @@ public class CombinedData extends BarLineScatterCandleBubbleData<IBarLineScatter
if (data.getXMin() < mXMin)
mXMin = data.getXMin();
if (data.mLeftAxisMax > mLeftAxisMax)
mLeftAxisMax = data.mLeftAxisMax;
for (IBarLineScatterCandleBubbleDataSet<? extends Entry> dataset : sets) {
if (dataset.getAxisDependency() == YAxis.AxisDependency.LEFT) {
if (dataset.getYMax() > mLeftAxisMax) {
mLeftAxisMax = dataset.getYMax();
}
if (data.mLeftAxisMin < mLeftAxisMin)
mLeftAxisMin = data.mLeftAxisMin;
if (data.mRightAxisMax > mRightAxisMax)
mRightAxisMax = data.mRightAxisMax;
if (data.mRightAxisMin < mRightAxisMin)
mRightAxisMin = data.mRightAxisMin;
if (dataset.getYMin() < mLeftAxisMin) {
mLeftAxisMin = dataset.getYMin();
}
}
else {
if (dataset.getYMax() > mRightAxisMax) {
mRightAxisMax = dataset.getYMax();
}
if (dataset.getYMin() < mRightAxisMin) {
mRightAxisMin = dataset.getYMin();
}
}
}
}
}

View file

@ -17,7 +17,7 @@ public abstract class DataSet<T extends Entry> extends BaseDataSet<T> {
/**
* the entries that this DataSet represents / holds together
*/
protected List<T> mValues = null;
protected List<T> mEntries;
/**
* maximum y-value in the value array
@ -45,15 +45,15 @@ public abstract class DataSet<T extends Entry> extends BaseDataSet<T> {
* label that describes the DataSet can be specified. The label can also be
* used to retrieve the DataSet from a ChartData object.
*
* @param values
* @param entries
* @param label
*/
public DataSet(List<T> values, String label) {
public DataSet(List<T> entries, String label) {
super(label);
this.mValues = values;
this.mEntries = entries;
if (mValues == null)
mValues = new ArrayList<T>();
if (mEntries == null)
mEntries = new ArrayList<T>();
calcMinMax();
}
@ -61,35 +61,36 @@ public abstract class DataSet<T extends Entry> extends BaseDataSet<T> {
@Override
public void calcMinMax() {
if (mValues == null || mValues.isEmpty())
return;
mYMax = -Float.MAX_VALUE;
mYMin = Float.MAX_VALUE;
mXMax = -Float.MAX_VALUE;
mXMin = Float.MAX_VALUE;
for (T e : mValues) {
if (mEntries == null || mEntries.isEmpty())
return;
for (T e : mEntries) {
calcMinMax(e);
}
}
@Override
public void calcMinMaxY(float fromX, float toX) {
if (mValues == null || mValues.isEmpty())
return;
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(mValues.get(i));
calcMinMaxY(mEntries.get(i));
}
}
@ -128,7 +129,18 @@ public abstract class DataSet<T extends Entry> extends BaseDataSet<T> {
@Override
public int getEntryCount() {
return mValues.size();
return mEntries.size();
}
/**
* This method is deprecated.
* Use getEntries() instead.
*
* @return
*/
@Deprecated
public List<T> getValues() {
return mEntries;
}
/**
@ -136,8 +148,19 @@ public abstract class DataSet<T extends Entry> extends BaseDataSet<T> {
*
* @return
*/
public List<T> getValues() {
return mValues;
public List<T> getEntries() {
return mEntries;
}
/**
* This method is deprecated.
* Use setEntries(...) instead.
*
* @param values
*/
@Deprecated
public void setValues(List<T> values) {
setEntries(values);
}
/**
@ -145,8 +168,8 @@ public abstract class DataSet<T extends Entry> extends BaseDataSet<T> {
*
* @return
*/
public void setValues(List<T> values) {
mValues = values;
public void setEntries(List<T> entries) {
mEntries = entries;
notifyDataSetChanged();
}
@ -169,8 +192,8 @@ public abstract class DataSet<T extends Entry> extends BaseDataSet<T> {
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append(toSimpleString());
for (int i = 0; i < mValues.size(); i++) {
buffer.append(mValues.get(i).toString() + " ");
for (int i = 0; i < mEntries.size(); i++) {
buffer.append(mEntries.get(i).toString() + " ");
}
return buffer.toString();
}
@ -183,7 +206,7 @@ public abstract class DataSet<T extends Entry> extends BaseDataSet<T> {
*/
public String toSimpleString() {
StringBuffer buffer = new StringBuffer();
buffer.append("DataSet, label: " + (getLabel() == null ? "" : getLabel()) + ", entries: " + mValues.size() +
buffer.append("DataSet, label: " + (getLabel() == null ? "" : getLabel()) + ", entries: " + mEntries.size() +
"\n");
return buffer.toString();
}
@ -214,23 +237,23 @@ public abstract class DataSet<T extends Entry> extends BaseDataSet<T> {
if (e == null)
return;
if (mValues == null) {
mValues = new ArrayList<T>();
if (mEntries == null) {
mEntries = new ArrayList<T>();
}
calcMinMax(e);
if (mValues.size() > 0 && mValues.get(mValues.size() - 1).getX() > e.getX()) {
if (mEntries.size() > 0 && mEntries.get(mEntries.size() - 1).getX() > e.getX()) {
int closestIndex = getEntryIndex(e.getX(), e.getY(), Rounding.UP);
mValues.add(closestIndex, e);
mEntries.add(closestIndex, e);
} else {
mValues.add(e);
mEntries.add(e);
}
}
@Override
public void clear() {
mValues.clear();
mEntries.clear();
notifyDataSetChanged();
}
@ -240,9 +263,9 @@ public abstract class DataSet<T extends Entry> extends BaseDataSet<T> {
if (e == null)
return false;
List<T> values = getValues();
List<T> values = getEntries();
if (values == null) {
values = new ArrayList<T>();
values = new ArrayList<>();
}
calcMinMax(e);
@ -257,11 +280,11 @@ public abstract class DataSet<T extends Entry> extends BaseDataSet<T> {
if (e == null)
return false;
if (mValues == null)
if (mEntries == null)
return false;
// remove the entry
boolean removed = mValues.remove(e);
boolean removed = mEntries.remove(e);
if (removed) {
calcMinMax();
@ -272,7 +295,7 @@ public abstract class DataSet<T extends Entry> extends BaseDataSet<T> {
@Override
public int getEntryIndex(Entry e) {
return mValues.indexOf(e);
return mEntries.indexOf(e);
}
@Override
@ -280,7 +303,7 @@ public abstract class DataSet<T extends Entry> extends BaseDataSet<T> {
int index = getEntryIndex(xValue, closestToY, rounding);
if (index > -1)
return mValues.get(index);
return mEntries.get(index);
return null;
}
@ -291,24 +314,24 @@ public abstract class DataSet<T extends Entry> extends BaseDataSet<T> {
@Override
public T getEntryForIndex(int index) {
return mValues.get(index);
return mEntries.get(index);
}
@Override
public int getEntryIndex(float xValue, float closestToY, Rounding rounding) {
if (mValues == null || mValues.isEmpty())
if (mEntries == null || mEntries.isEmpty())
return -1;
int low = 0;
int high = mValues.size() - 1;
int high = mEntries.size() - 1;
int closest = high;
while (low < high) {
int m = (low + high) / 2;
final float d1 = mValues.get(m).getX() - xValue,
d2 = mValues.get(m + 1).getX() - xValue,
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) {
@ -335,10 +358,10 @@ public abstract class DataSet<T extends Entry> extends BaseDataSet<T> {
}
if (closest != -1) {
float closestXValue = mValues.get(closest).getX();
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 < mValues.size() - 1) {
if (closestXValue < xValue && closest < mEntries.size() - 1) {
++closest;
}
} else if (rounding == Rounding.DOWN) {
@ -350,23 +373,23 @@ public abstract class DataSet<T extends Entry> extends BaseDataSet<T> {
// Search by closest to y-value
if (!Float.isNaN(closestToY)) {
while (closest > 0 && mValues.get(closest - 1).getX() == closestXValue)
while (closest > 0 && mEntries.get(closest - 1).getX() == closestXValue)
closest -= 1;
float closestYValue = mValues.get(closest).getY();
float closestYValue = mEntries.get(closest).getY();
int closestYIndex = closest;
while (true) {
closest += 1;
if (closest >= mValues.size())
if (closest >= mEntries.size())
break;
final Entry value = mValues.get(closest);
final Entry value = mEntries.get(closest);
if (value.getX() != closestXValue)
break;
if (Math.abs(value.getY() - closestToY) < Math.abs(closestYValue - closestToY)) {
if (Math.abs(value.getY() - closestToY) <= Math.abs(closestYValue - closestToY)) {
closestYValue = closestToY;
closestYIndex = closest;
}
@ -385,22 +408,22 @@ public abstract class DataSet<T extends Entry> extends BaseDataSet<T> {
List<T> entries = new ArrayList<T>();
int low = 0;
int high = mValues.size() - 1;
int high = mEntries.size() - 1;
while (low <= high) {
int m = (high + low) / 2;
T entry = mValues.get(m);
T entry = mEntries.get(m);
// if we have a match
if (xValue == entry.getX()) {
while (m > 0 && mValues.get(m - 1).getX() == xValue)
while (m > 0 && mEntries.get(m - 1).getX() == xValue)
m--;
high = mValues.size();
high = mEntries.size();
// loop over all "equal" entries
for (; m < high; m++) {
entry = mValues.get(m);
entry = mEntries.get(m);
if (entry.getX() == xValue) {
entries.add(entry);
} else {

View file

@ -85,8 +85,8 @@ public class LineDataSet extends LineRadarDataSet<Entry> implements ILineDataSet
@Override
public DataSet<Entry> copy() {
List<Entry> entries = new ArrayList<Entry>();
for (int i = 0; i < mValues.size(); i++) {
entries.add(mValues.get(i).copy());
for (int i = 0; i < mEntries.size(); i++) {
entries.add(mEntries.get(i).copy());
}
LineDataSet copied = new LineDataSet(entries, getLabel());
copy(copied);

View file

@ -1,6 +1,8 @@
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;
@ -46,6 +48,18 @@ public class PieData extends ChartData<IPieDataSet> {
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.
*

View file

@ -3,6 +3,7 @@ package com.github.mikephil.charting.data;
import com.github.mikephil.charting.interfaces.datasets.IPieDataSet;
import com.github.mikephil.charting.utils.Utils;
import android.support.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
@ -22,13 +23,14 @@ public class PieDataSet extends DataSet<PieEntry> implements IPieDataSet {
private ValuePosition mXValuePosition = ValuePosition.INSIDE_SLICE;
private ValuePosition mYValuePosition = ValuePosition.INSIDE_SLICE;
private boolean mUsingSliceColorAsValueLineColor = false;
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);
@ -38,8 +40,8 @@ public class PieDataSet extends DataSet<PieEntry> implements IPieDataSet {
@Override
public DataSet<PieEntry> copy() {
List<PieEntry> entries = new ArrayList<>();
for (int i = 0; i < mValues.size(); i++) {
entries.add(mValues.get(i).copy());
for (int i = 0; i < mEntries.size(); i++) {
entries.add(mEntries.get(i).copy());
}
PieDataSet copied = new PieDataSet(entries, getLabel());
copy(copied);
@ -135,15 +137,23 @@ public class PieDataSet extends DataSet<PieEntry> implements IPieDataSet {
}
/**
* When valuePosition is OutsideSlice, use slice colors as line color if true
* This method is deprecated.
* Use isUseValueColorForLineEnabled() instead.
*/
@Override
@Deprecated
public boolean isUsingSliceColorAsValueLineColor() {
return mUsingSliceColorAsValueLineColor;
return isUseValueColorForLineEnabled();
}
public void setUsingSliceColorAsValueLineColor(boolean usingSliceColorAsValueLineColor) {
this.mUsingSliceColorAsValueLineColor = usingSliceColorAsValueLineColor;
/**
* This method is deprecated.
* Use setUseValueColorForLine(...) instead.
*
* @param enabled
*/
@Deprecated
public void setUsingSliceColorAsValueLineColor(boolean enabled) {
setUseValueColorForLine(enabled);
}
/**
@ -158,6 +168,17 @@ public class PieDataSet extends DataSet<PieEntry> implements IPieDataSet {
this.mValueLineColor = valueLineColor;
}
@Override
public boolean isUseValueColorForLineEnabled()
{
return mUseValueColorForLine;
}
public void setUseValueColorForLine(boolean enabled)
{
mUseValueColorForLine = enabled;
}
/**
* When valuePosition is OutsideSlice, indicates line width
*/
@ -218,6 +239,21 @@ public class PieDataSet extends DataSet<PieEntry> implements IPieDataSet {
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

@ -102,8 +102,8 @@ public class RadarDataSet extends LineRadarDataSet<RadarEntry> implements IRadar
@Override
public DataSet<RadarEntry> copy() {
List<RadarEntry> entries = new ArrayList<RadarEntry>();
for (int i = 0; i < mValues.size(); i++) {
entries.add(mValues.get(i).copy());
for (int i = 0; i < mEntries.size(); i++) {
entries.add(mEntries.get(i).copy());
}
RadarDataSet copied = new RadarDataSet(entries, getLabel());
copy(copied);

View file

@ -48,8 +48,8 @@ public class ScatterDataSet extends LineScatterCandleRadarDataSet<Entry> impleme
@Override
public DataSet<Entry> copy() {
List<Entry> entries = new ArrayList<Entry>();
for (int i = 0; i < mValues.size(); i++) {
entries.add(mValues.get(i).copy());
for (int i = 0; i < mEntries.size(); i++) {
entries.add(mEntries.get(i).copy());
}
ScatterDataSet copied = new ScatterDataSet(entries, getLabel());
copy(copied);

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

@ -60,10 +60,18 @@ public class Highlight {
*/
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) {

View file

@ -1,5 +1,7 @@
package com.github.mikephil.charting.interfaces.datasets;
import android.support.annotation.Nullable;
import com.github.mikephil.charting.data.PieDataSet;
import com.github.mikephil.charting.data.PieEntry;
@ -35,16 +37,16 @@ public interface IPieDataSet extends IDataSet<PieEntry> {
PieDataSet.ValuePosition getXValuePosition();
PieDataSet.ValuePosition getYValuePosition();
/**
* When valuePosition is OutsideSlice, use slice colors as line color if true
* */
boolean isUsingSliceColorAsValueLineColor();
/**
* 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
* */
@ -70,5 +72,11 @@ public interface IPieDataSet extends IDataSet<PieEntry> {
* */
boolean isValueLineVariableLength();
/**
* Gets the color for the highlighted sector
* */
@Nullable
Integer getHighlightColor();
}

View file

@ -580,12 +580,19 @@ public class BarLineChartTouchListener extends ChartTouchListener<BarLineChartBa
MPPointF trans = getTrans(e.getX(), e.getY());
mChart.zoom(mChart.isScaleXEnabled() ? 1.4f : 1f, mChart.isScaleYEnabled() ? 1.4f : 1f, trans.x, trans.y);
float scaleX = mChart.isScaleXEnabled() ? 1.4f : 1f;
float scaleY = mChart.isScaleYEnabled() ? 1.4f : 1f;
mChart.zoom(scaleX, scaleY, trans.x, trans.y);
if (mChart.isLogEnabled())
Log.i("BarlineChartTouch", "Double-Tap, Zooming In, x: " + trans.x + ", y: "
+ trans.y);
if (l != null) {
l.onChartScale(e, scaleX, scaleY);
}
MPPointF.recycleInstance(trans);
}

View file

@ -57,7 +57,7 @@ public interface OnChartGestureListener {
void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY);
/**
* Callbacks when the chart is scaled / zoomed via pinch zoom gesture.
* Callbacks when the chart is scaled / zoomed via pinch zoom / double-tap gesture.
*
* @param me
* @param scaleX scalefactor on the x-axis

View file

@ -174,9 +174,12 @@ public abstract class AxisRenderer extends Renderer {
double intervalMagnitude = Utils.roundToNextSignificant(Math.pow(10, (int) Math.log10(interval)));
int intervalSigDigit = (int) (interval / intervalMagnitude);
if (intervalSigDigit > 5) {
// Use one order of magnitude higher, to avoid intervals like 0.9 or
// 90
interval = Math.floor(10 * intervalMagnitude);
// Use one order of magnitude higher, to avoid intervals like 0.9 or 90
// if it's 0.0 after floor(), we use the old value
interval = Math.floor(10.0 * intervalMagnitude) == 0.0
? interval
: Math.floor(10.0 * intervalMagnitude);
}
int n = mAxis.isCenterAxisLabelsEnabled() ? 1 : 0;
@ -214,11 +217,14 @@ public abstract class AxisRenderer extends Renderer {
double f;
int i;
if (interval != 0.0) {
if (interval != 0.0 && last != first) {
for (f = first; f <= last; f += interval) {
++n;
}
}
else if (last == first && n == 0) {
n = 1;
}
mAxis.mEntryCount = n;

View file

@ -108,7 +108,7 @@ public class BubbleChartRenderer extends BarLineScatterCandleBubbleRenderer {
if (!mViewPortHandler.isInBoundsRight(pointBuffer[0] - shapeHalf))
break;
final int color = dataSet.getColor((int) entry.getX());
final int color = dataSet.getColor(j);
mRenderPaint.setColor(color);
c.drawCircle(pointBuffer[0], pointBuffer[1], shapeHalf, mRenderPaint);

View file

@ -200,7 +200,8 @@ public class HorizontalBarChartRenderer extends BarChartRenderer {
// calculate the correct offset depending on the draw position of the value
float valueTextWidth = Utils.calcTextWidth(mValuePaint, formattedValue);
posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus));
negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus);
negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus)
- (buffer.buffer[j + 2] - buffer.buffer[j]);
if (isInverted) {
posOffset = -posOffset - valueTextWidth;

View file

@ -90,6 +90,7 @@ public class LegendRenderer extends Renderer {
for (int i = 0; i < data.getDataSetCount(); i++) {
IDataSet dataSet = data.getDataSetByIndex(i);
if (dataSet == null) continue;
List<Integer> clrs = dataSet.getColors();
int entryCount = dataSet.getEntryCount();
@ -100,10 +101,19 @@ public class LegendRenderer extends Renderer {
IBarDataSet bds = (IBarDataSet) dataSet;
String[] sLabels = bds.getStackLabels();
for (int j = 0; j < clrs.size() && j < bds.getStackSize(); j++) {
int minEntries = Math.min(clrs.size(), bds.getStackSize());
for (int j = 0; j < minEntries; j++) {
String label;
if (sLabels.length > 0) {
int labelIndex = j % minEntries;
label = labelIndex < sLabels.length ? sLabels[labelIndex] : null;
} else {
label = null;
}
computedEntries.add(new LegendEntry(
sLabels[j % sLabels.length],
label,
dataSet.getForm(),
dataSet.getFormSize(),
dataSet.getFormLineWidth(),

View file

@ -323,10 +323,14 @@ public class LineChartRenderer extends LineRadarRenderer {
// more than 1 color
if (dataSet.getColors().size() > 1) {
if (mLineBuffer.length <= pointsPerEntryPair * 2)
mLineBuffer = new float[pointsPerEntryPair * 4];
int numberOfFloats = pointsPerEntryPair * 2;
for (int j = mXBounds.min; j <= mXBounds.range + mXBounds.min; j++) {
if (mLineBuffer.length <= numberOfFloats)
mLineBuffer = new float[numberOfFloats * 2];
int max = mXBounds.min + mXBounds.range;
for (int j = mXBounds.min; j < max; j++) {
Entry e = dataSet.getEntryForIndex(j);
if (e == null) continue;
@ -357,16 +361,26 @@ public class LineChartRenderer extends LineRadarRenderer {
mLineBuffer[3] = mLineBuffer[1];
}
// Determine the start and end coordinates of the line, and make sure they differ.
float firstCoordinateX = mLineBuffer[0];
float firstCoordinateY = mLineBuffer[1];
float lastCoordinateX = mLineBuffer[numberOfFloats - 2];
float lastCoordinateY = mLineBuffer[numberOfFloats - 1];
if (firstCoordinateX == lastCoordinateX &&
firstCoordinateY == lastCoordinateY)
continue;
trans.pointValuesToPixel(mLineBuffer);
if (!mViewPortHandler.isInBoundsRight(mLineBuffer[0]))
if (!mViewPortHandler.isInBoundsRight(firstCoordinateX))
break;
// make sure the lines don't do shitty things outside
// bounds
if (!mViewPortHandler.isInBoundsLeft(mLineBuffer[2])
|| (!mViewPortHandler.isInBoundsTop(mLineBuffer[1]) && !mViewPortHandler
.isInBoundsBottom(mLineBuffer[3])))
if (!mViewPortHandler.isInBoundsLeft(lastCoordinateX) ||
!mViewPortHandler.isInBoundsTop(lastCoordinateY) ||
!mViewPortHandler.isInBoundsBottom(firstCoordinateY))
continue;
// get the color that is set for this line-segment

View file

@ -258,7 +258,7 @@ public class PieChartRenderer extends DataRenderer {
}
// Don't draw if it's highlighted, unless the chart uses rounded slices
if (mChart.needsHighlight(j) && !drawRoundedSlices) {
if (dataSet.isHighlightEnabled() && mChart.needsHighlight(j) && !drawRoundedSlices) {
angle += sliceAngle * phaseX;
continue;
}
@ -468,7 +468,9 @@ public class PieChartRenderer extends DataRenderer {
int entryCount = dataSet.getEntryCount();
mValueLinePaint.setColor(dataSet.getValueLineColor());
boolean isUseValueColorForLineEnabled = dataSet.isUseValueColorForLineEnabled();
int valueLineColor = dataSet.getValueLineColor();
mValueLinePaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getValueLineWidth()));
final float sliceSpace = getSliceSpace(dataSet);
@ -565,12 +567,15 @@ public class PieChartRenderer extends DataRenderer {
labelPty = pt2y;
}
if (dataSet.getValueLineColor() != ColorTemplate.COLOR_NONE) {
int lineColor = ColorTemplate.COLOR_NONE;
if (dataSet.isUsingSliceColorAsValueLineColor()) {
mValueLinePaint.setColor(dataSet.getColor(j));
}
if (isUseValueColorForLineEnabled)
lineColor = dataSet.getColor(j);
else if (valueLineColor != ColorTemplate.COLOR_NONE)
lineColor = valueLineColor;
if (lineColor != ColorTemplate.COLOR_NONE) {
mValueLinePaint.setColor(lineColor);
c.drawLine(pt0x, pt0y, pt1x, pt1y, mValueLinePaint);
c.drawLine(pt1x, pt1y, pt2x, pt2y, mValueLinePaint);
}
@ -825,8 +830,7 @@ public class PieChartRenderer extends DataRenderer {
continue;
IPieDataSet set = mChart.getData()
.getDataSetByIndex(indices[i]
.getDataSetIndex());
.getDataSetByIndex(indices[i].getDataSetIndex());
if (set == null || !set.isHighlightEnabled())
continue;
@ -857,7 +861,10 @@ public class PieChartRenderer extends DataRenderer {
final boolean accountForSliceSpacing = sliceSpace > 0.f && sliceAngle <= 180.f;
mRenderPaint.setColor(set.getColor(index));
Integer highlightColor = set.getHighlightColor();
if (highlightColor == null)
highlightColor = set.getColor(index);
mRenderPaint.setColor(highlightColor);
final float sliceSpaceAngleOuter = visibleAngleCount == 1 ?
0.f :

View file

@ -119,12 +119,17 @@ public class YAxisRenderer extends AxisRenderer {
? mYAxis.mEntryCount
: (mYAxis.mEntryCount - 1);
float xOffset = mYAxis.getLabelXOffset();
// draw
for (int i = from; i < to; i++) {
String text = mYAxis.getFormattedLabel(i);
c.drawText(text, fixedPosition, positions[i * 2 + 1] + offset, mAxisLabelPaint);
c.drawText(text,
fixedPosition + xOffset,
positions[i * 2 + 1] + offset,
mAxisLabelPaint);
}
}

View file

@ -142,11 +142,16 @@ public class YAxisRendererHorizontalBarChart extends YAxisRenderer {
? mYAxis.mEntryCount
: (mYAxis.mEntryCount - 1);
float xOffset = mYAxis.getLabelXOffset();
for (int i = from; i < to; i++) {
String text = mYAxis.getFormattedLabel(i);
c.drawText(text, positions[i * 2], fixedPosition - offset, mAxisLabelPaint);
c.drawText(text,
positions[i * 2],
fixedPosition - offset + xOffset,
mAxisLabelPaint);
}
}

View file

@ -52,9 +52,11 @@ public class YAxisRendererRadarChart extends YAxisRenderer {
double intervalMagnitude = Utils.roundToNextSignificant(Math.pow(10, (int) Math.log10(interval)));
int intervalSigDigit = (int) (interval / intervalMagnitude);
if (intervalSigDigit > 5) {
// Use one order of magnitude higher, to avoid intervals like 0.9 or
// 90
interval = Math.floor(10 * intervalMagnitude);
// Use one order of magnitude higher, to avoid intervals like 0.9 or 90
// if it's 0.0 after floor(), we use the old value
interval = Math.floor(10.0 * intervalMagnitude) == 0.0
? interval
: Math.floor(10.0 * intervalMagnitude);
}
boolean centeringEnabled = mAxis.isCenterAxisLabelsEnabled();
@ -161,6 +163,8 @@ public class YAxisRendererRadarChart extends YAxisRenderer {
? mYAxis.mEntryCount
: (mYAxis.mEntryCount - 1);
float xOffset = mYAxis.getLabelXOffset();
for (int j = from; j < to; j++) {
float r = (mYAxis.mEntries[j] - mYAxis.mAxisMinimum) * factor;
@ -169,7 +173,7 @@ public class YAxisRendererRadarChart extends YAxisRenderer {
String label = mYAxis.getFormattedLabel(j);
c.drawText(label, pOut.x + 10, pOut.y, mAxisLabelPaint);
c.drawText(label, pOut.x + xOffset, pOut.y, mAxisLabelPaint);
}
MPPointF.recycleInstance(center);
MPPointF.recycleInstance(pOut);