I want to plot time series data in Android.
I have a temperature sensor which reports data at every 10 seconds. I am taking that data from cloud using API.
In data, I have a temperature value and its timestamp.
I want to display data for 1 month and user can zoom chart till timestamp (till seconds or mins).
I tried to plot using MPAndroidChart. I have taken timestamps on X-axis and value on Y-axis.
MPAndroidChart only allows float values and I have timestamps which are of long type.
So I have taken first starting timestamp as 0 and calculated others by subtracting this base timestamp from it.
I have done this after reading this issue. https://github.com/PhilJay/MPAndroidChart/issues/2891
I have used code from answer of this question.
Here is my code snippet.
private void showChart(ArrayList<TempData> tempData, long baseTimestamp) {
// Prepare Bar Entries
ArrayList<BarEntry> barEntries = new ArrayList<>();
for (int i = 0; i < tempData.size(); i++) {
long timestamp = tempData.getTimestamp(); // Here timestamp & baseTimestamp both are in seconds.
timestamp = timestamp - baseTimestamp;
Log.d(TAG, "Adding entry with timestamp : " + timestamp);
BarEntry barEntry = new BarEntry(timestamp, tempData.get(i).getValue());
barEntries.add(barEntry);
}
// Initialise xAxis
XAxis xAxis = barChart.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(barEntries.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 TimestampXAxisFormatter(baseTimestamp));
// xAxis.setLabelCount(4);
// Initialize Y-Right-Axis
YAxis rightAxis = barChart.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(1000f);
rightAxis.setLabelCount(4, true); //draw y labels (Y-Values) for 4 horizontal grid lines starting from 0 to 1000f
rightAxis.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART);
//initialize Y-Left-Axis
YAxis leftAxis = barChart.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(barEntries, "Time Series");
barDataSet.setColor(Color.RED);
barDataSet.setFormSize(15f);
barDataSet.setDrawValues(false);
barDataSet.setValueTextSize(12f);
// set the BarData to chart
BarData data = new BarData(barDataSet);
barChart.setData(data);
barChart.setScaleEnabled(false);
barChart.getLegend().setEnabled(false);
barChart.setDrawBarShadow(false);
barChart.getDescription().setEnabled(false);
barChart.setPinchZoom(false);
barChart.setDrawGridBackground(true);
barChart.invalidate();
}
class TimestampXAxisFormatter extends IndexAxisValueFormatter {
long baseTimestamp;
TimestampXAxisFormatter(long baseTime) {
baseTimestamp = baseTime;
}
#Override
public String getFormattedValue(float value) {
// Add base timestamp
long timestamp = (long) value;
timestamp = timestamp + baseTimestamp;
Log.d(TAG, "getFormattedValue, value : " + value);
Log.e(TAG, "getFormattedValue, Timestamp : " + timestamp);
// Convert from seconds back to milliseconds to format time to show to the user
String dateTimeInStr = new SimpleDateFormat("HH:mm:ss").format(new Date(timestamp * 1000));
return dateTimeInStr;
}
}
I was expecting timestamp (timestamp difference which was used to add BarEntry) in "getFormattedValue" method of TimestampXAxisFormatter.
But I am getting 0.0, 2.0, 4.0, 6.0 etc.
What is the mistake here because of that I am not able to receive timestamp in "getFormattedValue" method?
Anyone please help me to find out the issue and plot 1 month of data and user will be able to zoom till seconds.
Thanks in advance.
The value you want to show in x-axis,first add in Array list as below
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
Here I had made a Year Graph.
Then when we are initializing the X -axis, There we can set value by using .setValueFormatter() function as shown below :-
XAxis xAxis = mBarChart.getXAxis();
xAxis.setValueFormatter(new ValueFormatter() {
#Override
public String getFormattedValue(float value) {
return xAxisLabel.get((int) value);
}
In your case, the value which you try to convert into timestamp in getFormattedValue()
public String getFormattedValue(float value) {
// Add base timestamp
long timestamp = (long) value;
is 1.0,2.0. etc, Thats the reason you are not geting the value as you want.
Now as You want to show data in every 10 sec, Here is the way -
For Showing the data in every 10 secs, we need to add xLabel accordingly :-
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);
}
and then setting it in XAxis using below code :-
XAxis xAxis = mBarChart.getXAxis();
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 "";
}
});
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();
I am using MPCHART of android but want to show date value on the X-Axis but BarEntry takes on float value how to parse the date to enter the value in BarEntry or any alternative to add value to the bar chart of the x and y axis.
private void grossChartSetup(ArrayList<BarEntry> grosssalesamountToday) {
grossChart.setMarker(iMarker);
grossChart.setDrawBarShadow(false);
grossChart.setMaxVisibleValueCount(24);
grossChart.setPinchZoom(false);
grossChart.setEnabled(false);
grossChart.setDrawBarShadow(false);
grossChart.setDoubleTapToZoomEnabled(false);
grossChart.getDescription().setEnabled(false);
grossChart.setHighlightFullBarEnabled(false);
grossChart.setOnChartGestureListener(this);
BarDataSet orderDataSet1 = new BarDataSet(grosssalesamountToday, "Actual");
orderDataSet1.setColor(mContext.getResources().getColor(R.color.darkCyan));
BarData orderdata = new BarData(orderDataSet1);
orderdata.setValueFormatter(new LargeValueFormatter());
grossChart.setData(orderdata);
float barWidth = 0.1f;
grossChart.getBarData().setBarWidth(barWidth);
XAxis orderXAxis = orderChart.getXAxis();
orderXAxis.setDrawGridLines(false);
orderXAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
// orderXAxis.setValueFormatter(new DayXAxisFormatter());
YAxis orderRightYAxis = orderChart.getAxisRight();
YAxis orderLeftAxis = orderChart.getAxisLeft();
orderLeftAxis.setDrawGridLines(false);
orderRightYAxis.setEnabled(false);
grossChart.getLegend().setPosition(Legend.LegendPosition.ABOVE_CHART_RIGHT);
grossChart.invalidate();
grossChart.setVisibleXRangeMaximum(7);
}
You have to create your own value formatter like below
public class MyYAxisValueFormatter implements IValueFormatter {
public MyYAxisValueFormatter() {
}
#Override
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
// "value" represents the position of the label on the axis (x or y)
// value => date in MiliSecond
val dateFormat = SimpleDateFormat("yyyy-MM-dd")
return dateFormat.format(value)
}
}
and after that you can set this as your dataFormatter like below
dataSet.setValueFormatter(new MyYAxisValueFormatter())
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 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);
I've been working with HorizontalBarChart MPAndroidChart a few days ago, and ran into an issue while trying to use a custom ValueFormatter which converts the time from milliseconds into 12h format time. (08:50 AM) But as you can see in the picture below, the value gets clipped...
Is there any possibility to set a padding for the chart on the right for the values to be visible?
Here's the code which configures the chart:
private void initializeChart() {
// Customize user interaction on chart
mHorizontalBarChart.setTouchEnabled(false);
mHorizontalBarChart.setPinchZoom(false);
mHorizontalBarChart.setDragEnabled(false);
// Customize chart's standard settings
mHorizontalBarChart.setDrawGridBackground(false);
mHorizontalBarChart.setDescription("");
mHorizontalBarChart.setHighlightEnabled(false);
mHorizontalBarChart.getLegend().setEnabled(false);
Paint infoPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
infoPaint.setTextAlign(Paint.Align.CENTER);
infoPaint.setTextSize(12f);
mHorizontalBarChart.setPaint(infoPaint, Chart.PAINT_INFO);
// Customize X axis
XAxis xAxis = mHorizontalBarChart.getXAxis();
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxis.setTextSize(12f);
xAxis.setDrawLabels(true);
xAxis.setDrawAxisLine(false);
xAxis.setDrawGridLines(false);
xAxis.setAvoidFirstLastClipping(true);
// Customize Y axis's
// Left
YAxis leftAxis = mHorizontalBarChart.getAxisLeft();
leftAxis.setDrawLabels(false);
leftAxis.setDrawAxisLine(false);
leftAxis.setDrawGridLines(false);
leftAxis.setDrawTopYLabelEntry(false);
// Right
YAxis rightAxis = mHorizontalBarChart.getAxisRight();
rightAxis.setDrawLabels(false);
rightAxis.setDrawAxisLine(false);
rightAxis.setDrawGridLines(false);
rightAxis.setDrawTopYLabelEntry(false);
}
And here's the code which populates it:
private void setChartData(float range) {
String[] daysOfWeek = new String[]{"MO", "TU", "WE", "TH", "FR", "SA", "SU"};
ArrayList<String> xVals = new ArrayList<String>();
for (int i = 0; i < daysOfWeek.length; i++) {
xVals.add(daysOfWeek[i]);
}
ArrayList<BarEntry> yVals1 = new ArrayList<BarEntry>();
for (int i = 0; i < daysOfWeek.length; i++) {
float mult = (range + 1);
float val = (float) (Math.random() * mult);
yVals1.add(new BarEntry(val, i));
}
BarDataSet set1 = new BarDataSet(yVals1, "DataSet");
set1.setBarSpacePercent(35f);
ArrayList<BarDataSet> dataSets = new ArrayList<BarDataSet>();
dataSets.add(set1);
BarData data = new BarData(xVals, dataSets);
data.setValueTextSize(10f);
data.setValueFormatter(new ValueFormatter() {
#Override
public String getFormattedValue(float value) {
return value + "some extra text";
}
});
mHorizontalBarChart.setData(data);
}
Any help is greatly appreciated.
Code is used from the examples provided by #Philipp Jahoda.
EDIT: BASED ON Philipp's answer:
I used setSpaceTop(float percent) to adjust the space for the values of the BarData.
// Customize Y axis's
// Left
YAxis leftAxis = mHorizontalBarChart.getAxisLeft();
leftAxis.setDrawLabels(false);
leftAxis.setDrawAxisLine(false);
leftAxis.setDrawGridLines(false);
leftAxis.setDrawTopYLabelEntry(false);
leftAxis.setSpaceTop(20); // use these to adjust space for the BarData values
// Right
YAxis rightAxis = mHorizontalBarChart.getAxisRight();
rightAxis.setDrawLabels(false);
rightAxis.setDrawAxisLine(false);
rightAxis.setDrawGridLines(false);
rightAxis.setDrawTopYLabelEntry(false);
rightAxis.setSpaceTop(20); // use these to adjust space for the BarData values
Code must be optimized though, this is just a quick test, but these suggestions worth tried out. Thanks again Philipp!
Read the documentation of the YAxis.
Focus on the following methods:
setAxisMaxValue(...) & setAxisMinValue(...)
setSpaceTop(...)
setSpaceBottom(...)
With them you can set the range / space of your axis.