How can I customize the X axis to show days (or months or years, based on the selected range) where a new day/month/year begins? I am using CategoryDateAxis (CreateMultiPaneStockChartsFragment example).
What I want:
Larger ranges:
Smaller ranges (easily see where new day begins):
What I have:
Right now I am using default label provider and it is hard to see when new day/month/year begins. E.g. for 7 day range:
Axis is construced like this:
final CategoryDateAxis xAxis = sciChartBuilder.newCategoryDateAxis()
.withVisibility(isMainPane ? View.VISIBLE : View.GONE)
.withVisibleRange(sharedXRange)
//.withLabelProvider(new TradeChartAxisLabelProviderDateTime())
.withGrowBy(0.01d, 0.01d)
.build();
How do I achieve this?
public static class TradeChartAxisLabelProviderDateTime extends TradeChartAxisLabelProvider {
public TradeChartAxisLabelProviderDateTime() {
super();
}
#Override
public String formatLabel(Comparable dataValue) {
if(currentRange == RANGE_1_YEAR) {
} else if(currentRange == RANGE_1_MONTH) {
} else if(currentRange == RANGE_1_DAY) {
}
String text = super.formatLabel(dataValue).toString();
return text;
}
}
To implement selection of label based on VisibleRange you can use code like this:
public static class TradeChartAxisLabelProviderDateTime extends TradeChartAxisLabelProvider {
public TradeChartAxisLabelProviderDateTime() {
super(new TradeChartAxisLabelFormatterDateTime());
}
private static class TradeChartAxisLabelFormatterDateTime implements ILabelFormatter<CategoryDateAxis> {
private final SimpleDateFormat labelFormat, cursorLabelFormat;
private TradeChartAxisLabelFormatterDateTime() {
labelFormat = new SimpleDateFormat(CategoryDateAxis.DEFAULT_TEXT_FORMATTING, Locale.getDefault());
cursorLabelFormat = new SimpleDateFormat(CategoryDateAxis.DEFAULT_TEXT_FORMATTING, Locale.getDefault());
}
#Override
public void update(CategoryDateAxis axis) {
final ICategoryLabelProvider labelProvider = Guard.instanceOfAndNotNull(axis.getLabelProvider(), ICategoryLabelProvider.class);
// this is range of indices which are drawn by CategoryDateAxis
final IRange<Double> visibleRange = axis.getVisibleRange();
// convert indicies to range of dates
final DateRange dateRange = new DateRange(
ComparableUtil.toDate(labelProvider.transformIndexToData((int) NumberUtil.constrain(Math.floor(visibleRange.getMin()), 0, Integer.MAX_VALUE))),
ComparableUtil.toDate(labelProvider.transformIndexToData((int) NumberUtil.constrain(Math.ceil(visibleRange.getMax()), 0, Integer.MAX_VALUE))));
if (dateRange.getIsDefined()) {
long ticksInViewport = dateRange.getDiff().getTime();
// select formatting based on diff in time between Min and Max
if (ticksInViewport > DateIntervalUtil.fromYears(1)) {
// apply year formatting
labelFormat.applyPattern("");
cursorLabelFormat.applyPattern("");
} else if (ticksInViewport > DateIntervalUtil.fromMonths(1)) {
// apply month formatting
labelFormat.applyPattern("");
cursorLabelFormat.applyPattern("");
} else if (ticksInViewport > DateIntervalUtil.fromMonths(1)) {
// apply day formatting
labelFormat.applyPattern("");
cursorLabelFormat.applyPattern("");
}
}
}
#Override
public CharSequence formatLabel(Comparable dataValue) {
final Date valueToFormat = ComparableUtil.toDate(dataValue);
return labelFormat.format(valueToFormat);
}
#Override
public CharSequence formatCursorLabel(Comparable dataValue) {
final Date valueToFormat = ComparableUtil.toDate(dataValue);
return cursorLabelFormat.format(valueToFormat);
}
}
}
In update() you can get access to VisibleRange of axis and based on it select label formatting and then use SimpleDateFormat to format Dates.
But as I understand your case is more complex than this because you can't get labels which allow to see when new day/month/year begins based on current VisibleRange. For this case you'll need to select format string based on previously formatted values and track when day/month/year changes.
I'm Trying to create a Financial transactions Graph like banks ,As shown in this Pic
When Customer Selects the Start and End Date , Graph should show the Months on XAxis and Date and Amount when transaction was done as shown by White filled circle .
I have tried examples provided by library but I wasn't able to use string labels and plot real time values !
Try using IAxisValueFormatter
final String[] months = new String[] { "Jan", "Feb", "Mar", "Apr" };
IAxisValueFormatter formatter = new IAxisValueFormatter() {
#Override
public String getFormattedValue(float value, AxisBase axis) {
return months [(int) value];
}
#Override
public int getDecimalDigits() { return 0; }
};
XAxis xAxis = mLineChart.getXAxis();
xAxis.setGranularity(1f);
xAxis.setValueFormatter(formatter);
I am using MPAndroidChart for my line chart.
I have date values and score values.
Example: on 11/10/2016 my score was 45.
I am struggling with the dates. Not sure how to set it in my setYAxisValues.
I am getting my values from a rest api and putting it in the graph.
This part is where i have my problem.
yVals.add(new Entry(Float.valueOf(ocd.getScore()), foo));
If I change foo to a normal int value like 1, 2, 3 I have no problem. The graph is working. The issue, i need to use dates to plot my value at the correct place.
#Override
protected void onPostExecute(List<ResultModel> result) {
super.onPostExecute(result);
//populating my yAxis with values from rest
for (ResultModel ocd : resModelList){
long unixSeconds = Long.parseLong(ocd.getPost_date());
Date date = new Date(unixSeconds*1000L);
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
String formattedDate = sdf.format(date);
int foo = Integer.parseInt(formattedDate);
yVals.add(new Entry(Float.valueOf(ocd.getScore()), foo));
}
}
The X axis is working
//set vales
private ArrayList<String> setXAxisValues(){
xVals = new ArrayList<String>();
//MM/dd/yyyy
xVals.add("01/01/2016");
xVals.add("02/01/2016");
xVals.add("03/01/2016");
xVals.add("04/01/2016");
xVals.add("05/01/2016");
return xVals;
}
private ArrayList<Entry> setYAxisValues(){
yVals = new ArrayList<Entry>();
return yVals;
}
Thanks in advance
I had the similar issue, the point is - MPChart library cannot have anything but float for X axis. I'd suggest you to have X axis represented by date's millis. Suppose you have four values with dates "01/01/2016", "02/01/2016", "03/01/2016", "04/01/2016", "05/01/2016". You add values like
yVals.add(new Entry(Float.valueOf(ocd.getScore()), "01/01/2016".toMillis()));
"01/01/2016".toMillis() is pseudocode of course, you need to convert your date to int (float).
Then, set up minX as "01/01/2016".toMillis(), maxX as"04/01/2016".toMillis(),
and provide a label formater which will format this millis back to string dates:
private class LabelFormatter implements AxisValueFormatter {
private Context context;
private LabelFormatter(Context context) {
this.context = context;
}
#Override
public int getDecimalDigits() {
return -1;
}
#Override
public String getFormattedValue(float value, AxisBase axis) {
return DateUtils.formatDateTime(context, (long) value, DateUtils.FORMAT_SHOW_DATE);
}
}
I am using mpandroidchart. I am using YAxisValueFormatter to format my value like 0.00 in my chart but i am getting simple value like 123.Not geeting two zero after my value like 123.00.
please see image to get more idea.
class MyYAxisValueFormatter implements YAxisValueFormatter {
private DecimalFormat mFormat;
public MyYAxisValueFormatter() {
mFormat = new DecimalFormat("#.00");
}
#Override
public String getFormattedValue(float value, YAxis yAxis) {
return mFormat.format(value) + "$";
}
}
mFormat = new DecimalFormat("###.##");
or
mFormat = new DecimalFormat("$###.##");
Your code looks fine (although I recommend the format #0.00, because values less then 1 will show as .45$ instead of 0.45$)
Are you sure that you have set the formatter on the chart like this example?
YAxis leftAxis = chart.getAxisLeft();
leftAxis.setValueFormatter(new MyYAxisValueFormatter());
Replace this,
mFormat = new DecimalFormat("#.00");
with this
mFormat = new DecimalFormat(".00");
It is working for me.
I have an array which having time ranges like below,
String[] str ={"6.30 AM","6.10 AM","10.00 PM","7.00 PM"};
i want to get the minimum time and maximum time in above array such as "6.10 AM" and "10.00 PM".i can find out using sorting but it takes long time.Is any other method avail.Guide me,Below i sorted like,
String[] str ={"1:0 PM","2:0 AM","3:0 PM",.....};
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm aa", Locale.getDefault());
Date TimeToCompare = null,Time1 = null;
for(int i=0;i<10;i++)
{
TimeToCompare=sdf.parse(str[i]);
for(int j=i+1;j<10;j++)
{
Time1=sdf.parse(str[j]);
if(TimeToCompare.after(Time1))
{
//sorting
}
}
}
This solution makes one pass through the array, keeping track of the min and max times. Runs in O(n).
double maxTime = 0.0;
double minTime = 0.0;
for(String s : str) {
String[] parts = str.split(" ");
double time = Double.parse(parts[0]);
if (parts[1].equals("PM")) {
time += 12;
}
if (time > maxTime) {
maxTime = time;
}
if (time < minTime) {
minTime = time;
}
}
// convert doubles back into strings and print
Date-Time Values
When working with date-time values, it's usually best to work with them as date-time values.
Parse the strings as date-time values, collect them, sort the collection, and retrieve the first and last elements in collection to get earliest & latest values. Convert back to strings if needed.
Joda-Time & java.time
You can easily parse the strings to create date-time objects.
However avoid using the bundled java.util.Date & .Calendar classes in Java as they are notoriously troublesome. Furthermore, they always combine date and time-of-day while in your case you have only a time-of-day.
Use either Joda-Time or the new java.time package in Java 8. Both offer a day-of-time only class, LocalTime.
Example Code
Example code using Joda-Time 2.3.
Convert your array to Collection as I prefer to not work with arrays.
String[] strings = { "6.30 AM", "6.10 AM", "10.00 PM", "7.00 PM" };
List<String> stringList = Arrays.asList( strings );
Create an empty collection to collect our LocalTime objects as we instantiate them.
List<LocalTime> localTimes = new ArrayList<>();
Create a formatter to parse your particular string format. By the way, if you can change the source of these strings, I suggest creating strings in 24-hour format without the "AM/PM", akin to the standard ISO 8601 format.
DateTimeFormatter formatter = DateTimeFormat.forPattern( "h'.'mm aa" );
Loop through our collection of strings, parsing each one. Store the new LocalTime instance in a collection.
for ( String string : stringList ) {
LocalTime localTime = formatter.parseLocalTime( string );
localTimes.add( localTime );
}
Sort the collection of LocalTime objects, to determine the earliest and latest.
Collections.sort( localTimes ); // Ascending order. Earliest first, latest last.
Retrieve the earliest and latest.
LocalTime earliest = localTimes.get( 0 );
LocalTime latest = localTimes.get( localTimes.size() - 1 );
Dump to console.
System.out.println( "localTimes: " + localTimes );
if ( !( localTimes.isEmpty() ) ) {
System.out.println( "earliest: " + formatter.print( earliest ) );
System.out.println( "latest: " + formatter.print( latest ) );
}
When run…
localTimes: [06:10:00.000, 06:30:00.000, 19:00:00.000, 22:00:00.000]
earliest: 6.10 AM
latest: 10.00 PM
Here's a sample solution picked from ggreiner #
How to sort a list of time strings in Java or Groovy
String[] str ={"6.30 AM","6.10 AM","10.00 PM","7.00 PM"};
List<String> times = Arrays.asList(str); // convert int to list
Collections.sort(times, new MyComparator()); // use a custom comparator
Log.i("Min time is ",""+times.get(0));
Log.i("Max Time is ",""+times.get(times.size()-1));
Custom Comparator
class MyComparator implements Comparator<String>
{
private DateFormat primaryFormat = new SimpleDateFormat("h.mm a");
#Override
public int compare(String time1, String time2){
return timeInMillis(time1) - timeInMillis(time2);
}
public int timeInMillis(String time){
return timeInMillis(time, primaryFormat);
}
// in milliseconds
private int timeInMillis(String time, DateFormat format) {
Date date = null ;
try {
date = format.parse(time); //
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return (int)date.getTime();
}
}
// Try this way,hope this will help you to solve your problem.
String[] str =new String[]{"6.30 AM","6.10 AM","10.00 PM","7.00 PM"};
ArrayList<Double> list = new ArrayList<Double>();
HashMap<Double,String> map = new HashMap<Double, String>();
for (int i=0;i<str.length;i++){
list.add(Double.parseDouble(str[i].split(" ")[0]));
map.put(Double.parseDouble(str[i].split(" ")[0]),str[i]);
}
System.out.println("Min >> " +map.get(Collections.min(list)));
System.out.println("Max >> "+map.get(Collections.max(list)));
Can you use these, But you may need some pre-arragments
Collections.max(arrayList);
Collections.min(arrayList);
Depending on how you're originally filling the array of values, like are you getting the long from the system then you could compare those. Or create an class that holds the value part and the AM or PM seperately like a flag or something, so then sort between AM and PM then values. I dabbled a lot with java date and calendars, and just use JodaTime. It's convenient!