I want the x-axis labelling to be as shown in the above picture. I have tried using the AxisValueFormatter interface but what it does is repeat the label 'jh ow' 3 times from 0 to 1, while missing out some of the labels shown here.
This is the output I am getting. While the shape of the graph is correct, the labels are not.
I am sharing the graph data so that anyone interested can try and make the graph as shown. Any help would be great, thank you.
Using MPAndroid is not important. Any help from any other library is fine too.
The data are:
"lables" : ["jh ow", "n ih l", "b iy", "hh iy r", "s uw n"]
"values":[[{"x":"0.00","y":"0.00"},{"x":"0.99","y":"0.00"}],
[{"x":"0.99","y":"1.00"},{"x":"1.19","y":"0.50"}]
, [{"x":"1.19","y":"0.50"},{"x":"1.37","y":"0.50"}]
, [{"x":"1.37","y":"0.50"},{"x":"1.59","y":"0.50"}]
, [{"x":"1.59","y":"0.50"},{"x":"2.12","y":"1.00"}]]
You just make a simple list of string like this in activity page;
final ArrayList<String> xVals= new ArrayList<>();
xVals.add("jh ow");
xVals.add("n ih l");
xVals.add("b iy");
xVals.add("hh iy r");
xVals.add("s uw n");
and Then you do this;
xAxis.setValueFormatter(new MyXAxisDateArrayFormatter(xVals));
your MyXAxisDateArrayFormatter.java
public class MyXAxisDateArrayFormatter implements IAxisValueFormatter {
private String[] mValues;
public MyXAxisDateArrayFormatter(String[] values) {
this.mValues = values;
}
#Override
public String getFormattedValue(float value, AxisBase axis) {
// "value" represents the position of the label on the axis (x or y)
return mValues[(int) value];
}
}
It worked on me
Nevermind, I was able to achieve this by using an older version of MPAndroidChart library and it worked like a charm.
Related
Good morning,
I was trying to implement an AnyChart polar plot in android (https://github.com/AnyChart/AnyChart-Android) which should look something similar to this (https://playground.anychart.com/gallery/Polar_Charts/Line_Polar_Chart). I know this example is in javascript and I am developping it within Android, which is not exactly the same, but to keep a reference of the result I want to achieve is fine.
From that example, I have been struggling to change the xAxis Labels (0°, 30°, ...) to custom made strings, such as ("Zero", "Thirty",...).
I didn't manage to find a xAxis label getter which returns the label string (https://api.anychart.com/v8/anychart.core.ui.LabelsFactory#getLabel) but haven't been able to find a label setter...
My aim would be to keep the numerical functionallity of a Anychart polar plot, where a line could be plotted from (45°, value 1) to (45°, value 5) and having displayed chars at the xlabel of the plot, so instead of seeing "45°" to the label, it should say "fourty-five"
Do anyone have any idea if this is achievable?
Thank you very much!
You can achieve that using format() function of the xAxis labels. In the callback function you can implement your own logic on how to translate numbers to words (if, case, hashmap, etc). The snippet describes how to achieve that.
AnyChartView anyChartView = findViewById(R.id.any_chart_view);
Polar polar = AnyChart.polar();
List<DataEntry> data = new ArrayList<>();
data.add(new ValueDataEntry(0, 10));
data.add(new ValueDataEntry(15, 8));
data.add(new ValueDataEntry(30, 12));
data.add(new ValueDataEntry(45, 11));
Set set = Set.instantiate();
set.data(data);
Mapping series1Data = set.mapAs("{ x: 'x', value: 'value' }");
polar.line(series1Data);
Linear scale = Linear.instantiate();
scale.minimum(0).maximum(360);
scale.ticks().interval(30);
polar.xScale(scale);
polar.xAxis().labels().format("function() {" +
"if (this.tickValue == 60) return 'sixty';" +
"if (this.tickValue == 90) return 'ninety';" +
"if (this.tickValue == 120) return 'hundred twenty';" +
"return this.tickValue;" +
"}");
anyChartView.setChart(polar);
Below is the result:
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 remove decimal value from the BarChart in MPAndroidChart. Even though I have taken it as a Integer value while displaying it is showing decimal and adding up .0 for my value as.
ex: If my value is 65, it is displaying 75.0.(I want to remove that .0 part)
I m using the library called "mpandroidchartlibrary-2-1-4"
below is my code snippet in integer.
Double a= Double.parseDouble(total_week);
int b=a.intValue();
Double a1= Double.parseDouble(complitiontest);
int b1=a1.intValue();
Log.d("duoosfsd","**** "+b1);
entries.add(new BarEntry(b,0));
entries.add(new BarEntry(b1,1));
Here the b1 is value which is Integer value even when I print in log it prints only as 75 but while showing it will show as 75.0.
Attached screenshot please check.
public class MyValueFormatter implements ValueFormatter {
private DecimalFormat mFormat;
public MyValueFormatter() {
mFormat = new DecimalFormat("#");
}
#Override
public String getFormattedValue(float value) {
return mFormat.format(value);
}
}
call this on BarData class object
data.setValueFormatter(new MyValueFormatter());
I did try for same and below worked for me.
new DecimalFormat("#").format(100.0);
You first need to check the mpandroidchartlibrary-2-1-4 library's code.
In that library find the method which is used to display the number on the bar.
As per my understanding, it converts integer into float or either it is displaying float value to the bar at the time of display.
Advice : Please check library code and understand it first. And if you find something and want to change it, then you have to import this project as library and then only you can do changes as per your requirement.
In my Android application I have to show my income and expense amounts in a piechart. The expense amounts will be negative. The amounts are of type float. So is it possible to display both negative and positive values in a pie chart using MPAndroidChart library?
I have tried with some negative and positive integer values and the result is horible. The pie-chart will behave crazy like a rummy board or illumination screen.
If I can't have negative values in a pie chart, is it possible to show the labels in negative by even though the actual values are positive?
You are essentially asking how to customise the labels on an MPAndroidChart chart. This can be done easily by writing your own implementation of the interface IValueFormatter.
IValueFormatter has one method that you need to implement:
getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler)`
You use the parameters of the method to construct the label you want as a String. You want a minus sign "-" in front so you simply do this:
return "-" + mFormat.format(value) + " %";
Here is the complete implementation:
public class NegativePercentFormatter implements IValueFormatter
protected DecimalFormat mFormat;
public NegativePercentFormatter() {
mFormat = new DecimalFormat("###,###,##0.0");
}
public NegativePercentFormatter(DecimalFormat format) {
this.mFormat = format;
}
#Override
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
return "-" + mFormat.format(value) + " %";
}
}
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.