Using the android plot library and am struggling with how to get the bars on the plot to the same size. For some reason the first one of the series appears wider than the rest. See image for example.
As you can see the domain labels are position correctly and read the correct values. Here is the code that customises the format of the XYPlot.
BarFormatter series1Format = new BarFormatter();
plot.addSeries(series, series1Format);
BarRenderer renderer = (BarRenderer) plot.getRenderer( BarRenderer.class );
renderer.setBarRenderStyle(BarRenderer.BarRenderStyle.SIDE_BY_SIDE);
renderer.setBarWidthStyle(BarRenderer.BarWidthStyle.VARIABLE_WIDTH);
renderer.setBarGap(2.0f);
Paint series1Fill = new Paint();
series1Fill.setColor(plot.getContext().getResources().getColor(R.color.accent));
series1Format.setFillPaint(series1Fill);
plot.setTicksPerRangeLabel(2);
plot.getGraphWidget().setDomainGridLinePaint(null);
plot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 1);
plot.setRangeLowerBoundary(0,BoundaryMode.FIXED);
plot.setUserDomainOrigin(1);
plot.setDomainLowerBoundary(0.5, BoundaryMode.FIXED);
plot.setDomainUpperBoundary(series.size() + 0.5, BoundaryMode.FIXED);
plot.setBorderStyle(XYPlot.BorderStyle.NONE, null, null);
plot.setDomainValueFormat(new DecimalFormat("0"));
plot.setRangeValueFormat(new DecimalFormat("0"));
Obviously this is more noticeable with small data series like the example above. Appreciate any help on this.
At least part of the issue is due to your use of VARIABLE_WIDTH. In this mode, Androidplot calculates the width of each bar evenly subdividing the plot graph space. When you have data on the edge (as you do with bar #3 above) it gets cutoff because the calculated width of the bar bleeds past the border.
I'd suggest applying some border paint to your bar formatted so it's easier to see when your bars are getting cutoff. To resolve the issue, you have at least 3 options:
Use FIXED_WIDTH bars. Should be self explanatory.
Set fixed domain boundaries and show an extra value on both ends of the scale:
plot.setDomainBoundaries(-1, 3, BoundaryMode.FIXED);
Add grid padding to allow the edges to be drawn
fully. In the latest version of Androidplot you'd do something like
this:
plot.getGraphWidget().getGridBox().setPaddingLeft(PixelUtils.dpToPix(70));
plot.getGraphWidget().getGridBox().setPaddingRight(PixelUtils.dpToPix(70));
You'll likely have to play with it to get the right setting though. Also keep in mind that as the screen gets wider, so do the bars; what looks good in portrait mode might look bad in landscape.
On a side note, I'm stumped as to why in the graphic above the first bar actually appears wider than the middle bar. Without seeing more of the code though, I couldn't say what's causing that.
Related
I'm currently working on a project using MPAndroidChart and i want to make a LineChart to represent data per hour. I need this chart to have gradient line depending on values on yAxis.
eg. In the graph above i want the line to change the color when yAxis values are >50.
I didn't find any solution to my problem, so any suggestions or examples are welcome
I've recently had to face the exact same problem, and the solution was to define a linear gradient and apply it as a shader, like this:
(Warning: You'll notice that the code looks funny, and that is because it is xamarin code (i.e: C#, not java nor kotlin), but you'll be able to translate it easily, mostly you have to change the PascalCase for camelCase, and change some properties into setters and getter or vice versa, just use your IDE's intellisense)
[set your line chart, add the dataset, etc]
...
var gradient = new LinearGradient(0f, 0f, 0f, 100f,
[your first color], [your second color],
Shader.TileMode.Clamp);
var paint = vh.Chart.Renderer.PaintRender;
paint.SetShader(gradient);
...
[set axis, labels, grid, etc]
...
lineChart.Invalidate(); //this is to refresh the chart on the screen, you may need it or not depending on your code
In the code above you just have to add your colors, and you may want to change the gradient direction (I'm using a vertical gradient here) by changing the 4 first floats when creating the linearGradient. One typical change is to set the y0 = 0 and y1 = the height of your chart, for example. You have to play with the values according to your layout.
The result for this code is something like:
Of course, I'm showing it with a repeated sample dataset, that's why you are seeing two cards with the same line.
UPDATE:
I understand what you want to achieve, and with the code above you can do it. You will need some calculations, of course, but you can set the "trigger" where the color starts to change at any Y coordinate you want.
For example, in this first picture, I've used the coordinates 0,0,0,100
(Please keep in mind that I'm showing you the last value in dp, but in the real code I am translating it to the equivalent in pixels according to the device's resolution. It is roughly the height of my chart)
And in the following image, I've changed it to 0,0,0,50:
As you can see, you have full control over how the gradient is shown just by changing the 4 values. (or, in my case, just one of them)
(You may notice that I've changed the colors as per my design, it has nothing to do with the threshold)
Lets say you are using for-loop to populate your data. In list you need to populate a list of colors for every value and then use lineDataSet.setColors(listOfColors).
List<Integer> listOfColors = new List<>();
for(i= 0; i<dataEntries.size(); i++){
//Logic for colors
if(i<= dataEntries.size()-1){ //Otherwise app will crash on last index
if(dataEntries.get(i+1).y > 50)
listOfColors.add(Color.GREEN);
else
listOfColors.add(Color.RED);
}
Then in the last
lineDataSet.setColors(listOfColors)
Hope, this will help you.
I'm trying to migrate androidplot from API 0.6 to 1.5, and found all equivalent methods, except one, XYPlot.graphWidget.setGridPadding(float left, float top, float right, float bottom)
This was removed from version 0.9.8 to 1.0.0, in this commit.
I created a sample project that shows the usage of the API.
The repository has two branches, AndroidPlot0.6.1 and AndroidPlot1.5.6 that show's the migration process and the solutions I tried without success.
Here is one screenshot with the grid padding, that show's the label in all points.
And here is one screenshot without the grid padding, that don't show the label in all points.
Does anyone know's how to solve this?
Thanks
This is unfortunately not an exact replacement for what you had as the padding is expressed in native units and not screen units, but it should allow you to achieve close to the same effect.
Add 1 "native unit" of space on each edge of a plot:
plot.calculateMinMaxVals();
RectRegion bounds = plot.getBounds();
plot.setRangeBoundaries(
bounds.getMinY().doubleValue() - 1, BoundaryMode.FIXED,
bounds.getMaxY().doubleValue() + 1, BoundaryMode.FIXED);
plot.setDomainBoundaries(
bounds.getMinX().doubleValue() - 1, BoundaryMode.FIXED,
bounds.getMaxX().doubleValue() + 1, BoundaryMode.FIXED);
If you absolutely need to express that space in pixels, there is a way to do it using XYPlot's screenToSeries conversion methods, but it's much less straightforward.
I have a DialogFragment that creates an AlertDialog with a custom view in onCreateDialog. The custom view includes a spinning progress bar and a prompt as well as a large view (larger than screen dimensions) that is fit inside the custom content via scaleX and scaleY values.
I am using the PixelCopy API to copy only the large view into a bitmap. This works well, but with a rather annoying caveat:
I call PixelCopy as such:
val winloc = intArrayOf(0, 0)
view.getLocationOnScreen(winloc)
val offset = 0
val left = winloc[0] + offset
val top = winloc[1] + offset
val rect = Rect(left, top, left + view.measuredWidth, top + view.measuredHeight)
PixelCopy.request(getDialog().getWindow(), rect, bitmap, listener, view.handler)
The view.getLocationOnScreen(winloc) returns x and y coordinates of (84, 84)
When I check the generated bitmap, PixelCopy has captured a whole lot of frame, padding, shadows, and the actual view content (with a bit missing from bottom right). The part of the actual content that is missing is exactly the same amount as the frame, padding, and shadow that I get at the top left of the image.
Having tried anything else I could think of to get the correct bounds of this content I want to save, I started adding random values to the coordinates (the offset value above). On a Nexus 6P an offset of 112 was perfect. Now I can't just throw 112 in there without reason because 1. it's a magic number and 2. it only works on one device.
I have ran out of ideas as to how I can get the correct bounds for this view OR where I can find this 112 value so that I can properly offset what I have.
As the app is unreleased, I can't include actual screenshots, but here are some redacted screenshots (ignore the black bars).
Some notes about the screenshots:
The green part comes from dialog!!.window!!.setBackgroundDrawable(ColorDrawable(0xff00ff00.toInt())) so I could outline the window bounds. The transparent/shadow bit between the screen edges and the green rectangle ... is this a margin or padding?
The white rectangle with the red (content 1) and the blue (content 2) rectangles is the portion going to the bitmap
The red and the blue rectangles within the content are different views generated from the same data.
The blue portion contains a few SurfaceViews that I suspect I'd have to extract separately.
Device screenshot
Captured Bitmap (without the 112 magic number offset)
The solution so far has been to ditch the DialogFragment and use a regular Fragment. After inspecting the layouts, there were two paddings of 56 pixels, without any accessors to get their values. I suspect there's a bug somewhere in the private decor views that doesn't account for these paddings when calculating the location of the view in window.
I'll mark this as the answer until a better answer comes along.
I am using CCTMXTiledMap on cocos2dx-2.2, I created and added the tiled map like this:
// TileMap
CCTMXTiledMap *m_pTileMap = CCTMXTiledMap::create("tilesets/my-isometric-small.tmx");
float fPosX = m_pTileMap->getPositionX();
float fPosY = m_pTileMap->getPositionY();
CCLOG( "TileMapPos: %f, %f", fPosX, fPosY );
this->addChild(m_pTileMap);
The tiled map are created and rendered successfully, but out of position. I use CCTMXTiledMap::getPosition, CCTMXLayer::positionAt, and also examine the CCSprite that I get from CCTMXLayer::tileAt... all of them are returning the correct value based on cocos2d screen coordinate { (0, 0) from bottom left and increasing upward and rightward } However, when viewed on the screen, there is always a slight offset and I can't get where it come from. All the m_obOffsetPosition are confirmed to be zero...
By correct value, I mean the tiles are positioned in the pink area (I getPosition from each of the tile, create CCSprite for each, setPosition of each tile and add it to the screen... They show up in the pink area)
Image supposed to be positioned at shady pink boxes, but instead positioned in the blue area (the entire blue sea is the whole map)
Any ideas are highly appreciated... Thanks!!
After wasting days trying to dissect tilemap_parallax_nodes in cocos2d-x, finally I figured out the culprit... it is the Layer Property cc_vertexz that cause it to be rendered off position. I haven't got the time to figure out how and why it works that way and since I'm not going to use it anyway (I just need flat, single layer, thus no need z order etc), so I just remove that property from all of my Layers and the problem is gone..
Hope it helps someone... Thanks!
I would simply like to have an Entity, Rectangle or something that would serve as a plain container for other Sprites.
Positioning purposes is what I need it for... I need to be able to set this container's width and position.
I have tried with Rectangle, but it seems to hide absolutely everything behind it (with a lower Z-index).
you can change the alpha of the Rectangle to "see" what's underneath it. Something like
myRect = new Rectangle(x, (CAMERA_HEIGHT - 480 + 100) / 2, size, size, vboManager);
myRect.setAlpha(0);
scene.attachChild(myRect);