I'm kind of stumped here. I've looked at the docs of MPAndroidChart and a bunch of questions being asked here, but I can't quite figure out what's causing it.
Basically, unless I have 12 or so labels, or I just force label counts, there are no Mondays in my charts. The labels are placed sunday evening and early tuesday morning consistently, which causes monday not to be drawn. Ideally, at a certain zoom level I should see months, then days, then hours, but the labels would have to be at logical places (1st day of month, midnight, start of hours, etc...)
What I'd love to do is to be able to force specific intervals for the xLabels. ex: at a certain zoom level have intervals of 24 hours, 1 hour, or 15 mins, but all starting at midnight so that there wouldn't be any weird time labels. So far I haven't found a single confirmed solution for this... at least not with real, and irregular Epoch timestamps. This very similar question was asked, but it didn't lead anywhere: MPAndroidChart x-axis date/time label formatting

(no mondays, unless you zoom into the hourly view, where they become visible if I force the label formatting to show days)
I'm graphing various data points, but their x values are ALL unix timestamps.
my code:
in my onCreate:
// creates line graphs for al DPs
for (int i = 0; i < activeDataPointIDs.size(); i++) {
// gets id and dataPointLogic
int id = DataPointsManager.activeDataPointIDs.get(i);
DataPointLogic logic = DataPointsManager.getDataPointLogic(id);
// creates the chart
LineChart lineChart;
lineChart = createLineGraph(logic, UnitConverter.dpToPixel(300, this));
// creates a label
TextView textView = new TextView(this);
textView.setText(DataPointsManager.dataPointNames[id]);
// adds the chart and label to the containing view
mainContainer.addView(textView);
mainContainer.addView(lineChart);
// adds the chart to a list accessible within the activity class
lineChartList.add(lineChart);
}
calculateGraphRange();
calculateGraphRange() :
// calculates the graph range based on the time interval selected
void calculateGraphRange() {
int days = CompassApp.daysToShowStats;
// figuring out stupid timezone offsets
long baseOffset = TimeZone.getDefault().getRawOffset();
long dstOffset = TimeZone.getDefault().getDSTSavings();
long timezoneOffset = baseOffset + dstOffset;
// default values are set for 1 week, TODO: deal with timezones
long graphMax = System.currentTimeMillis();
long graphMaxExtra = graphMax % TimeUtilities.UNIX_DAY;
graphMax = graphMax - graphMaxExtra + TimeUtilities.UNIX_DAY - timezoneOffset;
long graphMin = graphMax - (TimeUtilities.UNIX_DAY * days);
//
// prevents invalid graphs from being drawn //TODO: fix labels when range is 0 or null
if (days == 0) {
graphMax = 0;
graphMin = 0;
}
// applies to all charts
for (int i = 0; i < lineChartList.size(); i++) {
LineChart chart = lineChartList.get(i);
chart.getXAxis().setAxisMaximum(graphMax);
chart.getXAxis().setAxisMinimum(graphMin);
chart.notifyDataSetChanged();
chart.invalidate();
}
}
Nothing funky in the date formatter. most is commented out to make testing easier
IAxisValueFormatter createDateFormatter() {
IAxisValueFormatter formatter = new IAxisValueFormatter() {
@Override
public String getFormattedValue(float value, AxisBase axis) {
Date date = new Date((long) value);
SimpleDateFormat fmt;
// switch (labelModeSelected) {
// case HOURS_FORMAT:
// fmt = new SimpleDateFormat("h:mm a");
// break;
//
// case DAYS_FORMAT:
// fmt = new SimpleDateFormat("E d");
// break;
//
// case WEEKS_FORMAT:
// fmt = new SimpleDateFormat("d MMM");
// break;
//
// case MONTHS_FORMAT:
// fmt = new SimpleDateFormat("MMM yyyy");
// break;
//
// case YEARS_FORMAT:
// fmt = new SimpleDateFormat("yyyy");
//
// break;
//
// default:
// fmt = new SimpleDateFormat("E d MMM");
// break;
// }
fmt = new SimpleDateFormat("MMM d H:mm zz"); //TODO remove after tests and add switch
fmt.setTimeZone(TimeZone.getDefault()); // sets time zone... I think I did this properly...
String s = fmt.format(date);
return s;
}
// we don't draw numbers, so no decimal digits needed
public int getDecimalDigits() {
return 0;
}
};
return formatter;
}
method to create my line graphs
LineChart createLineGraph(DataPointLogic dataPointLogic, int height) {
final LineChart lineChart = new LineChart(this);
lineChart.setMinimumHeight(height);
List<Entry> entries = new ArrayList<Entry>();
List<Number> xList = new ArrayList<>();
List<Number> yList = new ArrayList<>();
xList = dataPointLogic.getXVals(); //TODO: does this just not work with the class inheritance
yList = dataPointLogic.getYVals(); //TODO: does this just not work with the class inheritance
for (int i = 0; i < xList.size(); i++) {
long x = xList.get(i).longValue();
float y = yList.get(i).floatValue();
entries.add(new Entry(x, y));
}
//
final LineDataSet dataSet = new LineDataSet(entries, "Time series");
// graph smoothing params
dataSet.setCubicIntensity(0.5f);
dataSet.setMode(LineDataSet.Mode.HORIZONTAL_BEZIER);
// graph fill params
dataSet.setDrawFilled(true);
// dataSet.setFillDrawable(ContextCompat.getDrawable(this, R.drawable.doge));
dataSet.setFillColor(primaryColor);
// line params
dataSet.setColor(primaryColor);
dataSet.setLineWidth(1f);
// circle params
dataSet.setCircleRadius(2.5f);
dataSet.setCircleColor(primaryColor);
dataSet.setDrawCircleHole(false);
LineData lineData = new LineData(dataSet);
lineData.setDrawValues(false);
lineChart.setData(lineData);
//
// applies the timestamp formatting
XAxis xAxis = lineChart.getXAxis();
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxis.setValueFormatter(createDateFormatter());
xAxis.setDrawLabels(true);
xAxis.setCenterAxisLabels(true);
xAxis.setLabelRotationAngle(90f); // rotates label so we can see it all TODO remove after tests
YAxis yAxisRight = lineChart.getAxisRight();
yAxisRight.setDrawLabels(false);
yAxisRight.setDrawGridLines(false);
YAxis yAxisLeft = lineChart.getAxisLeft();
yAxisLeft.setDrawGridLines(false);
yAxisLeft.setSpaceBottom(0.0f);
// removes description and legend
lineChart.getLegend().setEnabled(false);
lineChart.getDescription().setEnabled(false);
// sets the maximum zoom... must be provided unix time values
lineChart.setVisibleXRangeMinimum(TimeUtilities.UNIX_HOUR);
lineChart.setOnChartGestureListener(new OnChartGestureListener() {
@Override
public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {
}
@Override
public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {
}
@Override
public void onChartLongPressed(MotionEvent me) {
}
@Override
public void onChartDoubleTapped(MotionEvent me) {
}
@Override
public void onChartSingleTapped(MotionEvent me) {
}
@Override
public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) {
}
@Override
public void onChartScale(MotionEvent me, float scaleX, float scaleY) {
scaleChartLabels(lineChart);
}
@Override
public void onChartTranslate(MotionEvent me, float dX, float dY) {
}
});
// applies the scale
scaleChartLabels(lineChart);
lineChart.invalidate(); // refreshes data
return lineChart;
}
method change how the labels are displayed, depending on the scale of the graph
// changes the scale of the chart labels depending on what is selected
void scaleChartLabels(LineChart chart) {
float max = chart.getHighestVisibleX();
float min = chart.getLowestVisibleX();
float totalXVisible = max - min;
float granularity = TimeUtilities.UNIX_DAY;
int labelCount = 0;
// conditional switch to determine display params of graphs
if (totalXVisible < TimeUtilities.UNIX_DAY) {
labelCount = 8;
granularity = TimeUtilities.UNIX_HOUR;
labelModeSelected = HOURS_FORMAT;
// Day view
} else if (totalXVisible < TimeUtilities.UNIX_DAY * 2) {
labelCount = 2;
granularity = TimeUtilities.UNIX_DAY;
labelModeSelected = DAYS_FORMAT;
// Week view
} else if (totalXVisible < TimeUtilities.UNIX_DAY * 3) {
labelCount = 3;
granularity = TimeUtilities.UNIX_DAY;
labelModeSelected = DAYS_FORMAT;
// Week view
} else if (totalXVisible < TimeUtilities.UNIX_DAY * 4) {
labelCount = 4;
granularity = TimeUtilities.UNIX_DAY;
labelModeSelected = DAYS_FORMAT;
// Week view
} else if (totalXVisible < TimeUtilities.UNIX_DAY * 5) {
labelCount = 5;
granularity = TimeUtilities.UNIX_DAY;
labelModeSelected = DAYS_FORMAT;
// Week view
} else if (totalXVisible < TimeUtilities.UNIX_DAY * 6) {
labelCount = 6;
granularity = TimeUtilities.UNIX_DAY;
labelModeSelected = DAYS_FORMAT;
// Week view
} else if (totalXVisible < TimeUtilities.UNIX_WEEK) {
labelCount = 7;
granularity = TimeUtilities.UNIX_DAY;
labelModeSelected = DAYS_FORMAT;
// Week view
} else if (totalXVisible < (TimeUtilities.UNIX_WEEK * 2)) {
labelCount = 7;
granularity = TimeUtilities.UNIX_DAY;
labelModeSelected = DAYS_FORMAT;
// Week view
} else if (totalXVisible < TimeUtilities.UNIX_MONTH) {
labelCount = 7;
granularity = TimeUtilities.UNIX_DAY;
labelModeSelected = WEEKS_FORMAT;
//Month view
} else if (totalXVisible < TimeUtilities.UNIX_MONTH * 3) {
labelCount = 3;
granularity = TimeUtilities.UNIX_MONTH;
labelModeSelected = MONTHS_FORMAT;
// 90 day view
} else if (totalXVisible < TimeUtilities.UNIX_YEAR) {
labelCount = 6;
granularity = TimeUtilities.UNIX_MONTH;
labelModeSelected = MONTHS_FORMAT;
// year view
} else if (totalXVisible > TimeUtilities.UNIX_YEAR) {
Log.w(TAG, "case not implemented yet");
}
chart.getXAxis().setGranularityEnabled(true);
chart.getXAxis().setGranularity(granularity);
chart.getXAxis().setLabelCount(labelCount);
}