I am using graphview library and trying to format label of graph to time in 'HH' format from millisecond. I give each x value of datapoint in series as millisecond which is really big like '1.442440833138E12'. And after that i make my custom LabelFormatter like below:
#Override
public String formatLabel(double value, boolean isValueX) {
if (isValueX) {
SimpleDateFormat format = new SimpleDateFormat("HH");
Date date = new Date((long) value);
Log.i("formatLabel", Double.toString(date.getTime()));
return format.format(date);
} else {
return super.formatLabel(value, isValueX);
}
}
But when i looked at my graph, it was not same to what i put in, like when i put millisecond equal to 07 hour, the graph showed 11 hour or 01 hour as i change using setNumHorizontalLabels. So i tried to find what was wrong, and when i put log output code up that formatLabel method, the log output was like below:
09-17 20:03:50.287 28093-28093/? I/formatLabel﹕ 1.44243E12
09-17 20:03:50.287 28093-28093/? I/formatLabel﹕ 1.44244E12
09-17 20:03:50.287 28093-28093/? I/formatLabel﹕ 1.44245E12
09-17 20:03:50.287 28093-28093/? I/formatLabel﹕ 1.44246E12
09-17 20:03:50.292 28093-28093/? I/formatLabel﹕ 1.44247E12
which is supposed to look like '1.442440833138E12'. I also tried to make custom DefaultFormatter with number precision with minimum '12' digits but result was same.
Can someone explain to me why this happens?
-------------------------------------------------------------------------------
Here's my code of adding datapoints into each series.
DataPoint[] data1=new DataPoint[SIZE] ;
for(int i=0;i<SIZE;i++)
{
data1=new DataPoint(new Date(time),value) ;
//test whether x prints all full millisecond
Log.i("formatLabel", Double.toString(data1.getX()));
}
LineGraphSeries<DataPoint> series=new LineGraphSeries(data1) ;
when i saw a log output when i give each datapoint's x value with date, i get
full millisecond notation like '1.442440833138E12'. But in my custom formatLabel instance above, it is truncated. I hope user can get my problem more
clearly and give me some solution...
You should add date/times to a series like this:
series.appendData(new DataPoint(new Date(time), value), true, Integer.MAX_VALUE);
And format the label like this:
graph.getGridLabelRenderer().setLabelFormatter(
new DateAsXAxisLabelFormatter(getActivity(),
SimpleDateFormat.getDateInstance(SimpleDateFormat.SHORT)) {
#Override
public String formatLabel(double value, boolean isValueX) {
if (isValueX)
return super.formatLabel(value, isValueX);
else
// format Y-axis label
}
});
(the example uses the default short SimpleDateFormat instance)
Looking at your label formatting code: there is no need to instantiate SimpleDateFormat for each data point.
Related
I am implementing the function to display the change of exercise time by date as a graph.
But there was a problem.
Before I explain the problem, I will briefly explain my code.
When a date is input from the user, the date is stored in the database and output by date.
For example, when the user enters 2020/06/26, it is displayed as 06/26 on the graph.
Now I will explain the problem.
The x value of the graph is overlapping. 07/01 does not appear immediately after 06/30, but very far.
I will attach my code and execution result. enter image description here
xAxis.setValueFormatter(new ValueFormatter() {
#Override
public String getFormattedValue(float value) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyMMdd");
Date date = null;
try {
date = simpleDateFormat.parse(Float.toString(value));
} catch (ParseException e) {
e.printStackTrace();
}
SimpleDateFormat newFormat = new SimpleDateFormat("MM/dd");
String dateString = newFormat.format(date);
return dateString;
}
});
Hard to tell from the code you provide. But most probably, the problem does not lie in the ValueFormatter, but in the actual x values you use. Having x values in the format yyMMdd ist most likely not what you want, because the difference between e.g. 2020-04-01 and 2020-04-02 is not the same as between 2020-03-31 and 2020-04-01, event if it should be exactly the same (1 day). You should use another representation for the x values, e.g. "days since 1970".
This still does not explain why 06-30 is displayed after 07-01 and even after 08-19, however. My guess is that your Entry list is not sorted properly in ascending order.
I have tried figuring this out, but it doesn't add up. The data doesn't appear as it should.
First I generate dummy data. This is done async because I need time between the calls to System.currentTimeMillis to get some spacing between them. (Look aside the crappy code here, this is just debug data that will not be in the release. Using Thread.sleep on the main thread is a bad idea considering ANR's)
public class AsyncGeneration extends AsyncTask<String, String, String>{
public AsyncGeneration() {
super();
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
}
#Override
protected void onCancelled(String s) {
super.onCancelled(s);
}
#Override
protected void onCancelled() {
super.onCancelled();
}
#Override
protected String doInBackground(String... strings) {
while(root == null) {
try {
Thread.sleep(200);
}catch(InterruptedException e){}
}
List<Entry> rdata = new ArrayList<>();
for(int i = 1; i < 30; i++){
try{
Thread.sleep(200);
}catch(Exception e){
//IGNORE
}
float time = System.currentTimeMillis();
Log.e("GChart", "Timex: " + time);
Session s = new Session(r.nextInt(5000), time);//Replace time with the index in the for-loop and it works for some reason
rdata.add(new Entry(s.getXValue(), s.getYValue()));
Log.e("GChart", "Timey: " + s.getXValue());
}
final List<Entry> entries = rdata;
OverviewFragment.this.getActivity().runOnUiThread(() ->{
LineDataSet data = new LineDataSet(entries, "Distance");
data.setCircleColor(Color.parseColor("#FF0000"));
LineData lineData = new LineData(data);
tab1chart.setData(lineData);
tab1chart.invalidate(); // refresh
tab1chart.getXAxis().setValueFormatter(new DateFormatter());
tab1chart.getXAxis().setPosition(XAxis.XAxisPosition.BOTTOM);
tab1chart.getXAxis().setTextSize(10f);
tab1chart.getXAxis().setTextColor(Color.RED);
tab1chart.getXAxis().setDrawAxisLine(true);
tab1chart.getXAxis().setDrawGridLines(true);
tab1chart.getAxisLeft().setValueFormatter(new DistanceValueFormatter());
tab1chart.getAxisLeft().setDrawGridLines(true);
Log.v("Chart", "Chart data loaded and invalidated");//This prints
});
return null;
}
}
So far everything looks fine. The data gets put into the chart, no exceptions, no crashes.
When the chart renders, a single data point shows up at the far-left of the chart. I generate 30 data points, one shows up.
That's issue #1: Only one data point shows up.
Issue #2 is slightly harder. The entire X axis at the bottom disappears. X axis is gone and when zooming, the Y axis, and its text also disappears. This is fairly hard to explain, so here is a screenshot:
It is worth mentioning the fact that if I pass i in the for-loop as the time, it shows up just as expected: all the axises are in place, zoom doesn't break anything.
(Float values can take the same values as Longs except they have decimals in addition.)
And in addition, I format the data:
public class DateFormatter implements IAxisValueFormatter {
SimpleDateFormat formatter;
public DateFormatter(){
formatter = new SimpleDateFormat("dd/MM/yy, HH:mm");
}
#Override
public String getFormattedValue(float value, AxisBase axis) {
//This method is never called. Nothing is returned
if(axis instanceof XAxis) {
String formatted = formatter.format(new Date((long) value));
Log.d("Formatter", "Formatted \"" + value + "\" to \"" + formatted + "\".");
return formatted;
}
return "Not supported";
}
}
Removing the formatter doesn't change anything, the axis is still gone. Zoom still breaks the Y axis.
So my question is: How do I fix this? I don't get why the chart doesn't work when I pass the current time in milliseconds (and I checked the values with debug output, floats can handle it). I got some debug output earlier that eventually stopped coming at all that showed the value passed to the formatter was values < 2000. I can't reproduce this any more though
The chart itself doesn't appear to be broken but from touch events it looks like every single point is pushed into the same X coordinate but only one point renders. When I touch the chart, the orange-ish lines show up indicating the position of a data point. It pops up on points that aren't visible.
When I pass the for-loop index as the X value, it works as expected, the formatter works fine (looking aside the fact that it shows the date as in 1970, but it is counted as 1 millisecond into the epoch, so that is to expect)
I looked at this as well on formatting the date. When I then try passing the milliseconds since the epoch, the chart stops working.
I'm using MPChart v 3.0.2, compiling against Android 26, using Java 8, and running Android Studio 3.0 beta 2
As for the layout, it is just a LinearLayout with a LineChart in it.
This should work, but it breaks when I pass it the current time in milliseconds for some reason. Passing any other data (as long as the numbers aren't that big) works fine.
The two images are not how the chart is supposed to look. The X axis is supposed to be visible, but for some reason it isn't along with a large amount of the data.
This is known issue of Android MPChart (have a read this thread). Time series chart supported in MPChart - You can set time (in millis) as X values for Hourly Charts.
But too many consecutive data (points) won't correctly plot in Line Charts. Because Entry object will accept only float values due to some performance constraints.
So, keep the first value as the reference and subtract each up coming value from reference value & divide by some constants (say 1000) .So , you X value set will be like 10,20,30....& so on.
Do the reverse logic in your Axis Value formatter to render the X Axis Label properly (see the code snippet).
lineChart.getXAxis().setValueFormatter(new IAxisValueFormatter() {
#Override
public String getFormattedValue(float value, AxisBase axis) {
SimpleDateFormat format2 = new SimpleDateFormat("HH:mm:ss");
return format2.format(new Date(firstTimeStamp + ((long) value) * 1000L));
}
});
I want to add the following sort of data (can be any number of such pairs under 1000) to the newly introduced timeseries chart in MPAndroidChart library
Value : 50.0 at 1472112259
Value : 49.0 at 1472112294
Value : 50.0 at 1472112329
Value : 50.0 at 1472112360
Value : 50.0 at 1472112392
The following data will be fetched from the array.
Right now, I guess there is some mess up with the timestamps.
Here is the complete code: https://gist.github.com/utkarshns/e1723dcc57022fcd392bc3b127b6c898
UNIX timestamps will be parsed to required time format after I can successfully add values to the graph.
Currently, the problem I face is that the timestamps probably get clipped and values are overwritten which leads to a pretty messed up graph with really weird x-axis values.
Update:
Screenshots:
http://imgur.com/a/dGfmz
The problem is that Float values can't hold very big numbers and still be accurate, so you need a separate List with these timestamp values. BigDecimal should be ok for this purpose. Your distances must be in accordance to the time gaps between your events. Just iterate from the start date to end date keeping count of how many timestamps you have and add Entry with count from the timestamps you wish your value to be.
Long myValues[] = {1472112259L, 1472112294L, 1472112329L, 1472112360L, 1472112392L};// your values
ArrayList<Entry> values = new ArrayList<>();// Entry List
Long start = 1472112259L;//start
Long end = 1472112392L;//end
List<BigDecimal> mList = new ArrayList<>(); //Decimal list which holds timestamps
int count = 0;
for (Long i = start; i <= end; i++) {
mList.add(new BigDecimal(i));
if (myValues.equals(i)) {
values.add(new Entry(count, 50));
}
count++;//always increment
}
And your ValueFormatter should look like this:
AxisValueFormatter() {
private FormattedStringCache.Generic<Long, Date> mFormattedStringCache = new FormattedStringCache.Generic<>(new SimpleDateFormat("HH:mm:ss"));
#Override
public String getFormattedValue ( float value, AxisBase axis){
return mFormattedStringCache.getFormattedValue(new Date(mList.get((int)value).longValueExact()*1000), value);
}
#Override
public int getDecimalDigits () {
return 0;
}
}
If you have any question or something is unclear I'll be happy to help.
I have an iteration that runs for a set number of times depending on another value which can vary which is why I'm using an iteration that iterates based on that value, inside that iteration I add 30 days to a date once each iteration and then add the results to a table.
PROBLEMS
I simply end up with the first instance of adding 30 days which is outside the iteration itself. This means that my values inside the iteration are not being stored properly but I can't see why.
I've checked the DateTime operations and displayed the value of newdate and it shows the proper date so it's most likely the storing of the date. But I don't know what's going wrong, it works pre-iteration which is what's got me confused. Why isn't it executing inside the iteration? Does anyone have any idea?
Ex.
InitialDate | 3/29/2015
2ndDate | 4/28/2015<-- This is what's stored which is pre-iteration
3rdDate | 5/28/2015<-- This is what it's supposed to be after the iteration
so on and so forth....
Values pre-iteration
//Date stuff
String startdate = (String.valueOf(Acc._date));
DateTimeFormatter formatter = DateTimeFormat.forPattern("MM-dd-yyyy HH:mm:ss");
DateTime dt = formatter.parseDateTime(startdate);
DateTime startpoint = new DateTime(dt);
DateTime whendue = startpoint.plusDays(30);
DateTime foriteration = whendue;
String formattedDate = whendue.toString(formatter);
//Storing initial date
pay.setDateDue(formattedDate);
db.AddPayment(pay);
Actual iteration
while (i < j) {
//Operation for Date Calculation
DateTime ndt = foriteration.plusDays(30);
foriteration = ndt;
String newdate = ndt.toString(formatter);
//Adding values to PayTable
pay.setDateDue(newdate);
db.AddPayment(pay);
i++;
}
Finally found out what was wrong. Nothing. My roommate played a prank on me and just got back from his trip out of town and explained to me how he changed my getDateDue to execute a plusDays(30) to mimic my code so that when I called AddPayment which calls getDateDue it would look like it would work but in actuality would only add 30 days once to the startdate no matter what I did.
Summary
Roommate is an ass, nothing is wrong with my code. Sorry for this pointless post.
I have a datepicker, and I pick up two dates, like 2012-04-08 and 2012-05-11. Because I have a database and need store dates as strings I convert them to 20120408 and 20120511 (strings so far). My code contains the next steps. I call my function with these strings:
public void durva(String datefrom, String dateto) throws ParseException {
datefrom = GlobalVars.getDateStringFrom();
dateto = GlobalVars.getDateStringTo();
Log.i("DateFrom", datefrom);
Log.i("Dateto", dateto);
SimpleDateFormat formatter2 = new SimpleDateFormat("yyyyMMdd"); //-de most yyMMdd
formatter2.setLenient(false);
Date dates1;
Date dates2;
long mili1;
long mili2;
long diff;
String dates="";
String convertedDates = "";
dates1 = formatter2.parse(datefrom);
mili1 = dates1.getTime();
Log.i("date1", String.valueOf(mili1));
dates2 = formatter2.parse(dateto);
mili2 = dates2.getTime();
Log.i("date2", String.valueOf(mili2));
diff = 1+((mili2-mili1)/86400000);
Log.i("diff", String.valueOf(diff));
long [] millis = new long[(int) diff];
for (int i=0;i<diff;i++)
{
millis[i] = mili1+(86400000*i);
Log.i("millii", String.valueOf(millis[i]));
dates = dates + String.valueOf(millis[i]) + " ";
SimpleDateFormat formatterX = new SimpleDateFormat("yyyyMMdd");
String dateString = formatterX.format(new Date(millis[i]));
convertedDates = convertedDates + dateString + " ";
}
Log.i("DATES", convertedDates);
}
I use the a created GlobalVars when I pick a date and covert them to this string format I mentioned above. Then I convert them to millisecs. Then I convert them back to my format but it is not important, since the millisecs are already messed up. With
for (int i=0;i<diff;i++)
{
millis[i] = mili1+(86400000*i);
Log.i("millii", String.valueOf(millis[i]));
I always increase the millisecs, but what happens after the 25th value? It travels back in time and continues from another value! In this example I get: 20120408 20120409 .. 20120502 20120314..20120322 . I add 86400000 (millisecs per day) for jumping a whole day.
What's happening here?
Thank you in advance!
You should use 86400000L, or declare i as long:
millis[i] = mili1+(86400000L*i);
Otherwise both i and 86400000 are 32 bit integers, so the result is calculated as a 32-bit integer. Unfortunately 86400000*25 is too big to fit in 32 bits, so it wraps around.
86400000*25 is -2134967296.
Another thing you should be careful about is that not all days have 24 hours thanks to DST.