Androidplot background, start, end values and ranges - android

I am trying to create a custom XYPlot with Androidplot but can't seem to fully configure it.
This is my code:
XYSeries series = new SimpleXYSeries(Arrays.asList(dates), Arrays.asList(times), "Time on level");
// Create a formatter to use for drawing a series using LineAndPointRenderer:
LineAndPointFormatter formatter = new LineAndPointFormatter(Color.rgb(0, 100, 0), Color.rgb(0, 100, 0), null, new PointLabelFormatter(Color.BLUE));
totalTimePlot.setRangeValueFormat(new DecimalFormat("#.#"));
totalTimePlot.setGridPadding(0, 0, 0, 200);
totalTimePlot.setBackgroundColor(Color.WHITE);
totalTimePlot.getGraphWidget().getBackgroundPaint().setColor(Color.WHITE);
totalTimePlot.getGraphWidget().getGridBackgroundPaint().setColor(Color.WHITE);
totalTimePlot.getGraphWidget().getDomainOriginLinePaint().setColor(Color.BLACK);
totalTimePlot.getGraphWidget().getRangeOriginLinePaint().setColor(Color.BLACK);
totalTimePlot.setTicksPerDomainLabel(1);
totalTimePlot.setRangeStepValue(10);
totalTimePlot.getGraphWidget().setDomainLabelOrientation(-90);
totalTimePlot.setMarkupEnabled(false);
totalTimePlot.setBorderStyle(Plot.BorderStyle.SQUARE, null, null);
totalTimePlot.setDomainValueFormat(new Format() {
SimpleDateFormat sdf = new SimpleDateFormat("EEE(MM.dd)-HH:mm:ss");
#Override
public StringBuffer format(Object object, StringBuffer buffer, FieldPosition field) {
long timestamp = ((Number) object).longValue();
Date date = new Date(timestamp);
return sdf.format(date, buffer, field);
}
#Override
public Object parseObject(String string, ParsePosition position) {
return null;
}
});
totalTimePlot.getGraphWidget().setPaddingLeft(4);
totalTimePlot.addSeries(series, formatter);
And this is the end result:
As you can see this is very ugly. Can you help me at least configure it so that the Range labels are displayed. I would also like to know how to make the background white as well as have the first and last "ticks" visible.
Thank you.
EDIT:
I managed to include the first and last value by setting the boundaries. But the other problems remain.

Agreed it's pretty ugly :-) It would be easier to give you suggestions if you list a few specific items you'd like to improve. Having said that, here's a few things I would start with:
Use the DemoApp's SimpleXYPlotActivity xml style as a starting point. This should fix your margin issues and improve the overall appearance.
Simplify your date/time format for your domain labels. Maybe something like day/month hour:minute. I don't know your exact use case so maybe you require the format you're using.
Get rid of the dark background that frames your plot.
Consider using the CatmullRomInterpolator to draw a smooth line.

Related

Passing current time in milliseconds to MPChart breaks it

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));
}
});

Box2d creates specific body and fixture seemingly at random

I have created an android application similar to Not Tetris 2 using Libgdx with Box2d.
It can successfully remove a slice from the world, which obviously involves duplicating several bodies and destroying/creating fixtures. However, seemingly at random, a body with a 2x2 fixture will appear. The body and fixture are displayed using information related to the objects around it when it is created, so I narrowed its creation down to the following function:
Body duplicateBody(Body original){
BodyDef d = new BodyDef();
d.position.set(original.getPosition());
d.angle = original.getAngle();
d.linearVelocity.set(original.getLinearVelocity());
d.angularVelocity = original.getAngularVelocity();
Body dup = world.createBody(d);
dup.setType(BodyDef.BodyType.DynamicBody);
return dup;
}
I use this function in 2 different contexts:
Making a copy of the body if a "slice" cuts one in two -- I then transfer the fixtures which are below to it.
When a fixture is below the line then it is added to a body created for ones below
Making a copy of the body when groups of fixtures are separated
I commented out the code responsible for the third instance and still had the 2x2 boxes spawning, so here are the functions relevant to the others:
...
if (below && !above) {
//copy fixture, add copy to lower body and remove original
Body top = fixture.getBody();
FixtureDef n = new FixtureDef();
PolygonShape s = new PolygonShape();
s.set(getLocalVerticesOfFixture(fixture));
n.shape = s;
n.density = fixture.getDensity();
//create lower body if a lower one doesn't already exist
if (!topBottomPairs.containsKey(top)) {
Body dup = duplicateBody(top);
topBottomPairs.put(top, dup);
}
//delete fixture
remove.add(fixture);
Fixture f = topBottomPairs.get(top).createFixture(n);
s.dispose();
}
...
if (below && above) {
//copy fixture, add copy to lower body, but keep original on upper as it needs to split
FixtureDef n = new FixtureDef();
PolygonShape s = new PolygonShape();
s.set(getLocalVerticesOfFixture(fixture));
n.shape = s;
n.density = fixture.getDensity();
Body top = fixture.getBody();
//create lower body if a lower one doesn't already exist
if (!topBottomPairs.containsKey(top)) {
Body dup = duplicateBody(top);
topBottomPairs.put(top, dup);
}
Fixture second = topBottomPairs.get(top).createFixture(n);
s.dispose();
}
....
private Vector2[] getLocalVerticesOfFixture(Fixture fixture) {
PolygonShape shape = ((PolygonShape) fixture.getShape());
Vector2[] localVertices = new Vector2[shape.getVertexCount()];
for (int i = 0; i < shape.getVertexCount(); i++) {
localVertices[i] = new Vector2();
shape.getVertex(i, localVertices[i]);
}
return localVertices;
}
I also have this remove fixture function which runs on all fixtures I want to remove:
private void smartDeleteFixture(Fixture f){
f.getBody().destroyFixture(f);
if(f.getBody().getFixtureList().size == 0){
world.destroyBody(f.getBody());
}
}
Nowhere do I create vertices, let alone a fixture of a 2x2 shape. I was wondering if this duplication function has any flaws, or if I stumbled upon some "default shape" that box2d uses.
Edit: I have removed anything not related to the manipulation of box2d bodies. Hope that helps
Deleted this question as I decided to perform a major recode and hoped that would fix my problem. It did not, but I figured out the cause.
I looked through box2d and found a couple instances of code similar to this in the polygon shape class:
if (n < 3)
{
// Polygon is degenerate.
b2Assert(false);
SetAsBox(1.0f, 1.0f);
return;
}
These instances check the number of vertices after various operations and turn the shape into a 2x2 box if there are fewer than 3. One of these operations makes the shape convex. Another checks if vertices are close together (closer than 0.0025f), deleting one if so.
In my case, the problem was simple. Some of my vertices were less than 0.0025f from each other, resulting in them being deleted, the vert count dropping below 3, an assertion being ignored, and then my shape being turned into a 2x2 box. I hope this helps someone out.

how to make androidplot axis caption visible

Could somebody please explain me what I have to do so that my plot shows me at least any kind of axis caption? The horrible androidplot documentation explains less than nothing.
Code:
Date day1 = getDate(28, MAY, 2016);
Date day2 = getDate(29, MAY, 2016);
Date day3 = getDate(30, MAY, 2016);
Date day4 = getDate(31, MAY, 2016);
Date day5 = getDate(1, JUNE, 2016);
Number[] quotes = new Float[]{4.23f, 5.13f, 5.73f, 6.12f, 4.23f};
Number[] dates = new Long[]{day1.getTime(), day2.getTime(), day3.getTime(), day4.getTime(), day5.getTime(),};
XYSeries series = new SimpleXYSeries(Arrays.asList(dates), Arrays.asList(quotes), "History 5 days");
LineAndPointFormatter formatter = new LineAndPointFormatter();
formatter.setPointLabelFormatter(new PointLabelFormatter());
formatter.configure(this, R.xml.line_point_formatter_with_labels);
plot.setDomainStep(XYStepMode.SUBDIVIDE, 5);
plot.setRangeStep(XYStepMode.SUBDIVIDE, 5);
plot.setDomainValueFormat(new Format()
{
private SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.");
#Override
public StringBuffer format(Object object, StringBuffer buffer, FieldPosition field)
{
long millis = ((Number) object).longValue();
Date date = new Date(millis);
return dateFormat.format(date, buffer, field);
}
#Override
public Object parseObject(String string, ParsePosition position)
{
return null;
}
});
plot.addSeries(series, formatter);
Plot xml:
<com.androidplot.xy.XYPlot
android:id="#+id/plot"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="10dp"
app:domainLabel="domain label"
app:label="A simple plot"
app:rangeLabel="range label" />
There couldnĀ“t be less caption:
at the left side I would like to have numbers like 4.0, 5.0, 6.0 and at the bottom the date
Looks like you arent applying any styling in your XML. If you check out the xml example in the Androidplot quickstart you'll notice it has a style applied like so:
<com.androidplot.xy.XYPlot
style="#style/APDefacto.Dark"
android:id="#+id/plot"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
ap:label="A Simple XY Plot"
ap:rangeLabel="range"
ap:domainLabel="domain"/>
The key attribute is style="#style/APDefacto.Dark", which basically applies a canned set of attrs to your plot to make it pretty. There's also a link in the doc to the full list of custom attrs if you prefer to come up with your own layout.
In any case applying the above style should solve the immediate problem you are having :-)

Remove a lengend from an XYPlot

Am using AndroidPlot on my project and it's just great so far. This is what i got.
Now am trying to remove one of the series indicators from the legend. Actually i want to keep only the first one (the blue one) cause the others are self explanatory.
Am not asking how to remove the legend it self (see https://stackoverflow.com/a/13413758/665823 for that issue).
If someone can help me i'll be great. Thanks in advance.
I finally found a tweek that allows me to do what i need. To achieve this the series that i need to be shown must be added to the plot first over the ones i need to hide :
// I add the blue serie first
LineAndPointFormatter mFormat = new LineAndPointFormatter();
mFormat.configure(context, R.xml.plot_blue);
graphView.addSeries(serieMesures, mFormat);
// I add the other colors after
for (XYSeries serie : seriesSeuils) {
LineAndPointFormatter sFormat = new LineAndPointFormatter();
if (serie.getTitle().equals("RED") {
sFormat.configure(context, R.xml.plot_red);
}
else if (serie.getTitle().equals("ORANGE")) {
sFormat.configure(context, R.xml.plot_orange);
}
else if (serie.getTitle().equals("YELLOW")) {
sFormat.configure(context, R.xml.plot_yellow);
}
graphView.addSeries(serie, sFormat);
}
Then i just created a legend where there is only enough space to show the series i need (in my case just the first one).
// This is what does the trick! A Table with 1 line and 1 column
// so the other series can't be rendered in the LegendWidget
graphView.getLegendWidget().setTableModel(new DynamicTableModel(1, 1));
// Other customizations (size and position)
graphView.getLegendWidget().setSize(new SizeMetrics(55, SizeLayoutType.ABSOLUTE, legendWith, SizeLayoutType.ABSOLUTE));
graphView.getLegendWidget().setPositionMetrics(
new PositionMetrics(20,
XLayoutStyle.ABSOLUTE_FROM_RIGHT,
20,
YLayoutStyle.ABSOLUTE_FROM_TOP,
AnchorPosition.RIGHT_TOP));
graphView.getLegendWidget().setBackgroundPaint(bgPaint);
graphView.getLegendWidget().setTextPaint(txPaint);
graphView.getLegendWidget().setBorderPaint(brPaint);
graphView.getLegendWidget().setPadding(10, 1, 10, 1);
And voila!

Inconsistent SpannableString setSpan() coloring?

I'm trying to color only vowels in my String 1 color (ex red), and non-vowels another (ex: blue). But the SpannableString setSpan() method is being inconsistent when iterating thru each char. The function is detecting the vows and non-vows correctly, as I had checked the logged output,except that the coloring is not correct:
//ColorLogic.java:
public SpannableString colorString(String myStr)
{
SpannableString spnStr=new SpannableString(myStr);
ForegroundColorSpan vowColor=new ForegroundColorSpan(Color.RED);
ForegroundColorSpan conColor=new ForegroundColorSpan(Color.BLUE);
int strLen=myStr.length();
for(int i=0; i< strLen; i++)
{
if (vowSet.contains(Character.toLowerCase(myStr.charAt(i))))
//if (i%2==0)
{
Log.v(DTAG, "vow"+myStr.charAt(i));
spnStr.setSpan(vowColor, i, i, 0);
}
else
{
Log.v(DTAG, "cons"+myStr.charAt(i));
spnStr.setSpan(conColor, i, i, 0);
}
}
return spnStr;
}
//In my OnCreate of my activity class:
//PASS
//Log.v(DTAG, message);
// Create the text view
TextView textView = new TextView(this);
textView.setTextSize(50);
//Call Color Logic to color each letter individually
ColorLogic myColorTxt=new ColorLogic();
SpannableString spnMsg=myColorTxt.colorString(message);
//Log.v(DTAG, "spnMsg: "+spnMsg.toString());
textView.setText(spnMsg, BufferType.SPANNABLE);
//textView.setTextColor(Color.GREEN);
setContentView(textView);
}
![Vows Only its correct (non-vowels only is correct as well)][1]
![With cons and vows, 2 letters then its incorrect!][2]
You cannot reuse span objects. As pskink indicates, please use distinct ForegroundColorSpan objects for each setSpan() call.
Also, you may wish to use fewer spans overall. While your sample ("abibobu") requires the maximum possible number of spans, most words have consonants and vowels strung together. For example, the word "consonant" has two two-consonant spans ("ns" and "nt"). Those could be colored using a single ForegroundColorSpan, rather than two, improving rendering speed. Spans are easy but not the fastest, and so the fewer spans you use, the better your app will perform, particularly in animated situations (e.g., scrolling in a ListView).
Furthermore, you may only need to color either consonants or vowels, unless you are planning on a third color for hyphens and apostrophes. Remember: your text can start with a color (e.g., android:textColor).

Categories

Resources