Related
I want to plot Bar charts in an Android app similar to "Health" app of iOS.
Here are screenshots.
I tried to plot using MPAndroidChart.
I have seen examples given in that library but didn't able to plot as per my requirement.
I am able to display bars for 1 year graph (2nd screenshot) because it has 12 data points and 12 x-axis labels.
But for one day graph, there is a need to display bars between 2 labels of x-axis.
Also didn't understand how I map the time duration of 24 hours or any other on x-axis and its value.
Is there any way from which I can make X-axis as a time or date axis according to selected tabs ?
X-axis labels will be dynamic as per the current date and time. So I cannot set a fixed String array and also need to map values and x-axis timings.
Can anyone please help to map data and time axis?
You can draw the day times in the same way you render the months but instead of 12 values you will have 24 (one value for each time). The only difference is that the times graph has only 4 X-labels instead of 12 for months and it renders the times: 12AM, 6AM, 12PM, 6PM only and all the other time labels have empty values.
Below i will describe how you can plot and map each Y-Value with its corresponding X-Label with code examples:
1.Add the BarChart into an Activity xml layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.github.mikephil.charting.charts.BarChart
android:id="#+id/barChart"
android:layout_width="match_parent"
android:layout_height="300dp" />
</RelativeLayout>
2.Get a reference to the BarChart
mBarChart = findViewById(R.id.barChart);
3.Plot the Months Chart using the below function:
private void showMonthsBars(){
//input Y data (Months Data - 12 Values)
ArrayList<Double> valuesList = new ArrayList<Double>();
valuesList.add((double)1800);
valuesList.add((double)1600);
valuesList.add((double)500);
valuesList.add((double)1500);
valuesList.add((double)900);
valuesList.add((double)1700);
valuesList.add((double)400);
valuesList.add((double)2000);
valuesList.add((double)2500);
valuesList.add((double)3500);
valuesList.add((double)3000);
valuesList.add((double)1800);
//prepare Bar Entries
ArrayList<BarEntry> entries = new ArrayList<>();
for (int i = 0; i < valuesList.size(); i++) {
BarEntry barEntry = new BarEntry(i+1, valuesList.get(i).floatValue()); //start always from x=1 for the first bar
entries.add(barEntry);
}
//initialize x Axis Labels (labels for 13 vertical grid lines)
final ArrayList<String> xAxisLabel = new ArrayList<>();
xAxisLabel.add("J"); //this label will be mapped to the 1st index of the valuesList
xAxisLabel.add("F");
xAxisLabel.add("M");
xAxisLabel.add("A");
xAxisLabel.add("M");
xAxisLabel.add("J");
xAxisLabel.add("J");
xAxisLabel.add("A");
xAxisLabel.add("S");
xAxisLabel.add("O");
xAxisLabel.add("N");
xAxisLabel.add("D");
xAxisLabel.add(""); //empty label for the last vertical grid line on Y-Right Axis
//initialize xAxis
XAxis xAxis = mBarChart.getXAxis();
xAxis.enableGridDashedLine(10f, 10f, 0f);
xAxis.setTextColor(Color.BLACK);
xAxis.setTextSize(14);
xAxis.setDrawAxisLine(true);
xAxis.setAxisLineColor(Color.BLACK);
xAxis.setDrawGridLines(true);
xAxis.setGranularity(1f);
xAxis.setGranularityEnabled(true);
xAxis.setAxisMinimum(0 + 0.5f); //to center the bars inside the vertical grid lines we need + 0.5 step
xAxis.setAxisMaximum(entries.size() + 0.5f); //to center the bars inside the vertical grid lines we need + 0.5 step
xAxis.setLabelCount(xAxisLabel.size(), true); //draw x labels for 13 vertical grid lines
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxis.setXOffset(0f); //labels x offset in dps
xAxis.setYOffset(0f); //labels y offset in dps
xAxis.setCenterAxisLabels(true);
xAxis.setValueFormatter(new ValueFormatter() {
#Override
public String getFormattedValue(float value) {
return xAxisLabel.get((int) value);
}
});
//initialize Y-Right-Axis
YAxis rightAxis = mBarChart.getAxisRight();
rightAxis.setTextColor(Color.BLACK);
rightAxis.setTextSize(14);
rightAxis.setDrawAxisLine(true);
rightAxis.setAxisLineColor(Color.BLACK);
rightAxis.setDrawGridLines(true);
rightAxis.setGranularity(1f);
rightAxis.setGranularityEnabled(true);
rightAxis.setAxisMinimum(0);
rightAxis.setAxisMaximum(6000f);
rightAxis.setLabelCount(4, true); //draw y labels (Y-Values) for 4 horizontal grid lines starting from 0 to 6000f (step=2000)
rightAxis.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART);
//initialize Y-Left-Axis
YAxis leftAxis = mBarChart.getAxisLeft();
leftAxis.setAxisMinimum(0);
leftAxis.setDrawAxisLine(true);
leftAxis.setLabelCount(0, true);
leftAxis.setValueFormatter(new ValueFormatter() {
#Override
public String getFormattedValue(float value) {
return "";
}
});
//set the BarDataSet
BarDataSet barDataSet = new BarDataSet(entries, "Months");
barDataSet.setColor(Color.RED);
barDataSet.setFormSize(15f);
barDataSet.setDrawValues(false);
barDataSet.setValueTextSize(12f);
//set the BarData to chart
BarData data = new BarData(barDataSet);
mBarChart.setData(data);
mBarChart.setScaleEnabled(false);
mBarChart.getLegend().setEnabled(false);
mBarChart.setDrawBarShadow(false);
mBarChart.getDescription().setEnabled(false);
mBarChart.setPinchZoom(false);
mBarChart.setDrawGridBackground(true);
mBarChart.invalidate();
}
or plot the 24-Hours of a day like the below:
private void showDayHoursBars(){
//input Y data (Day Hours - 24 Values)
ArrayList<Double> valuesList = new ArrayList<Double>();
valuesList.add((double)0);
valuesList.add((double)0);
valuesList.add((double)0);
valuesList.add((double)0);
valuesList.add((double)0);
valuesList.add((double)0);
valuesList.add((double)0);
valuesList.add((double)100);
valuesList.add((double)270);
valuesList.add((double)430);
valuesList.add((double)110);
valuesList.add((double)30);
valuesList.add((double)200);
valuesList.add((double)180);
valuesList.add((double)0);
valuesList.add((double)140);
valuesList.add((double)160);
valuesList.add((double)0);
valuesList.add((double)100);
valuesList.add((double)0);
valuesList.add((double)750);
valuesList.add((double)1200);
valuesList.add((double)10);
valuesList.add((double)0);
//initialize x Axis Labels (labels for 25 vertical grid lines)
final ArrayList<String> xAxisLabel = new ArrayList<>();
for(int i = 0; i < 24; i++){
switch (i){
case 0:
xAxisLabel.add("12 AM"); //12AM - 5AM
break;
case 6:
xAxisLabel.add("6"); //6AM - 11AM
break;
case 12:
xAxisLabel.add("12 PM"); //12PM - 5PM
break;
case 18:
xAxisLabel.add("6"); //6PM - 11PM
break;
default:
xAxisLabel.add(String.format(Locale.US, "%02d", i)+":00");
break;
}
}
xAxisLabel.add(""); //empty label for the last vertical grid line on Y-Right Axis
//prepare Bar Entries
ArrayList<BarEntry> entries = new ArrayList<>();
for (int i = 0; i < valuesList.size(); i++) {
BarEntry barEntry = new BarEntry(i+1, valuesList.get(i).floatValue()); //start always from x=1 for the first bar
entries.add(barEntry);
}
//initialize xAxis
XAxis xAxis = mBarChart.getXAxis();
xAxis.enableGridDashedLine(10f, 10f, 0f);
xAxis.setTextColor(Color.BLACK);
xAxis.setTextSize(14);
xAxis.setDrawAxisLine(true);
xAxis.setAxisLineColor(Color.BLACK);
xAxis.setDrawGridLines(true);
xAxis.setGranularity(1f);
xAxis.setGranularityEnabled(true);
xAxis.setAxisMinimum(0 + 0.5f); //to center the bars inside the vertical grid lines we need + 0.5 step
xAxis.setAxisMaximum(entries.size() + 0.5f); //to center the bars inside the vertical grid lines we need + 0.5 step
xAxis.setLabelCount(5, true); //show only 5 labels (5 vertical grid lines)
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxis.setXOffset(0f); //labels x offset in dps
xAxis.setYOffset(0f); //labels y offset in dps
//xAxis.setCenterAxisLabels(true); //don't center the x labels as we are using a custom XAxisRenderer to set the label x, y position
xAxis.setValueFormatter(new ValueFormatter() {
#Override
public String getFormattedValue(float value) {
return xAxisLabel.get((int) value);
}
});
//initialize Y-Right-Axis
YAxis rightAxis = mBarChart.getAxisRight();
rightAxis.setTextColor(Color.BLACK);
rightAxis.setTextSize(14);
rightAxis.setDrawAxisLine(true);
rightAxis.setAxisLineColor(Color.BLACK);
rightAxis.setDrawGridLines(true);
rightAxis.setGranularity(1f);
rightAxis.setGranularityEnabled(true);
rightAxis.setAxisMinimum(0);
rightAxis.setAxisMaximum(1500f);
rightAxis.setLabelCount(4, true); //labels (Y-Values) for 4 horizontal grid lines
rightAxis.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART);
//initialize Y-Left-Axis
YAxis leftAxis = mBarChart.getAxisLeft();
leftAxis.setAxisMinimum(0);
leftAxis.setDrawAxisLine(true);
leftAxis.setLabelCount(0, true);
leftAxis.setValueFormatter(new ValueFormatter() {
#Override
public String getFormattedValue(float value) {
return "";
}
});
//set the BarDataSet
BarDataSet barDataSet = new BarDataSet(entries, "Hours");
barDataSet.setColor(Color.RED);
barDataSet.setFormSize(15f);
barDataSet.setDrawValues(false);
barDataSet.setValueTextSize(12f);
//set the BarData to chart
BarData data = new BarData(barDataSet);
mBarChart.setData(data);
mBarChart.setScaleEnabled(false);
mBarChart.getLegend().setEnabled(false);
mBarChart.setDrawBarShadow(false);
mBarChart.getDescription().setEnabled(false);
mBarChart.setPinchZoom(false);
mBarChart.setDrawGridBackground(true);
mBarChart.setXAxisRenderer(new XAxisRenderer(mBarChart.getViewPortHandler(), mBarChart.getXAxis(), mBarChart.getTransformer(YAxis.AxisDependency.LEFT)){
#Override
protected void drawLabel(Canvas c, String formattedLabel, float x, float y, MPPointF anchor, float angleDegrees) {
//for 6AM and 6PM set the correct label x position based on your needs
if(!TextUtils.isEmpty(formattedLabel) && formattedLabel.equals("6"))
Utils.drawXAxisValue(c, formattedLabel, x+Utils.convertDpToPixel(5f), y+Utils.convertDpToPixel(1f), mAxisLabelPaint, anchor, angleDegrees);
//for 12AM and 12PM set the correct label x position based on your needs
else
Utils.drawXAxisValue(c, formattedLabel, x+Utils.convertDpToPixel(20f), y+Utils.convertDpToPixel(1f), mAxisLabelPaint, anchor, angleDegrees);
}
});
mBarChart.invalidate();
}
From the above examples i have used some dummy data to show the mapping between Y-Values and X-Values. One main difference between Month-Chart and Day-Chart is that in Month-Chart i have centered the x-labels inside each Bar using the xAxis.setCenterAxisLabels(true); and in Day-Chart i have used a custom XAxisRenderer to align each label to the correct x,y position based on your screenshot.
Below are the results for the above codes:
Months-BarChart:
DayHours-BarChart:
Note: This was tested with com.github.PhilJay:MPAndroidChart:v3.1.0.
Example to map X-Axis Timestamps with Y-Values:
In case you already have the Timestamps for X-Axis you can convert them to the appropriate String Label value which will be mapped to the corresponding index of Y-Data values.
Below is an example of how you can plot the Times-Graph starting from the timestamp eg: 27-02-2022 16:00. Here i am converting the Timestamp to a Calendar object using the cal.setTimeInMillis() and then i am using this Calendar to get the new hour in the loop using the cal.add(Calendar.HOUR_OF_DAY, 1); which is used as the Time label in X-Axis like String dayHour = DateFormat.format("hh a", cal).toString().toUpperCase(); in the format you wish to be dispayed in X-Axis. Each Y-Data value corresponds to the appropriate index of X-Axis. The below example starts from 4 PM until 3 PM to handle the 24 hours of a day:
//input Y data (Day Hours - 24 Values)
ArrayList<Double> valuesList = new ArrayList<Double>();
valuesList.add((double)0);
valuesList.add((double)0);
valuesList.add((double)0);
valuesList.add((double)0);
valuesList.add((double)0);
valuesList.add((double)0);
valuesList.add((double)0);
valuesList.add((double)100);
valuesList.add((double)270);
valuesList.add((double)430);
valuesList.add((double)110);
valuesList.add((double)30);
valuesList.add((double)200);
valuesList.add((double)180);
valuesList.add((double)0);
valuesList.add((double)140);
valuesList.add((double)160);
valuesList.add((double)0);
valuesList.add((double)100);
valuesList.add((double)0);
valuesList.add((double)750);
valuesList.add((double)1200);
valuesList.add((double)10);
valuesList.add((double)0);
//in case you already have the timestamp for a day then do the following:
//convert the timestamp to calendar by using the cal.setTimeInMillis() and then use the Calendar to iterate the 24H using the cal.add(Calendar.HOUR_OF_DAY, 1);
//like the below example:
//get a dummy TimeStamp for 27-02-2022 16:00
Date date = new Date();
long dateTimeStamp = 0;
try {
String strDate = "27-02-2022 16:00";
date = new SimpleDateFormat("dd-MM-yyyy HH:mm", Locale.US).parse(strDate);
if(date!=null) {
dateTimeStamp = date.getTime();
}
}catch (Exception e){}
//convert the above timestamp to Calendar using the cal.setTimeInMillis()
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(dateTimeStamp);
//this is the day from the above Calendar
String day = DateFormat.format("dd-MM-yyyy HH:mm", cal).toString(); //this is: 27-02-2022 16:00
//initialize x Axis Labels (labels for 25 vertical grid lines)
final ArrayList<String> xAxisLabel = new ArrayList<>();
//iterate all 24 hours to add the label for each time
for(int i = 0; i < 24; i++){
//get current hour in format "hh a" which is eg: 12 AM, 01 AM, etc..
String dayHour = DateFormat.format("hh a", cal).toString().toUpperCase();
//get the hour 0-11 AM or 0-11 PM
int hour = cal.get(Calendar.HOUR);
//check if the above hour is AM or PM (0=AM, 1=PM)
int amPm = cal.get(Calendar.AM_PM);
//this is the current TimeStamp for the current working hour in the loop:
long timeStamp = cal.getTimeInMillis();
//add the dayHour String as the label you want to display in xAxis
xAxisLabel.add(dayHour); //this will be mapped to the corresponding index of the valuesList (Y-Data List)
//increment the Calendar.HOUR_OF_DAY plus +1 to go to the next hour in 24H format
cal.add(Calendar.HOUR_OF_DAY, 1);
}
xAxisLabel.add(""); //empty label for the last vertical grid line on Y-Right Axis
Here is the full code:
private void showDayHoursBars() {
//input Y data (Day Hours - 24 Values)
ArrayList<Double> valuesList = new ArrayList<Double>();
valuesList.add((double)0);
valuesList.add((double)0);
valuesList.add((double)0);
valuesList.add((double)0);
valuesList.add((double)0);
valuesList.add((double)0);
valuesList.add((double)0);
valuesList.add((double)100);
valuesList.add((double)270);
valuesList.add((double)430);
valuesList.add((double)110);
valuesList.add((double)30);
valuesList.add((double)200);
valuesList.add((double)180);
valuesList.add((double)0);
valuesList.add((double)140);
valuesList.add((double)160);
valuesList.add((double)0);
valuesList.add((double)100);
valuesList.add((double)0);
valuesList.add((double)750);
valuesList.add((double)1200);
valuesList.add((double)10);
valuesList.add((double)0);
//in case you already have the timestamp for a day then do the following:
//convert the timestamp to calendar by using the cal.setTimeInMillis() and then use the Calendar to iterate the 24H using the cal.add(Calendar.HOUR_OF_DAY, 1);
//like the below example:
//get a dummy TimeStamp for 27-02-2022 16:00
Date date = new Date();
long dateTimeStamp = 0;
try {
String strDate = "27-02-2022 16:00";
date = new SimpleDateFormat("dd-MM-yyyy HH:mm", Locale.US).parse(strDate);
if(date!=null) {
dateTimeStamp = date.getTime();
}
}catch (Exception e){}
//convert the above timestamp to Calendar using the cal.setTimeInMillis()
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(dateTimeStamp);
//this is the day from the above Calendar
String day = DateFormat.format("dd-MM-yyyy HH:mm", cal).toString(); //this is: 27-02-2022 16:00
//initialize x Axis Labels (labels for 25 vertical grid lines)
final ArrayList<String> xAxisLabel = new ArrayList<>();
//iterate all 24 hours to add the label for each time
for(int i = 0; i < 24; i++){
//get current hour in format "hh a" which is eg: 12 AM, 01 AM, etc..
String dayHour = DateFormat.format("hh a", cal).toString().toUpperCase();
//get the hour 0-11 AM or 0-11 PM
int hour = cal.get(Calendar.HOUR);
//check if the above hour is AM or PM (0=AM, 1=PM)
int amPm = cal.get(Calendar.AM_PM);
//this is the current TimeStamp for the current working hour in the loop:
long timeStamp = cal.getTimeInMillis();
//add the dayHour String as the label you want to display in xAxis
xAxisLabel.add(dayHour); //this will be mapped to the corresponding index of the valuesList (Y-Data List)
//increment the Calendar.HOUR_OF_DAY plus +1 to go to the next hour in 24H format
cal.add(Calendar.HOUR_OF_DAY, 1);
}
xAxisLabel.add(""); //empty label for the last vertical grid line on Y-Right Axis
//prepare Bar Entries
ArrayList<BarEntry> entries = new ArrayList<>();
for (int i = 0; i < valuesList.size(); i++) {
BarEntry barEntry = new BarEntry(i+1, valuesList.get(i).floatValue()); //start always from x=1 for the first bar
entries.add(barEntry);
}
//initialize xAxis
XAxis xAxis = mBarChart.getXAxis();
xAxis.enableGridDashedLine(10f, 10f, 0f);
xAxis.setTextColor(Color.BLACK);
xAxis.setTextSize(14);
xAxis.setDrawAxisLine(true);
xAxis.setAxisLineColor(Color.BLACK);
xAxis.setDrawGridLines(true);
xAxis.setGranularity(1f);
xAxis.setGranularityEnabled(true);
xAxis.setAxisMinimum(0 + 0.5f); //to center the bars inside the vertical grid lines we need + 0.5 step
xAxis.setAxisMaximum(entries.size() + 0.5f); //to center the bars inside the vertical grid lines we need + 0.5 step
xAxis.setLabelCount(5, true); //show only 5 labels (5 vertical grid lines)
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxis.setXOffset(0f); //labels x offset in dps
xAxis.setYOffset(0f); //labels y offset in dps
//xAxis.setCenterAxisLabels(true); //don't center the x labels as we are using a custom XAxisRenderer to set the label x, y position
xAxis.setValueFormatter(new ValueFormatter() {
#Override
public String getFormattedValue(float value) {
return xAxisLabel.get((int) value);
}
});
//initialize Y-Right-Axis
YAxis rightAxis = mBarChart.getAxisRight();
rightAxis.setTextColor(Color.BLACK);
rightAxis.setTextSize(14);
rightAxis.setDrawAxisLine(true);
rightAxis.setAxisLineColor(Color.BLACK);
rightAxis.setDrawGridLines(true);
rightAxis.setGranularity(1f);
rightAxis.setGranularityEnabled(true);
rightAxis.setAxisMinimum(0);
rightAxis.setAxisMaximum(1500f);
rightAxis.setLabelCount(4, true); //labels (Y-Values) for 4 horizontal grid lines
rightAxis.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART);
//initialize Y-Left-Axis
YAxis leftAxis = mBarChart.getAxisLeft();
leftAxis.setAxisMinimum(0);
leftAxis.setDrawAxisLine(true);
leftAxis.setLabelCount(0, true);
leftAxis.setValueFormatter(new ValueFormatter() {
#Override
public String getFormattedValue(float value) {
return "";
}
});
//set the BarDataSet
BarDataSet barDataSet = new BarDataSet(entries, "Hours");
barDataSet.setColor(Color.RED);
barDataSet.setFormSize(15f);
barDataSet.setDrawValues(false);
barDataSet.setValueTextSize(12f);
//set the BarData to chart
BarData data = new BarData(barDataSet);
mBarChart.setData(data);
mBarChart.setScaleEnabled(false);
mBarChart.getLegend().setEnabled(false);
mBarChart.setDrawBarShadow(false);
mBarChart.getDescription().setEnabled(false);
mBarChart.setPinchZoom(false);
mBarChart.setDrawGridBackground(true);
mBarChart.setXAxisRenderer(new XAxisRenderer(mBarChart.getViewPortHandler(), mBarChart.getXAxis(), mBarChart.getTransformer(YAxis.AxisDependency.LEFT)){
#Override
protected void drawLabel(Canvas c, String formattedLabel, float x, float y, MPPointF anchor, float angleDegrees) {
//for 6AM and 6PM set the correct label x position based on your needs
if(!TextUtils.isEmpty(formattedLabel) && formattedLabel.equals("6"))
Utils.drawXAxisValue(c, formattedLabel, x+Utils.convertDpToPixel(5f), y+Utils.convertDpToPixel(1f), mAxisLabelPaint, anchor, angleDegrees);
//for 12AM and 12PM set the correct label x position based on your needs
else
Utils.drawXAxisValue(c, formattedLabel, x+Utils.convertDpToPixel(20f), y+Utils.convertDpToPixel(1f), mAxisLabelPaint, anchor, angleDegrees);
}
});
mBarChart.invalidate();
}
And here is the new Result (4 PM until 3 PM):
Example 2 to map X-Axis Timestamps with Y-Values:
Below is another example of how you can map the X(timestamps) with Y(Values):
//prepare X,Y data
ArrayList<Double> valuesList = new ArrayList<Double>();
ArrayList<Long> xAxisLabel = new ArrayList<>();
for(int i = 0; i < 24; i++){
valuesList.add((double)100+i); //y-values
xAxisLabel.add(1645970400000L+i); //x-timestamps
}
xAxisLabel.add(0L); //empty x for the last vertical grid line on Y-Right Axis
//prepare Bar Entries
ArrayList<BarEntry> entries = new ArrayList<>();
for (int i = 0; i < valuesList.size(); i++) {
BarEntry barEntry = new BarEntry(i+1, valuesList.get(i).floatValue()); //start always from x=1 for the first bar
entries.add(barEntry);
}
And based on the above mapping you can use the setLabelCount to render how many labels you wish on X-Axis. Eg: using xAxis.setLabelCount(xAxisLabel.size(), true); you can render all the labels in X-axis.
The callback getFormattedValue(float value) is called every time a specific X-index is ready to be rendered on the graph so you have to get the index first and from that you can get the correct timestamp from the list like in the below example:
//initialize xAxis
XAxis xAxis = mBarChart.getXAxis();
xAxis.setLabelCount(xAxisLabel.size(), true); //show all the labels
xAxis.setValueFormatter(new ValueFormatter() {
#Override
public String getFormattedValue(float value) {
int xIndex = (int) value;
if(xIndex == xAxisLabel.size()-1)
return "";
Long timeStamp = xAxisLabel.get(xIndex);
//here convert the timestamp to the appropriate String value
return String.valueOf(timeStamp);
}
});
And below is the X-Timestamps Result for all labels based on the above sample. To render the labels like in the below picture i have made also the below changes:
xAxis.setLabelRotationAngle(90); //this rotates the x labels to 90 degrees
mBarChart.setXAxisRenderer(new XAxisRenderer(mBarChart.getViewPortHandler(), mBarChart.getXAxis(), mBarChart.getTransformer(YAxis.AxisDependency.LEFT)){
#Override
protected void drawLabel(Canvas c, String formattedLabel, float x, float y, MPPointF anchor, float angleDegrees) {
Utils.drawXAxisValue(c, formattedLabel, x+Utils.convertDpToPixel(5f), y, mAxisLabelPaint, anchor, angleDegrees);
}
});
Timestamps Result:
X-Timestamps for every 10 seconds with a horizontal scroll. Each page has 10 timestamps.
Below i will show the key points of how you can modify the above code to handle the X-Y mapping to show X-Timestamps for every 10 seconds:
int visibleXValuesPerPage = 10;
//get a dummy TimeStamp for 27-02-2022 16:00
Date date = new Date();
long dateTimeStamp = 0;
try {
String strDate = "27-02-2022 16:00";
date = new SimpleDateFormat("dd-MM-yyyy HH:mm", Locale.US).parse(strDate);
if(date!=null) {
dateTimeStamp = date.getTime();
}
}catch (Exception e){}
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(dateTimeStamp);
ArrayList<Double> valuesList = new ArrayList<Double>();
ArrayList<Long> xAxisLabel = new ArrayList<>();
//1h = 3600 seconds
//1d = 86400 seconds
//1w = 604800 seconds
int seconds = 3600;
//the loop here is incremented by 10 seconds (i+=10)
for(int i = 0; i < seconds; i+=10){
String time = DateFormat.format("dd-MM-yyyy HH:mm:ss", cal).toString();
long timeStamp = cal.getTimeInMillis();
valuesList.add((double)i);
xAxisLabel.add(timeStamp);
cal.add(Calendar.SECOND, 10);
}
ArrayList<BarEntry> entries = new ArrayList<>();
for (int i = 0; i < valuesList.size(); i++) {
BarEntry barEntry = new BarEntry(i+1, valuesList.get(i).floatValue());
entries.add(barEntry);
}
XAxis xAxis = mBarChart.getXAxis();
xAxis.setAxisMinimum(0 + 0.5f);
xAxis.setAxisMaximum(entries.size() + 0.5f);
xAxis.setLabelCount(visibleXValuesPerPage, false);
xAxis.setLabelRotationAngle(90);
xAxis.setCenterAxisLabels(true);
xAxis.setValueFormatter(new ValueFormatter() {
#Override
public String getFormattedValue(float value) {
if (value >= 0)
{
int xIndex = (int)value;
if (value > xAxisLabel.size()-1 && value <= xAxisLabel.size())
return "";
if (xIndex < xAxisLabel.size())
{
Long timeStamp = xAxisLabel.get(xIndex);
//here convert the timestamp to the appropriate String value
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(timeStamp);
String time = DateFormat.format("dd-MM-yyyy HH:mm:ss", cal).toString();
return String.valueOf(time);
}
return "";
}
return "";
}
});
//set the BarData to chart
mBarChart.setVisibleXRangeMaximum(visibleXValuesPerPage);
mBarChart.invalidate();
So I am trying to do a weekly chart with current weeks data vs previous weeks data using MPAndroidChart. I can do single bars just fine and everything lines up, but when I try to do multi bar groups it does the following.
Is there something else I need to be doing? I only have 2 bars per group for each day. Here is my current code:
BarData barDataPayments = new BarData(barDataSetCurrentWeek, barDataSetPrevWeek);
barChartPayments.setData(barDataPayments);
XAxis xAxisPayments = barChartPayments.getXAxis();
xAxisPayments.setValueFormatter(new IndexAxisValueFormatter(labelWeekdayNames));
xAxisPayments.setYOffset(5);
xAxisPayments.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxisPayments.setLabelCount(labelWeekdayNames.size());
xAxisPayments.setGranularity(1f);
xAxisPayments.setCenterAxisLabels(false);
xAxisPayments.setDrawGridLines(false);
float groupSpace = 0.08f;
float barSpace = 0.03f;
float barWidth = 0.2f;
barDataPayments.setBarWidth(barWidth);
barChartPayments.getXAxis().setAxisMinimum(0);
barChartPayments.getXAxis().setAxisMaximum(0 + barChartPayments.getBarData().getGroupWidth(groupSpace, barSpace) * 14);
barChartPayments.groupBars(0, groupSpace, barSpace);
barChartPayments.invalidate();
here I have a snippet it may help you,
BarData barData = new BarData();
int i=10;
for (ExpenseData expenseData : results) {
List<BarEntry> barEntryList = new ArrayList<>();
barEntryList.add(new BarEntry(i, (float) expenseData.getBalance()));
BarDataSet barDataSet = new BarDataSet(barEntryList, expenseData.getCategory());
int rand = (int) (i*Math.random());
barDataSet.setBarBorderWidth(1);
barDataSet.setBarBorderColor(Color.rgb(rand,i*20,i));
barDataSet.setColor(Color.rgb(rand,i*20,i));
barData.addDataSet(barDataSet);
i+=10;
}
barChart.setData(barData);
So what I ended up having to do is manually set the data. It took a little tweaking to get it just how I wanted it, I am sure there is a more proper way, but so far this was the easiest way that I have currently found.
xAxisMiles.setYOffset(0); // <---- CHANGED TO ZERO
xAxisMiles.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxisMiles.setLabelCount(labelWeekdayNames.size());
xAxisMiles.setGranularity(1f);
xAxisMiles.setCenterAxisLabels(true); // <---- SET TO TRUE
xAxisMiles.setDrawGridLines(false);
//IMPORTANT *****
barDataPayments.setBarWidth(0.40f);
barDataPayments.getXAxis().setAxisMinimum(0);
barDataPayments.getXAxis().setAxisMaximum(7);
barDataPayments.groupBars(0, 0.10f, 0.05f);
//***** IMPORTANT*/
I'm using the MPAndroidChart to create a bar chart. My configurations:
<string-array name="months_initials">
<item>J</item>
<item>F</item>
<item>M</item>
<item>A</item>
<item>M</item>
<item>J</item>
<item>J</item>
<item>A</item>
<item>S</item>
<item>O</item>
<item>N</item>
<item>D</item>
</string-array>
...
String[] months = getResources().getStringArray(R.array.months_initials);
chart.getXAxis().setCenterAxisLabels(true);
chart.getXAxis().setLabelCount(months.length, true);
chart.getXAxis().setValueFormatter(new IndexAxisValueFormatter(months) {
#Override
public String getFormattedValue(float value, AxisBase axis) {
Timber.i("index = %s", value);
return super.getFormattedValue(value, axis);
}
});
index = 0.5
index = 1.5909091
index = 2.6818182
index = 3.7727275
index = 4.8636365
index = 5.9545455
index = 7.0454545
index = 8.136364
index = 9.227273
index = 10.318182
index = 11.409091
And this is the result:
Now, if I change it to: return super.getFormattedValue(value-0.5f, axis);
The result is:
Again, if I add another change: chart.getXAxis().setLabelCount(Integer.MAX_VALUE, true);
index = 0.5
index = 1.0
index = 1.5
index = 2.0
index = 2.5
index = 3.0
index = 3.5
index = 4.0
index = 4.5
index = 5.0
index = 5.5
index = 6.0
index = 6.5
index = 7.0
index = 7.5
index = 8.0
index = 8.5
index = 9.0
index = 9.5
index = 10.0
index = 10.5
index = 11.0
index = 11.5
index = 12.0
index = 12.5
The result is:
A bit "hammer time" but it would work for me, unfortunately the labels are not correctly centered.
So, what's happening here, what am I missing? How can I achieve my final result?
Thanks for your time.
ps: opened an issue too.
EDIT full setup code:
String[] months = getResources().getStringArray(R.array.months_initials);
BarChart chart = binding.barChart;
chart.setTouchEnabled(false);
chart.getDescription().setEnabled(false);
chart.getLegend().setEnabled(false);
chart.getAxisLeft().setEnabled(false);
chart.getAxisRight().setEnabled(false);
chart.getXAxis().setPosition(XAxis.XAxisPosition.BOTTOM);
chart.getXAxis().setDrawAxisLine(false);
chart.getXAxis().setDrawGridLines(false);
chart.getXAxis().setCenterAxisLabels(true);
chart.getXAxis().setLabelCount(Integer.MAX_VALUE, true);
chart.getXAxis().setValueFormatter(new IndexAxisValueFormatter(months) {
#Override
public String getFormattedValue(float value, AxisBase axis) {
Timber.i("index = %s", value);
return super.getFormattedValue(value - 0.5f, axis);
}
});
chart.getXAxis().setTextColor(ContextCompat.getColor(this, R.color.grey));
chart.getXAxis().setTextSize(12);
BarDataSet barData = new BarDataSet(data, "data");
barData.setColor(ContextCompat.getColor(this, R.color.chart_bar));
barData.setDrawValues(false);
ArrayList<IBarDataSet> dataSets = new ArrayList<>();
dataSets.add(barData);
binding.barChart.setData(new BarData(dataSets));
binding.barChart.animateY(1000, Easing.EasingOption.Linear);
I can't exactly pin point the issue in your code but it is probably something do with the granularity of the chart.
But here's a rough working example. Hope this helps!
String[] months = {"Jan", "Feb", "Mar", "Apr", "May", "Jun"};
BarChart mChart = (BarChart) findViewById(R.id.barChart);
mChart.setDrawBarShadow(false);
mChart.setDrawValueAboveBar(false);
mChart.getDescription().setEnabled(false);
mChart.setDrawGridBackground(false);
XAxis xaxis = mChart.getXAxis();
xaxis.setDrawGridLines(false);
xaxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xaxis.setGranularity(1f);
xaxis.setDrawLabels(true);
xaxis.setDrawAxisLine(false);
xaxis.setValueFormatter(new IndexAxisValueFormatter(months));
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>();
for (int i = 0; i < 6; ++i) {
BarEntry entry = new BarEntry(i, (i+1)*10);
valueSet1.add(entry);
}
List<IBarDataSet> dataSets = new ArrayList<>();
BarDataSet barDataSet = new BarDataSet(valueSet1, " ");
barDataSet.setColor(Color.CYAN);
barDataSet.setDrawValues(false);
dataSets.add(barDataSet);
BarData data = new BarData(dataSets);
mChart.setData(data);
mChart.invalidate();
RESULT
I had same issue and resolved by doing this :
barChart.getXAxis().setLabelCount(barDataSet.getEntryCount());
Moreover if you are using only one dataset then instead of this:
ArrayList<IBarDataSet> dataSets = new ArrayList<>();
dataSets.add(barData);
Use:
BarDataSet barDataSet = new BarDataSet(yourValauesHere, "Legend Text here");
For reference please follow example below:
barEntries = new ArrayList<BarEntry>();
barEntries.add(new BarEntry(0, 1));
barEntries.add(new BarEntry(1, 2));
barEntries.add(new BarEntry(2, 4));
barEntries.add(new BarEntry(3, 6));
barEntries.add(new BarEntry(4, 5));
barEntries.add(new BarEntry(5, 7));
barDataSet = new BarDataSet(barEntries, "Contracts");
barDataSet.setAxisDependency(YAxis.AxisDependency.LEFT);
barDataSet.setColor(getColor("defaultYellow"));
barDataSet.setHighlightEnabled(true);
barDataSet.setHighLightColor(Color.RED);
barDataSet.setValueTextSize(defaultValueTextSize);
barDataSet.setValueTextColor(getColor("primaryDark"));
BarData barData = new BarData(barDataSet);
barChart.getDescription().setText("No. of Contracts signed in 6
months");
barChart.getDescription().setTextSize(12);
barChart.getAxisLeft().setAxisMinimum(0);
barChart.getXAxis().setPosition(XAxis.XAxisPosition.BOTH_SIDED);
barChart.getXAxis().setValueFormatter(new
IndexAxisValueFormatter(getXAxisValues()));
barChart.animateY(1000);
barChart.setData(barData);
Getting xAxis values method:
private ArrayList<String> getXAxisValues()
{
ArrayList<String> labels = new ArrayList<String> ();
labels.add( "JAN");
labels.add( "FEB");
labels.add( "MAR");
labels.add( "APR");
labels.add( "MAY");
labels.add( "JUN");
return labels;
}
Handling axis labels like this can be rather complicated sometimes. You have to first make sure the labels are shown at the interval and spacing you want, then you have to use an axis value formatter to transform them to some other string in this case.
The TL;DR is
setCenterAxisLabels does not do what it sounds like it does - don't use it. Labels are already centered under their axis values by default. This centers the labels between axis values.
Use numeric labels to debug label position before applying IndexAxisValueFormatter to make sure you have integer values in the expected locations.
IndexAxisValueFormatter has some buggy rounding logic and doesn't display labels for values between X.5 and X.999999, while values between X.0 and X.499999 get rounded down to X and labels[X] is shown. Use a custom ValueFormatter to fix those issues.
Long Answer
To start with, I used the following test data, which reproduces the behavior you saw (the behavior will be a bit different if your x values start at 0 instead of 1, but the same type of issues will show up). The code here is in Kotlin, but the solution is equally applicable to Java.
val months = listOf("J","F","M","A","M","J","J","A","S","O","N","D")
val yvals = listOf(1f,2f,5f,0.1f,0.5f,1f,0.5f,2f,1f,2.5f,2f,0.2f)
val entries = yvals.mapIndexed { i, y -> BarEntry((i+1).toFloat(),y)}
val barDataSet = BarDataSet(entries,"data")
Initial Approach
When you plot this using your initial chart settings
val xAxis = barChart.xAxis
xAxis.position = XAxis.XAxisPosition.BOTTOM
xAxis.setDrawGridLines(true)
xAxis.setDrawAxisLine(true)
xAxis.setCenterAxisLabels(true)
xAxis.setLabelCount(months.size, true)
xAxis.valueFormatter = IndexAxisValueFormatter(months)
you get the first behavior you saw where half the labels are gone and the rest are not positioned under the bars:
To better understand why this is happening, we can use a different axis value formatter to see where MPAndroidChart is trying to place labels. This will write the numeric values instead of trying to convert them to array indices:
class VerboseAxisValueFormatter : ValueFormatter() {
override fun getFormattedValue(value: Float): String {
return "%.2f".format(value)
}
}
This gives (the values match the ones you saw when debugging)
Looking at the source code for IndexAxisValueFormatter
public String getFormattedValue(float value, AxisBase axis) {
int index = Math.round(value);
if (index < 0 || index >= mValueCount || index != (int)value)
return "";
return mValues[index];
}
the final check in there (index != (int)value) means that if the rounded value is not equal to the truncated (floor) value then the result is blank. This means that for values from 1.0 to 1.499999 it will return mValues[1] but for values from 1.5 to 1.99999 it will return a blank string. That's why the entire first half of the chart was missing labels - the x values it chose to draw axis labels at had decimal portions greater than 0.5.
Second Approach
So for your second iteration, you set the label count to Integer.MAX_VALUE and shifted the values down by 0.5. Doing that here (but leaving the numeric labels on) gives the following, where the labels have nice even spacing (default granularity here is 0.5). If you replaced the integer values here (0.0, 1.0, etc) you would get the result you saw with your "offset label case" (labels were present but not centered)
However, the left bar is centered at 1.0 which is very different from where the label was drawn. This is because xAxis.setCenterAxisLabels(true) does not center labels under their values (which would make sense, but that's actually the default behavior already) but instead centers labels between the actual axis values. Turning this off and removing the 0.5f shift, gives
Now, we finally have integer value labels centered below bars and in the correct positions, which means we can use the IndexAxisValueFormatter - however, the array indices are 0-based and the data set is 1-based. To fix that, you can subtract 1 from the x values either in the data set or in the formatter. Even so, it's not quite right - we get an extra label on the left edge because the -0.5 there rounds and truncates to 0.
Working Solution
In the end, once we have set the label position correctly, the best choice is still to write a custom ValueFormatter that doesn't have all the issues in the built-in IndexAxisValueFormatter. Instead of using index != (int)value we use abs(index - x) > 0.01f so that the behavior is symmetric and only shows labels actually at integer positions.
class FixedIndexAxisValueFormatter(private val offset: Float, private val labels: List<String>) : ValueFormatter()
{
override fun getFormattedValue(value: Float): String {
val x = value - offset
val index = x.roundToInt()
return if( index < 0 || index >= labels.size || abs(index - x) > 0.01f) ""
else labels[index]
}
}
Using this formatter finally gives the correct result
The final settings to get this were
// Force it to draw labels at all positions controlled by the axis
// bounds and granularity
xAxis.setLabelCount(Integer.MAX_VALUE, true)
// These are already the defaults in this case so they're not necessary,
// but good to specify explicitly in case the defaults change later
xAxis.axisMinimum = 0.5f
xAxis.axisMaximum = 12.5f
xAxis.granularity = 0.5f
// Use a better value formatter, with an offset of 1 since the
// data set started at 1
xAxis.valueFormatter = FixedIndexAxisValueFormatter(1f, months)
I have this grouped bar chart that can set to any number of bars in a group. It will first start with 2 bars in a group. Then if user clicks the button. it will add a new bar in the group. and click again. new bar again. and so on. my goal is to redraw the chart with new bar but the x-axis label must always be in the center of the grouped bar.
I always have a problem on the time when there are 5 bars.
Above, Feb is not centered on the 2nd grouped bar anymore.
Sample code in here:
https://github.com/PhilJay/MPAndroidChart/issues/3505
(I posted the question here too bec it seems that github is not maintained anymore)
In the code, the function resize is core of it that wants to reduce the width of the bars when new bars are added and at the same time increase the scale minima. But to no avail.
Need help
Check this sample grouped bar chart code.
public class MpAndroidGroupedBarChart extends AppCompatActivity {
BarChart mChart;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mChart = (BarChart) findViewById(R.id.bar_chart);
mChart.setDrawBarShadow(false);
mChart.getDescription().setEnabled(false);
mChart.setPinchZoom(false);
mChart.setDrawGridBackground(false);
// empty labels so that the names are spread evenly
String[] labels = {"", "Name1", "Name2", "Name3", "Name4", "Name5", ""};
IAxisValueFormatter xAxisFormatter = new LabelFormatter(mChart, labels);
XAxis xAxis = mChart.getXAxis();
xAxis.setCenterAxisLabels(true);
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxis.setDrawGridLines(false);
xAxis.setGranularity(1f); // only intervals of 1 day
xAxis.setTextColor(Color.BLACK);
xAxis.setTextSize(12);
xAxis.setAxisLineColor(Color.WHITE);
xAxis.setAxisMinimum(1f);
xAxis.setValueFormatter(xAxisFormatter);
YAxis leftAxis = mChart.getAxisLeft();
leftAxis.setTextColor(Color.BLACK);
leftAxis.setTextSize(12);
leftAxis.setAxisLineColor(Color.WHITE);
leftAxis.setDrawGridLines(false);
leftAxis.setGranularity(2);
leftAxis.setLabelCount(8, true);
leftAxis.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART);
mChart.getAxisRight().setEnabled(false);
mChart.getLegend().setEnabled(false);
float[] valOne = {10, 20, 30, 40, 50};
float[] valTwo = {60, 50, 40, 30, 20};
float[] valThree = {20, 10, 30, 60, 40};
float[] valFour = {40, 10, 60, 30, 10};
float[] valFive = {60, 50, 40, 30, 50};
ArrayList<BarEntry> barOne = new ArrayList<>();
ArrayList<BarEntry> barTwo = new ArrayList<>();
ArrayList<BarEntry> barThree = new ArrayList<>();
ArrayList<BarEntry> barFour = new ArrayList<>();
ArrayList<BarEntry> barFive = new ArrayList<>();
for (int i = 0; i < valOne.length; i++) {
barOne.add(new BarEntry(i, valOne[i]));
barTwo.add(new BarEntry(i, valTwo[i]));
barThree.add(new BarEntry(i, valThree[i]));
barFour.add(new BarEntry(i, valFour[i]));
barFive.add(new BarEntry(i, valFive[i]));
}
BarDataSet set1 = new BarDataSet(barOne, "barOne");
set1.setColor(Color.BLUE);
BarDataSet set2 = new BarDataSet(barTwo, "barTwo");
set2.setColor(Color.MAGENTA);
BarDataSet set3 = new BarDataSet(barThree, "barTwo");
set3.setColor(Color.RED);
BarDataSet set4 = new BarDataSet(barFour, "barTwo");
set4.setColor(Color.GREEN);
BarDataSet set5 = new BarDataSet(barFive, "barTwo");
set5.setColor(Color.LTGRAY);
set1.setHighlightEnabled(false);
set1.setDrawValues(false);
set2.setHighlightEnabled(false);
set2.setDrawValues(false);
set3.setHighlightEnabled(false);
set3.setDrawValues(false);
set4.setHighlightEnabled(false);
set4.setDrawValues(false);
set5.setHighlightEnabled(false);
set5.setDrawValues(false);
ArrayList<IBarDataSet> dataSets = new ArrayList<IBarDataSet>();
dataSets.add(set1);
dataSets.add(set2);
dataSets.add(set3);
dataSets.add(set4);
dataSets.add(set5);
BarData data = new BarData(dataSets);
float groupSpace = 0.2f;
float barSpace = 0f;
float barWidth = 0.16f;
// (barSpace + barWidth) * 5 + groupSpace = 1
// multiplied by 5 because there are 5 five bars
// labels will be centered as long as the equation is satisfied
data.setBarWidth(barWidth);
// so that the entire chart is shown when scrolled from right to left
xAxis.setAxisMaximum(labels.length - 1.1f);
mChart.setData(data);
mChart.setScaleEnabled(false);
mChart.setVisibleXRangeMaximum(2f);
mChart.groupBars(1f, groupSpace, barSpace);
mChart.invalidate();
}
private class LabelFormatter implements IAxisValueFormatter {
String[] labels;
BarLineChartBase<?> chart;
LabelFormatter(BarLineChartBase<?> chart, String[] labels) {
this.chart = chart;
this.labels = labels;
}
#Override
public String getFormattedValue(float value, AxisBase axis) {
return labels[(int) value];
}
}
}
labels will be centered as long as this equation is satisfied
(barSpace + barWidth) * 5 + groupSpace = 1
multiplied by 5 because there are 5 five bars
RESULT
first you have to dynamically set the min and max value for x-axis
mChart.getXAxist().setAxisMinimum(0);
mChart.getXAxis().setAxisMaximum(0 +
barChart.getBarData().getGroupWidth(groupSpace, barSpace) * groupCount);
and then set it to center:
mChart.getXAxis().setCenterAxisLabels(true);
I have provided a detailed answer here:
set interval between x-axis labels: stackoverflow
I think the Label position is correct & the problem is with Bar position & Spacing.Please try the following method.
/**
* Group charts by specifying width & other properties externally
*
* #param dataSets Entire DataSet made
* #param data Bar Data created from datasets
* #see http://google.com
*/
private void setupChartGrouping(ArrayList<IBarDataSet> dataSets,
BarData data) {
if (dataSets.size() > 1) {
barChart.getXAxis().setCenterAxisLabels(true);
barChart.getBarData().setBarWidth(getBarWidth(dataSets.size()));
barChart.getXAxis().setAxisMinimum(-data.getBarWidth() / 2);
barChart.getXAxis().setAxisMaximum(dataSets.size());
barChart.groupBars(0, GROUPED_CHART_GROUP_SPACING, GROUPED_CHART_BAR_SPACING);
} else {
barChart.getXAxis().setCenterAxisLabels(false);
barChart.getXAxis().resetAxisMaximum();
barChart.getXAxis().resetAxisMinimum();
barChart.getAxisLeft().setDrawGridLines(false);
barChart.getXAxis().setDrawGridLines(false);
}
}
private static final float GROUPED_CHART_MAGICAL_NUMBER = 1.00f;
public static final float GROUPED_CHART_BAR_SPACING = 0.05f;
public static final float GROUPED_CHART_GROUP_SPACING = 0.20f;
/**
* Size would be ==> ((barSpace + barWidth) * no: of bars in a group) + groupSpace = 1.00
*
* #param barEntryCount No: of bars in a Group
* #return Width of a Single Bar in Chart
*/
public static float getBarWidth(int barEntryCount) {
return ((GROUPED_CHART_MAGICAL_NUMBER - GROUPED_CHART_GROUP_SPACING) / barEntryCount) - GROUPED_CHART_BAR_SPACING;
}
To align x axis at center of groupBarChart you need to fulfill the below formula
Formula :: (barWidth + barSpace)*count + groupSpace = 1 :-count= no of bars/groups
here:
barWdith = ??
barSpace = 0.05f
grouSpace = 0.44f
You can find the barWidth using below formula:
barWidth = (1 - groupSpace) /`enter code here` count - barSpace;
I am using MPAndroidChart for plotting scatteredBarChart. The code is as follows:
Code:
public void setupScatteredBarChart(ArrayList<String> x, ArrayList<String> y1, ArrayList<String> y2 )
{
int maxYvalue = 0;
final String[] x_name = new String[x.size()];
for (int j = 0; j <x.size(); j++)
{
String[] temp = x.get(j).split("-");
x_name[j] = temp[0] + "/" + temp[1];
}
ArrayList<BarEntry> yVals1 = new ArrayList<BarEntry>();
for (int i = 0; i < x_name.length; i++)
{
float val1 = Float.parseFloat(y1.get(i));
float val2 = Float.parseFloat(y2.get(i));
yVals1.add(new BarEntry(i, new float[]{val1, val2},getResources().getDrawable(R.drawable.ic_launcher)));
float val_total = val1 + val2;
int I = (int) Math.ceil(val_total);
if (maxYvalue < I)
{
maxYvalue = I;
}
}
chart_scattered = (BarChart) findViewById(R.id.chart_scattered);
chart_scattered.setOnChartValueSelectedListener(Stat.this);
chart_scattered.getDescription().setEnabled(false);
chart_scattered.setMaxVisibleValueCount(40); // if more than 40 entries are displayed in the chart, no values will be drawn
chart_scattered.setPinchZoom(false); // scaling can now only be done on x- and y-axis separately
chart_scattered.setDrawGridBackground(false);
chart_scattered.setDrawBarShadow(false);
chart_scattered.setDrawValueAboveBar(false);
chart_scattered.setHighlightFullBarEnabled(false);
chart_scattered.setDoubleTapToZoomEnabled(false);
chart_scattered.setTouchEnabled(false);
chart_scattered.setFitBars(true);
chart_scattered.animateXY(800, 800);
chart_scattered.getAxisRight().setEnabled(false);
chart_scattered.getXAxis().setAxisMinimum(-0.5f); // adjust for showing first bar properly
// Left Y axis
YAxis leftAxis = chart_scattered.getAxisLeft(); // change the position of the y-labels
leftAxis.setAxisMinimum(0f); // this replaces setStartAtZero(true)
chart_scattered.getAxisLeft().setLabelCount(maxYvalue + 1, true);
chart_scattered.getAxisLeft().setAxisMinValue(0f);
chart_scattered.getAxisLeft().setValueFormatter(new MyYAxisValueFormatter());
// Bottom X axis
XAxis xLabels = chart_scattered.getXAxis();
xLabels.setPosition(XAxis.XAxisPosition.BOTTOM);
xLabels.setAxisMinimum(0f);
xLabels.setGranularity(1f);
xLabels.setValueFormatter(new IAxisValueFormatter() {
#Override
public String getFormattedValue(float value, AxisBase axis) {
return x_name[(int) value % x_name.length];
}
});
Legend l = chart_scattered.getLegend();
l.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM);
l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT);
l.setOrientation(Legend.LegendOrientation.HORIZONTAL);
l.setDrawInside(false);
l.setFormSize(6f);
l.setFormToTextSpace(4f);
l.setXEntrySpace(6f);
BarDataSet set1;
if (chart_scattered.getData() != null && chart_scattered.getData().getDataSetCount() > 0)
{
set1 = (BarDataSet) chart_scattered.getData().getDataSetByIndex(0);
set1.setValues(yVals1);
chart_scattered.getData().notifyDataChanged();
chart_scattered.notifyDataSetChanged();
}
else
{
set1 = new BarDataSet(yVals1, "");
set1.setDrawIcons(false);
set1.setColors(getColors());
set1.setStackLabels(new String[]{"A", "B"});
ArrayList<IBarDataSet> dataSets = new ArrayList<IBarDataSet>();
dataSets.add(set1);
BarData data = new BarData(dataSets);
data.setValueTextColor(Color.WHITE);
chart_scattered.setData(data);
}
chart_scattered.invalidate();
}
Screen Capture:
Question:
How could the first Bar to be showing properly? I have researched a lot and added the following to the code but is still not successful as showing in the screen capture above.
chart_scattered.getXAxis().setAxisMinimum(-0.5f);
chart_scattered.setFitBars(true);
As the date will go on increasing, could it set max 7 Bars and more than that, it has to be scrolled horizontally?
Thanks!
Question 1
remove this line from your code
xLabels.setAxisMinimum(0f);
will solve your problem.
Question 2
To only show the first 7 bars, use this..
chart_scattered.setVisibleXRangeMaximum(7f);
but this will not allow the user to scroll horizontally yet, if you want to enable scrolling, you'll have to remove this line from your code
chart_scattered.setTouchEnabled(false);
I am assuming you used the above line so the user can't highlight the bars. To keep that behavior, use this function..
chart_scattered.setHighlightPerTapEnabled(false);