I'm currently making an Excel chart using org.apache.poi and oooxml library. I developed the picture below.
enter image description here
enter image description here
XSSFDrawing drawing = (XSSFDrawing)sheet.createDrawingPatriarch();
ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 35, 20, 57);
XSSFChart chart = drawing.createChart(anchor);
CTChart ctChart = ((XSSFChart)chart).getCTChart();
CTPlotArea ctPlotArea = ctChart.getPlotArea();
//여기까지는 똑같음
//the first bar chart
CTBarChart ctBarChart = ctPlotArea.addNewBarChart();
CTBoolean ctBoolean = ctBarChart.addNewVaryColors();
ctBoolean.setVal(true);
ctBarChart.addNewBarDir().setVal(STBarDir.COL);
//the first chart series
CTBarSer ctBarSer = ctBarChart.addNewSer();
CTSerTx ctSerTx = ctBarSer.addNewTx();
CTStrRef ctStrRef = ctSerTx.addNewStrRef();
ctStrRef.setF("Sheet1!$B$10");
ctBarSer.addNewIdx().setVal(0);
CTAxDataSource ctAxDataSource = ctBarSer.addNewCat();
ctStrRef = ctAxDataSource.addNewStrRef();
ctStrRef.setF("Sheet1!$A$11:$A$34");
CTNumDataSource ctNumDataSource = ctBarSer.addNewVal();
CTNumRef ctNumRef = ctNumDataSource.addNewNumRef();
ctNumRef.setF("Sheet1!$B$11:$B$34");
//the second chart series
CTBarSer ctBarSer1 = ctBarChart.addNewSer();
CTSerTx ctSerTx1 = ctBarSer1.addNewTx();
CTStrRef ctStrRef1 = ctSerTx1.addNewStrRef();
ctStrRef1.setF("Sheet1!$C$10");
ctBarSer1.addNewIdx().setVal(1);
CTAxDataSource ctAxDataSource1 = ctBarSer1.addNewCat();
ctStrRef1 = ctAxDataSource1.addNewStrRef();
ctStrRef1.setF("Sheet1!$A$11:$A$34");
CTNumDataSource ctNumDataSource1 = ctBarSer1.addNewVal();
CTNumRef ctNumRef1 = ctNumDataSource1.addNewNumRef();
ctNumRef1.setF("Sheet1!$C$11:$C$34");
// 3번째
CTBarSer ctBarSer2 = ctBarChart.addNewSer();
CTSerTx ctSerTx2 = ctBarSer2.addNewTx();
CTStrRef ctStrRef2 = ctSerTx2.addNewStrRef();
ctStrRef2.setF("Sheet1!$D$10");
ctBarSer2.addNewIdx().setVal(2);
CTAxDataSource ctAxDataSource2 = ctBarSer2.addNewCat();
ctStrRef2 = ctAxDataSource2.addNewStrRef();
ctStrRef2.setF("Sheet1!$A$11:$A$34");
CTNumDataSource ctNumDataSource2 = ctBarSer2.addNewVal();
CTNumRef ctNumRef2 = ctNumDataSource2.addNewNumRef();
ctNumRef2.setF("Sheet1!$D$11:$D$34");
//at least the border lines in Libreoffice Calc ;-)
ctBarSer.addNewSpPr().addNewLn().addNewSolidFill().addNewSrgbClr().setVal(new byte[] {0,0,0});
ctBarSer1.addNewSpPr().addNewLn().addNewSolidFill().addNewSrgbClr().setVal(new byte[] {0,0,0});
ctBarSer2.addNewSpPr().addNewLn().addNewSolidFill().addNewSrgbClr().setVal(new byte[] {0,0,0});
ctBarChart.addNewAxId().setVal(123456); //cat axis 1 (lines)
ctBarChart.addNewAxId().setVal(123457); //val axis 1 (left)
ctBarChart.addNewAxId().setVal(123458); //val axis 1 (right)
ctBarChart.addNewAxId().setVal(123459); //val axis 1 (right)
//cat axis 1
CTCatAx ctCatAx = ctPlotArea.addNewCatAx();
ctCatAx.addNewAxId().setVal(123456); //id of the cat axis
CTScaling ctScaling = ctCatAx.addNewScaling();
ctScaling.addNewOrientation().setVal(STOrientation.MIN_MAX);
ctCatAx.addNewDelete().setVal(false);
ctCatAx.addNewAxPos().setVal(STAxPos.B);
ctCatAx.addNewCrossAx().setVal(123457); //id of the val axis
ctCatAx.addNewTickLblPos().setVal(STTickLblPos.NEXT_TO);
//val axis 1 (left)
CTValAx ctValAx = ctPlotArea.addNewValAx();
ctValAx.addNewAxId().setVal(123457); //id of the val axis
ctScaling = ctValAx.addNewScaling();
ctScaling.addNewOrientation().setVal(STOrientation.MIN_MAX);
ctValAx.addNewDelete().setVal(false);
ctValAx.addNewAxPos().setVal(STAxPos.L);
ctValAx.addNewCrossAx().setVal(123456); //id of the cat axis
ctValAx.addNewCrosses().setVal(STCrosses.AUTO_ZERO); //this val axis crosses the cat axis at zero
ctValAx.addNewTickLblPos().setVal(STTickLblPos.NEXT_TO);
As for the amount of "transmission= C cell", the unit is large, so I'm trying to make another value axis on the right, but I kept getting errors, so I asked. How can I create a valueaxis on the right, the number of "transmissions" based on the right, and the "user D cell , transfer count B cell" generates a chart based on the left?
From apache poi 4 on you should use the new XDDF classes to create charts. Playing around with the low level CT* classes is very complex and error prone.
Using line charts I have provided an example already here: Second Line in an Apache-POI chart with seperate axis.
But using bar charts there is an additional problem to solve. If one bar chart series is on primary axis and another is on secondary axis, then the bars will overlap each other. This is because each of the series are oriented only on those series using the same axis. In other words the series on primary axis do not respect the series on the secondary axis when it comes to taking space on the chart. For line charts this is not a problem but for bar charts it is.
To solve this additional series having dummy data (all 0) can be used on both the axes. Those additional series lead to invisible bars (height 0) which also take space and so can be used to shift the visible bars.
The following complete example shows this. It is tested and works using apache poi 4.1.2 and apache poi 5.1.0.
import java.io.*;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xddf.usermodel.*;
import org.apache.poi.xddf.usermodel.chart.*;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class CreateExcelXDDFChart {
public static void main(String[] args) throws Exception {
try (XSSFWorkbook document = new XSSFWorkbook()) {
XSSFSheet chartSheet = document.createSheet("chart");
XSSFSheet dataSheet = document.createSheet("data");
// create the data
String[] categories = new String[] { "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9" };
Double[] values1 = new Double[] { 1d, 2d, 3d, 4d, 5d, 6d, 7d, 8d, 9d };
Double[] values2 = new Double[] { 200d, 300d, 400d, 500d, 600d, 700d, 800d, 900d, 1000d };
int r = 0;
for (String cat : categories) {
dataSheet.createRow(r).createCell(0).setCellValue(cat);
dataSheet.getRow(r).createCell(1).setCellValue(values1[r]);
dataSheet.getRow(r).createCell(2).setCellValue(values2[r]);
r++;
}
// create the chart
XSSFDrawing drawing = chartSheet.createDrawingPatriarch();
XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 0, 16, 22);
XDDFChart chart = drawing.createChart(anchor);
// create data sources
int numOfPoints = categories.length;
// dummy 0-values for the pad data source
Double[] dummyValuesForPad = new Double[numOfPoints];
for (int i = 0; i < numOfPoints; i++) {
dummyValuesForPad[i] = 0d;
}
XDDFDataSource<String> categoriesData = XDDFDataSourcesFactory.fromStringCellRange(dataSheet, new CellRangeAddress(0, numOfPoints-1, 0, 0));
XDDFNumericalDataSource<Double> valuesData1 = XDDFDataSourcesFactory.fromNumericCellRange(dataSheet, new CellRangeAddress(0, numOfPoints-1, 1, 1));
XDDFNumericalDataSource<Double> valuesData2 = XDDFDataSourcesFactory.fromNumericCellRange(dataSheet, new CellRangeAddress(0, numOfPoints-1, 2, 2));
// data source for the pad series
XDDFNumericalDataSource<Double> pad = XDDFDataSourcesFactory.fromArray(dummyValuesForPad);
// first bar chart
XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
leftAxis.setCrossBetween(AxisCrossBetween.BETWEEN);
XDDFChartData data = chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
XDDFBarChartData bar = (XDDFBarChartData) data;
bar.setBarDirection(BarDirection.COL);
XDDFChartData.Series series = data.addSeries(categoriesData, valuesData1);
series.setTitle("Series 1", null);
// additional pad series - takes space at right side for primary axis
series = data.addSeries(categoriesData, pad);
series.setTitle("pad", null);
chart.plot(data);
// second bar chart
// bottom axis must be there but must not be visible
bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
bottomAxis.setVisible(false);
XDDFValueAxis rightAxis = chart.createValueAxis(AxisPosition.RIGHT);
rightAxis.setCrosses(AxisCrosses.MAX);
rightAxis.setCrossBetween(AxisCrossBetween.BETWEEN);
// set correct cross axis
bottomAxis.crossAxis(rightAxis);
rightAxis.crossAxis(bottomAxis);
data = chart.createData(ChartTypes.BAR, bottomAxis, rightAxis);
bar = (XDDFBarChartData) data;
bar.setBarDirection(BarDirection.COL);
// additional pad series - takes space at left side for secondary axis
series = data.addSeries(categoriesData, pad);
series.setTitle("pad", null);
series = data.addSeries(categoriesData, valuesData2);
series.setTitle("Series 2", null);
chart.plot(data);
// Write the output to a file
try (FileOutputStream fileOut = new FileOutputStream("CreateExcelXDDFChart.xlsx")) {
document.write(fileOut);
}
}
}
}
Above example works when Microsoft Excel is used to show the resulting *.xlsx file. But some other spreadsheet calculation applications are not able to use array literals ({0,0,0,0,0,0,0,0,0}) as data source for chart series. So the more compatible solution will be using a unused cell range as datasource for the additional dummy series. For example use column IV for this as so:
...
// dummy 0-values for the pad data source in column IV
for (int r = 0; r < numOfPoints; r++) {
XSSFRow row = dataSheet.getRow(r); if (row == null) row = dataSheet.createRow(r);
XSSFCell cell = row.createCell(255);
cell.setCellValue(0);
}
// data source for the pad series
XDDFNumericalDataSource<Double> pad = XDDFDataSourcesFactory.fromNumericCellRange(dataSheet, new CellRangeAddress(0, numOfPoints-1, 255, 255));
...
And also for compatibility reasons you should set colors for the bars as done in the apache poi example in BarChart.java which is available in https://svn.apache.org/repos/asf/poi/trunk/poi-examples/src/main/java/org/apache/poi/examples/xssf/usermodel/. Some other spreadsheet calculation applications will not automatically choose color if not given, as Excel does.
If you have more than one series in one of the bar chart, you need more pad series in the other bar chart which uses the other axis. The pad series are needed to shift the visible bar series and to determine the bar width of them. So if you have two visible bar series on the first chart which uses the left axis and one visible bar series on the second chart which uses the right axis, then you need one additional pad series as the last series in the first bar chart and you need two pad series as the first series in the second bar chart. In other words, you need as much pad series that both bar charts have the same count of series to have the same bar width in both. And the position of the invisible pad series determines how the visible bar series are shifted.
If you need a legend, then the pad series will disturb. So you need deleting them from the legend. In remove specific legend apache poi excel graph XDDFChartLegend I have shown how to do that.
I will provide another complete example which shows all this:
import java.io.*;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xddf.usermodel.*;
import org.apache.poi.xddf.usermodel.chart.*;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class CreateExcelXDDFChart {
public static void main(String[] args) throws Exception {
try (XSSFWorkbook document = new XSSFWorkbook()) {
XSSFSheet chartSheet = document.createSheet("chart");
XSSFSheet dataSheet = document.createSheet("data");
// create the data
String[] categories = new String[] { "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9" };
Double[] values1 = new Double[] { 1d, 2d, 3d, 4d, 5d, 6d, 7d, 8d, 9d };
Double[] values2 = new Double[] { 200d, 300d, 400d, 500d, 600d, 700d, 800d, 900d, 1000d };
Double[] values3 = new Double[] { 4.5d, 4d, 3.5d, 3d, 2.5d, 2d, 1.5d, 1d, 0.5d };
int r = 0;
for (String cat : categories) {
dataSheet.createRow(r).createCell(0).setCellValue(cat);
dataSheet.getRow(r).createCell(1).setCellValue(values1[r]);
dataSheet.getRow(r).createCell(2).setCellValue(values2[r]);
dataSheet.getRow(r).createCell(3).setCellValue(values3[r]);
r++;
}
// create the chart
XSSFDrawing drawing = chartSheet.createDrawingPatriarch();
XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 0, 16, 22);
XDDFChart chart = drawing.createChart(anchor);
// create data sources
int numOfPoints = categories.length;
XDDFDataSource<String> categoriesData = XDDFDataSourcesFactory.fromStringCellRange(dataSheet, new CellRangeAddress(0, numOfPoints-1, 0, 0));
XDDFNumericalDataSource<Double> valuesData1 = XDDFDataSourcesFactory.fromNumericCellRange(dataSheet, new CellRangeAddress(0, numOfPoints-1, 1, 1));
XDDFNumericalDataSource<Double> valuesData2 = XDDFDataSourcesFactory.fromNumericCellRange(dataSheet, new CellRangeAddress(0, numOfPoints-1, 2, 2));
XDDFNumericalDataSource<Double> valuesData3 = XDDFDataSourcesFactory.fromNumericCellRange(dataSheet, new CellRangeAddress(0, numOfPoints-1, 3, 3));
/*
// dummy 0-values for the pad data source
Double[] dummyValuesForPad = new Double[numOfPoints];
for (int i = 0; i < numOfPoints; i++) {
dummyValuesForPad[i] = 0d;
}
*/
// data source for the pad series
//XDDFNumericalDataSource<Double> pad = XDDFDataSourcesFactory.fromArray(dummyValuesForPad);
//XDDFNumericalDataSource<Double> pad = XDDFDataSourcesFactory.fromArray(dummyValuesForPad, null);
// dummy 0-values for the pad data source in column IV
for (int i = 0; i < numOfPoints; i++) {
XSSFRow row = dataSheet.getRow(i); if (row == null) row = dataSheet.createRow(i);
XSSFCell cell = row.createCell(255);
cell.setCellValue(0);
}
// data source for the pad series
XDDFNumericalDataSource<Double> pad = XDDFDataSourcesFactory.fromNumericCellRange(dataSheet, new CellRangeAddress(0, numOfPoints-1, 255, 255));
// first bar chart
XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
leftAxis.setCrossBetween(AxisCrossBetween.BETWEEN);
XDDFChartData data = chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
XDDFBarChartData bar = (XDDFBarChartData) data;
bar.setBarDirection(BarDirection.COL);
XDDFChartData.Series series = data.addSeries(categoriesData, valuesData1);
series.setTitle("Series 1", null);
series = data.addSeries(categoriesData, valuesData3);
series.setTitle("Series 3", null);
// additional pad series - takes space at right side for primary axis
series = data.addSeries(categoriesData, pad);
series.setTitle("pad", null);
chart.plot(data);
// set bar colors
solidFillSeries(data, 0, PresetColor.GREEN);
solidFillSeries(data, 1, PresetColor.BLUE);
// second bar chart
// bottom axis must be there but must not be visible
bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
bottomAxis.setVisible(false);
XDDFValueAxis rightAxis = chart.createValueAxis(AxisPosition.RIGHT);
rightAxis.setCrosses(AxisCrosses.MAX);
rightAxis.setCrossBetween(AxisCrossBetween.BETWEEN);
// set correct cross axis
bottomAxis.crossAxis(rightAxis);
rightAxis.crossAxis(bottomAxis);
data = chart.createData(ChartTypes.BAR, bottomAxis, rightAxis);
bar = (XDDFBarChartData) data;
bar.setBarDirection(BarDirection.COL);
// additional pad series - takes space at left side for secondary axis
series = data.addSeries(categoriesData, pad);
series.setTitle("pad", null);
series = data.addSeries(categoriesData, pad);
series.setTitle("pad", null);
series = data.addSeries(categoriesData, valuesData2);
series.setTitle("Series 2", null);
chart.plot(data);
// set bar colors
solidFillSeries(data, 2, PresetColor.RED);
// set legend
XDDFChartLegend legend = chart.getOrAddLegend();
legend.setPosition(LegendPosition.BOTTOM);
// set legend entries for pad series deleted
XDDFLegendEntry legendEntry = getOrAddLegendEntry(legend, 2);
legendEntry.setDelete(true);
legendEntry = getOrAddLegendEntry(legend, 3);
legendEntry.setDelete(true);
legendEntry = getOrAddLegendEntry(legend, 4);
legendEntry.setDelete(true);
// Write the output to a file
try (FileOutputStream fileOut = new FileOutputStream("CreateExcelXDDFChart.xlsx")) {
document.write(fileOut);
}
}
}
private static XDDFLegendEntry getOrAddLegendEntry(XDDFChartLegend legend, long index) {
XDDFLegendEntry legendEntry = null;
for (XDDFLegendEntry storedLegendEntry : legend.getEntries()) {
if (storedLegendEntry.getIndex() == index) {
legendEntry = storedLegendEntry;
break;
}
}
if (legendEntry == null) {
legendEntry = legend.addEntry();
legendEntry.setIndex(index);
}
return legendEntry;
}
private static void solidFillSeries(XDDFChartData data, int index, PresetColor color) {
XDDFSolidFillProperties fill = new XDDFSolidFillProperties(XDDFColor.from(color));
XDDFChartData.Series series = data.getSeries(index);
XDDFShapeProperties properties = series.getShapeProperties();
if (properties == null) {
properties = new XDDFShapeProperties();
}
properties.setFillProperties(fill);
series.setShapeProperties(properties);
}
}
I am using MPAndroidChart BarChart (Grouped DataSets) for showing data of two users. It is showing data but the problem is that its not displaying data on x-axis from start due to which all the bars are not visible.
Arrays:
String[] title_list = {"Whatsapp", "Visit", "Callback", "Interested"}
int[] title_values_1 = {50, 15, 25, 36};
int[] title_values_2 = {70, 35, 15, 10};
BarChart:
public void LoadBarChart()
{
List<BarEntry> barEntries1 = new ArrayList<>();
for (int i = 0; i < title_list.length; i++) {
barEntries1.add(new BarEntry(i, title_values_1[i]));
}
List<BarEntry> barEntries2 = new ArrayList<>();
for (int i = 0; i < title_list.length; i++) {
barEntries2.add(new BarEntry(i, title_values_2[i]));
}
BarDataSet dataSet1 = new BarDataSet(barEntries1, "Dataset 1");
dataSet1.setColors(getColor(R.color.pie_chart_blue));
dataSet1.setValueTextSize(10f); /* values size */
dataSet1.setValueTextColor(Color.WHITE);
BarDataSet dataSet2 = new BarDataSet(barEntries2, "Dataset 2");
dataSet2.setColors(getColor(R.color.pie_chart_red));
dataSet2.setValueTextSize(10f); /* values size */
dataSet2.setValueTextColor(Color.WHITE);
float groupSpace = 0.06f;
float barSpace = 0.02f; // x2 dataset
float barWidth = 0.45f; // x2 dataset
BarData data = new BarData(dataSet1, dataSet2);
ValueFormatter vf = new ValueFormatter() {
#Override
public String getFormattedValue(float value) { return ""+(int)value; }
};
data.setValueFormatter(vf);
data.setValueTextSize(12f);
data.setBarWidth(barWidth);
XAxis xAxis = barChart.getXAxis();
xAxis.setValueFormatter(new ValueFormatter() {
#Override
public String getFormattedValue(float value) {
return title_list[(int) value];
}
});
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxis.setDrawGridLines(false);
xAxis.setDrawAxisLine(false);
xAxis.setLabelCount(title_list.length);
barChart.setData(data);
barChart.groupBars(0f, groupSpace, barSpace);
barChart.getDescription().setEnabled(false);
barChart.setDrawValueAboveBar(false);
barChart.setTouchEnabled(false);
barChart.animateY(1000);
barChart.invalidate();
}
I have tried answers on stackoverflow but nothing resolved my issue. Kindly help!
UPDATE:
After Shayan answer all bars are now visible but labels are not centered.
Is it possible to center the lables with the bars?
You have to play with the spacings, I think when its unable to adjust the whole thing in the screen it starts cutting bars.
Add this line so bars may start from the begining
xAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true)
Setting spacing and widths a bit low like this
float groupSpace = 0.06f;
float barSpace = 0.02f; // x2 dataset
float barWidth = 0.40f; // x2 dataset
The complete method will look something like this
public void LoadBarChart()
{
List<BarEntry> barEntries1 = new ArrayList<>();
for (int i = 0; i < title_list.length; i++) {
barEntries1.add(new BarEntry(i, title_values_1[i]));
}
List<BarEntry> barEntries2 = new ArrayList<>();
for (int i = 0; i < title_list.length; i++) {
barEntries2.add(new BarEntry(i, title_values_2[i]));
}
BarDataSet dataSet1 = new BarDataSet(barEntries1, "Dataset 1");
dataSet1.setColors(R.color.colorPrimary);
dataSet1.setValueTextSize(10f); /* values size */
dataSet1.setValueTextColor(Color.WHITE);
BarDataSet dataSet2 = new BarDataSet(barEntries2, "Dataset 2");
dataSet2.setColors(R.color.colorPrimary);
dataSet2.setValueTextSize(10f); /* values size */
dataSet2.setValueTextColor(Color.WHITE);
float groupSpace = 0.06f;
float barSpace = 0.02f; // x2 dataset
float barWidth = 0.40f; // x2 dataset
BarData data = new BarData(dataSet1, dataSet2);
ValueFormatter vf = new ValueFormatter() {
#Override
public String getFormattedValue(float value) { return ""+(int)value; }
};
data.setValueFormatter(vf);
// data.setValueTextSize(12f);
data.setBarWidth(barWidth);
XAxis xAxis = barChart.getXAxis();
xAxis.setValueFormatter(new ValueFormatter() {
#Override
public String getFormattedValue(float value) {
return title_list[(int) value];
}
});
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxis.setDrawGridLines(false);
xAxis.setDrawAxisLine(false);
xAxis.setLabelCount(title_list.length);
xAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true)
barChart.setData(data);
barChart.groupBars(0f,groupSpace,barSpace);
barChart.getDescription().setEnabled(false);
barChart.setDrawValueAboveBar(false);
barChart.setTouchEnabled(false);
barChart.animateY(1000);
barChart.invalidate();
}
Hope this works!
I am able to achieve the desired result by the guidance of Shayan. I have added few attributes in the answer.
Spacing and Bar Width:
float groupSpace = 0.15f;
float barSpace = 0.01f; // x2 dataset
float barWidth = 0.42f; // x2 dataset
Addition at X-Axis:
xAxis.setAxisMinimum(0f);
xAxis.setGranularity(1);
xAxis.setCenterAxisLabels(true);
xAxis.setAxisMaximum(title_list.length);
Formatter:
barChart.getXAxis().setValueFormatter(new IndexAxisValueFormatter(title_list));
So bar chart method now looks like this:
public void LoadBarChart()
{
List<BarEntry> barEntries1 = new ArrayList<>();
for (int i = 0; i < title_list.length; i++) {
barEntries1.add(new BarEntry(i, title_values_1[i]));
}
List<BarEntry> barEntries2 = new ArrayList<>();
for (int i = 0; i < title_list.length; i++) {
barEntries2.add(new BarEntry(i, title_values_2[i]));
}
BarDataSet dataSet1 = new BarDataSet(barEntries1, Global.employeesComparisonList.get(0).getName());
dataSet1.setColors(getColor(R.color.blue));
dataSet1.setValueTextSize(10f); /* values size */
dataSet1.setValueTextColor(Color.WHITE);
BarDataSet dataSet2 = new BarDataSet(barEntries2, Global.employeesComparisonList.get(1).getName());
dataSet2.setColors(getColor(R.color.red));
dataSet2.setValueTextSize(10f); /* values size */
dataSet2.setValueTextColor(Color.WHITE);
float groupSpace = 0.15f;
float barSpace = 0.01f; // x2 dataset
float barWidth = 0.42f; // x2 dataset
BarData data = new BarData(dataSet1, dataSet2);
ValueFormatter vf = new ValueFormatter() {
#Override
public String getFormattedValue(float value) { return ""+(int)value; }
};
data.setValueFormatter(vf);
data.setValueTextSize(12f);
data.setBarWidth(barWidth);
XAxis xAxis = barChart.getXAxis();
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxis.setDrawGridLines(false);
xAxis.setDrawAxisLine(false);
xAxis.setLabelCount(title_list.length);
xAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true)
xAxis.setGranularity(1);
xAxis.setCenterAxisLabels(true);
xAxis.setAxisMaximum(title_list.length);
Legend l = barChart.getLegend();
l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP);
l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER);
l.setOrientation(Legend.LegendOrientation.HORIZONTAL);
barChart.setData(data);
barChart.groupBars(0f,groupSpace,barSpace);
barChart.getXAxis().setValueFormatter(new IndexAxisValueFormatter(title_list));
barChart.getDescription().setEnabled(false);
barChart.setDrawValueAboveBar(false);
barChart.setTouchEnabled(false);
barChart.animateY(1000);
barChart.invalidate();
}
Final Result:
Yes, that can be done quite easily.
What you need is a BarChart with multiple BarDataSets where each set (in your case) represents count of each activity.
Here is an example of how to create a BarChart with multiple DataSets
Here is an tutorial of how to use MPAndroidChart with Realm.io
Example code (without realm.io)
List<String> xValues = ...; // "Denmark", "Finland", ...
XAxis xAxis = chart.getXAxis();
xAxis.setValueFormatter(new MyValueFormatter(xValues));
xAxis.setCenterAxisLabels(true);
// create 2 datasets
BarDataSet set1 = new BarDataSet(valuesMen, "Men");
set1.setColor(Color.BLUE);
BarDataSet set2 = new BarDataSet(valuesWomen, "Women");
set2.setColor(Color.RED);
BarData data = new BarData(set1, set2);
chart.setData(data);
chart.groupBars(...); // available since release v3.0.0
chart.invalidate(); // refresh
If you need further assistance, here is a detailed tutorial on grouped BarChart available on the wiki.
If you want to "stack" values in a BarChart above each other, you need to create a stacked-barchart: Android Stacked Bars Chart
**
Result Image
**
I am using the MPAndroidChart library for the bar chart, In that, I have used chart.setDrawValueAboveBar(false) to set bar values inside of bars, now I want to display the values vertically inside of bars.
Please help..Thank you in advance.
first make this false
mChart.setDrawValueAboveBar(false);
then enable draw values for your data set
barDataSet.setDrawValues(true);
Since you need to rotate the text as well.. you have to implement a custom renderer for your chart. If you want to know how renderer works, check this answer
I have provided a sample implementation below. You can modify it to control the position of the text as it suits you.
Custom Renderer
public class BarChartCustomRenderer extends BarChartRenderer {
public BarChartCustomRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
super(chart, animator, viewPortHandler);
}
public void drawValue(Canvas c, IValueFormatter formatter, float value, Entry entry, int dataSetIndex, float x, float y, int color) {
mValuePaint.setColor(color);
c.save();
c.rotate(90f, x, y);
Log.d("here", formatter.getFormattedValue(value, entry, dataSetIndex, mViewPortHandler) );
c.drawText(formatter.getFormattedValue(value, entry, dataSetIndex, mViewPortHandler), x, y, mValuePaint);
c.restore();
}
}
Chart Code
BarChart mChart = (BarChart) findViewById(R.id.barChart);
mChart.setDrawBarShadow(false);
mChart.setDrawValueAboveBar(false);
mChart.getDescription().setEnabled(false);
mChart.setDrawGridBackground(false);
//**add renderer**
BarChartCustomRenderer barChartCustomRenderer = new BarChartCustomRenderer(mChart, mChart.getAnimator(), mChart.getViewPortHandler());
mChart.setRenderer(barChartCustomRenderer);
XAxis xaxis = mChart.getXAxis();
xaxis.setDrawGridLines(false);
xaxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xaxis.setGranularity(0.5f);
xaxis.setGranularityEnabled(true);
xaxis.setDrawLabels(true);
xaxis.setDrawAxisLine(false);
YAxis yAxisLeft = mChart.getAxisLeft();
yAxisLeft.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART);
yAxisLeft.setDrawGridLines(false);
yAxisLeft.setDrawAxisLine(false);
yAxisLeft.setEnabled(false);
mChart.getAxisRight().setEnabled(false);
Legend legend = mChart.getLegend();
legend.setEnabled(false);
ArrayList<BarEntry> valueSet1 = new ArrayList<BarEntry>();
ArrayList<String> ylabels = new ArrayList<>();
for (int i = 0; i < 6; ++i) {
BarEntry entry = new BarEntry(i, (i + 1) * 2);
valueSet1.add(entry);
ylabels.add(" " + i);
}
List<IBarDataSet> dataSets = new ArrayList<>();
BarDataSet barDataSet = new BarDataSet(valueSet1, " ");
barDataSet.setColor(Color.CYAN);
barDataSet.setDrawValues(true);
dataSets.add(barDataSet);
BarData data = new BarData(dataSets);
data.setBarWidth(0.4f);
data.setValueTextSize(10f);
data.setValueTextColor(Color.BLACK);
mChart.setData(data);
mChart.setFitBars(true);
mChart.invalidate();
Result
class MyCustomRenderer(
chart: BarDataProvider, animator: ChartAnimator, viewPortHandler: ViewPortHandler
) : HorizontalBarChartRenderer(chart, animator, viewPortHandler) {
override fun drawValue(c: Canvas, valueText: String, x: Float, y: Float, color: Int) {
mValuePaint.color = color
val xPoint = Utils.convertDpToPixel(26f)
val yPoint = Utils.convertDpToPixel(16f)
c.drawText(valueText, xPoint , yPoint , mValuePaint)
}
}
I want to plot a sine wave, but instead my application generates a flat line. When I use series with random from jchartfree example everything works fine. I also use debugger to check if values are good. Values are different than zero
public void createDataset() {
XYSeriesCollection dataset = new XYSeriesCollection();
XYSeries series1 = new XYSeries("Object 1");
double A = 1;
double T = 1;
double Fs = 10;
double f = 200;
int rozmiar = (int) (T*Fs);
double[] x = new double[rozmiar];
for (int i = 0; i < rozmiar; i++)
{
x[i] = A * Math.sin(2 * Math.PI * f * i / Fs);
series1.add(i, x[i]);
}
dataset.addSeries(series1);
data = dataset;
}
//...
public void createChartPanel() {
//pWykres = new JPanel();
//if(java.util.Arrays.asList(getComponents()).contains(pWykres)){
//getContentPane().remove(pWykres);
//}
if(pWykres != null){
pWykres.removeAll();
pWykres.revalidate();
}
String chartTitle = "Objects Movement Chart";
String xAxisLabel = "X";
String yAxisLabel = "Y";
JFreeChart chart = ChartFactory.createXYLineChart(chartTitle,
xAxisLabel, yAxisLabel, dataset);
customizeChart(chart);
pWykres = new ChartPanel(chart);
getContentPane().add(pWykres, BorderLayout.CENTER);
setSize(620, 460);
//validate();
pWykres.repaint();
}
//endregion
//...
//region
private void customizeChart(JFreeChart chart) {
XYPlot plot = chart.getXYPlot();
XYSplineRenderer renderer;
renderer = new XYSplineRenderer();
renderer.setSeriesShapesVisible(0, false);
// sets paint color for each series
renderer.setSeriesPaint(0, Color.RED);
// sets thickness for series (using strokes)
renderer.setSeriesStroke(0, new BasicStroke(1.0f));
// sets paint color for plot outlines
//plot.setOutlinePaint(Color.BLUE);
//plot.setOutlineStroke(new BasicStroke(2.0f));
// sets renderer for lines
plot.setRenderer(renderer);
// sets plot background
plot.setBackgroundPaint(Color.WHITE);
// sets paint color for the grid lines
plot.setRangeGridlinesVisible(true);
plot.setRangeGridlinePaint(Color.BLACK);
plot.setDomainGridlinesVisible(true);
plot.setDomainGridlinePaint(Color.BLACK);
}
You are taking the sine of values that are always whole multiples of 2 * PI, so all of these values will be (approximately) zero, hence your graph will end up appearing flat unless it is scaled to show up these tiny values (which are just floating point errors).
x[i] = A * Math.sin(2 * Math.PI * f * i / Fs);
where A = 1 and f/Fs = 20 and i is integer
For example:
Math.sin(0) // 0.0
Math.sin(2 * Math.PI) // -2.4492935982947064E-16 (approximately zero)
Math.sin(4 * Math.PI) // -4.898587196589413E-16 (approximately zero)
To see the characteristic shape of a sine wave, you need to vary the input to the sin function by much smaller increments, e.g. pi/10 or less.
I am referring to :
Changing the shapes of points in scatter plot
But Am not able to see the shape of my x,y data points:
public void plotHysteresis()
{
s1= new double[6];
s2= new double[6];
s3= new double[6];
s4= new double[6];
int i=0;
for(i=0;i<6;i++)
{
s1[i]=hyst[i];
System.out.println("s1[" +i +"] : " +s1[i] +"\n");
}
for(i=0;i<6;i++)
{
s2[i]=hyst[10-i];
System.out.println("s2[" +i +"] : " +s2[i] +"\n");
}
for(i=0;i<6;i++)
{
s3[i]=hyst[11+i];
System.out.println("s3[" +i +"] : " +s3[i] +"\n");
}
for(i=0;i<6;i++)
{
s4[i]=hyst[21-i];
System.out.println("s4[" +i +"] : " +s4[i] +"\n");
}
// DefaultCategoryDataset dataset = new DefaultCategoryDataset();
XYSeries series3 = new XYSeries("III");
int x=-25;
System.out.println(x +"***\n");
for(i=5;i>=0;i--)
{
series3.add(x,s3[i]);
x=x+5;
}
System.out.println(x +"***\n");
XYSeries series4 = new XYSeries("IV");
for(i=0;i<6;i++)
{
x=x-5;
series4.add(x,s4[i] );
}
System.out.println(x +"###\n");
XYSeries series1 = new XYSeries("I");
x=0;
for(i=0;i<6;i++)
{
series1.add(x,s1[i] );
x=x+5;
}
// x=x-5;
System.out.println(x +"***\n");
XYSeries series2 = new XYSeries("II");
for(i=5;i>=0;i--)
{
x=x-5;
series2.add(x,s2[i] );
}
System.out.println(x +"***\n");
XYSeriesCollection dataset = new XYSeriesCollection();
dataset.addSeries(series3);
dataset.addSeries(series4);
dataset.addSeries(series1);
dataset.addSeries(series2);
JFreeChart chart = ChartFactory.createXYLineChart(
"Hysteresis Plot", // chart title
"Pounds(lb)", // domain axis label
"Movement(inch)", // range axis label
dataset, // data
PlotOrientation.VERTICAL, // orientation
false, // include legend
true, // tooltips
false // urls
);
chart.setBackgroundPaint(Color.white);
//chart.setBackgroundPaint(new Color(249, 231, 236));
/*CategoryPlot plot = (CategoryPlot) chart.getPlot();
plot.setBackgroundPaint(Color.lightGray);
plot.setRangeGridlinePaint(Color.white);
plot.getRenderer().setSeriesPaint(0, Color.BLUE);
plot.getRenderer().setSeriesPaint(1, Color.BLUE);
plot.getRenderer().setSeriesPaint(2, Color.BLUE);
plot.getRenderer().setSeriesPaint(3, Color.BLUE);*/
Shape cross = ShapeUtilities.createRegularCross(4, 3);
XYPlot plot = (XYPlot) chart.getPlot();
XYItemRenderer renderer = plot.getRenderer();
renderer.setSeriesPaint(0, Color.RED);
renderer.setSeriesPaint(1, Color.RED);
renderer.setSeriesPaint(2, Color.RED);
renderer.setSeriesPaint(3, Color.RED);
renderer.setSeriesShape(0, cross);
renderer.setSeriesShape(1, cross);
renderer.setSeriesShape(2, cross);
renderer.setSeriesShape(3, cross);
renderer.setSeriesVisible(0,true);
renderer.setSeriesVisible(1,true);
renderer.setSeriesVisible(2,true);
renderer.setSeriesVisible(3,true);
plot.setDomainCrosshairVisible(true);//Sets Y axis visible blue
plot.setRangeCrosshairVisible(true);//Sets X axis visible blue
/*LineAndShapeRenderer rend
= (LineAndShapeRenderer) plot.getRenderer();
rend.setShapesVisible(true);*/
//renderer.setDrawOutlines(true);
//renderer.setUseFillPaint(true);
//renderer.setFillPaint(Color.white);
ChartPanel frame = new ChartPanel(chart);
frame.setVisible(true);
frame.setSize(plotPanel.getWidth(),plotPanel.getHeight());
plotPanel.add(frame);
plotPanel.repaint();
}
The above code gives me output:
Earlier I was able to see the shape with CategoryPlot and LineChart as shown in commented part of the code, but it is not working for XYLineChart.
Please help
Thanks
Got the solution at:
How to get diamond shape for points in JFreechart
Can I change the color of
plot.setDomainCrosshairVisible(true);//Sets Y axis visible blue
plot.setRangeCrosshairVisible(true);//Sets X axis visible blue
want to make it black instead of it appearing as blue.
Ok got the answer:
How to get diamond shape for points in JFreechart
XYLineAndShapeRenderer r = (XYLineAndShapeRenderer) plot.getRenderer();
r.setSeriesShape(0, ShapeUtilities.createDiamond(5));
r.setSeriesShapesVisible(0, true);
Was not adding XYLineAndShapeRenderer in my code