I am building an Android Application and I am using AChartEngine to display a graph and values I obtain.
I am able to graph the values I have properly. However, once the user zooms in and pans around my graph, it becomes difficult to view the data. One option I considered is to display a "redraw graph" button where once the user clicks it, the graph would redraw itself to the original state, zoom and pan would also reset.
Here's by code thus far:
public void redrawGraph(View view){
chartView.repaint();
}
where redrawGraph is a the onClick function of my button and chartView is an object of type GraphicalView.
However, that does seem to do anything as it only repaints changes in my graph (if I added/removed series renderers, series, etc).
How can I reset the zoom and pan of graphs in AChartEngine?
My answer refers to XY/time charts. First you have to enable the external zoom feature in every renderer you use. The snippet below shows the code for accomplishing this:
yourMultipleSeriesRenderer.setExternalZoomEnabled(true);
In your method redrawGraph you then have to execute the command shown below:
public void redrawGraph(View view) {
chartView.zoomReset();
}
This works great for me. If you need any further information leave a comment.
Put this in your Multiple Series Render
multiRenderer.setZoomButtonsVisible(false);
// setting pan enablity which uses graph to move on both axis
multiRenderer.setPanEnabled(true, false);
// setting click false on graph
multiRenderer.setClickEnabled(false);
// setting zoom to false on both axis
multiRenderer.setZoomEnabled(true, false);
// setting lines to display on y axis
multiRenderer.setShowGridY(true);
// setting lines to display on x axis
multiRenderer.setShowGridX(true);
// setting legend to fit the screen size
multiRenderer.setFitLegend(true);
// setting displaying line on grid
multiRenderer.setShowGrid(true);
// setting zoom to false
multiRenderer.setZoomEnabled(false);
// setting external zoom functions to false
multiRenderer.setExternalZoomEnabled(false);
Related
I am using the MPAndroidChart library to graph live sensor data in a line graph. I'm hoping to give the data a sort of 'buffer' on the right-hand end so that new data is in the centre of the screen. I've tried to illustrate what I mean below:
Basically, I have got it so the ViewPort scrolls and shows the most recent item in the right-most end of the screen, but I would like it in the centre. Thanks!
you need to set Xaxis maximum, set visibleXrange and last move the viewport
double range = 5; // how many data you want to show in view port
double maxX = 100; // your highest X value
chartView.getXAxis().setAxisMaximum(maxX + range/2);
chartView.setVisibleXRange(range, range);
chartView.moveViewToX(maxX);
chartView.notifyDataSetChanged();
Suppose I have six points drawn in a Canvas along the circumference of a circle, in the pattern of a hexagon.
I want to simultaneously move and re-draw each of these six points in a small circular path - for example, each point moves along the circumference of a circle with 1/10th the radius of the larger circle.
I'm a little lost on how to do this with Canvas and onDraw and I don't know if better solutions exist. How would you do this?
Some answers have at least pointed me toward this which shows how a point might move along a circular path, but I don't know how to implement it for this situation:
for (double t = 0; t < 2*Pi; t += 0.01)
{
x = R*cos(t) + x_0;
y = R*sin(t) + y_0;
}
Thank you!
Here's one approach:
Start with a custom view that extends View and overrides onDraw. You are on the right track with using the drawing functions of Canvas.
Give your custom view a field to hold the current angle:
private float mTheta;
Then add a method like:
public void setTheta(float radians) {
mTheta = radians;
invalidate();
}
Then in onDraw, use the current value of mTheta to calculate the position of your points.
Of course, with the custom view you might need to handle sizing with an onMeasure override and possibly some layout with an onLayout override. If you set the dimensions of the view to an absolute dp value, the default behavior should work for you.
Doing it this way will set you up to override onTouch and allow user interaction to move the graphics, or use a ValueAnimator to cycle from 0 to 2π calling setTheta from within your AnimatorUpdateListener.
Put some code together and when you get stuck, post another question. If you add a comment to this answer with a link to the new question, I'll take a look at it.
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 have set min max range for y-axis values but graph can scroll beyond those values which hampers the look and feel of graph. I wish to control/stop scrolling of graph along the y-axis. How can I do that?
What you need is specify which axis panning is available for: mRenderer.setPanEnabled(boolean enabledX, boolean enabledY)
Click here for more
use the following for locking both x and y axis scrolling, based on our need boolean values can be specified
render.setPanEnabled(false, false);
use setPanLimits method.
mRenderer.setPanLimits(new double[]{0,10,0,5});
this code will disable panning beyond 0 to the left,10 to the right on X axis,and beyond 0 down and 5 up on Y axis.
I guess the title gives away most of my questions, but let's detail and give a bit of background:
I have an Android app focused mainly for tablets that will be displaying a few different real-time data in TimeCharts. So I already have a service to communicate with the data source that grabs the data, parse it and add the values to singletons TimeSeries. When the user navigates in and out of fragments the fragment simply adds the correct TimeSeries and keeps calling mChartView.repaint(); every 500ms
All that is operational and working nicely across tabs, rotation, notification, etc.
Because it's a tablet app I want to achieve two things: 1) maximize viewing area; 2) make it look good.
1) I already removed the zoom buttons (for some reason if I try to add them it gives me NullException) with mRenderer.setZoomButtonsVisible(false); but there's still a huge bottom margin taking a lot of screen space. I tried setting a negative border, and it works, but the labels don't move with this border and I end up with the X axis crossing over the labels or even passing them.
How can I move the labels down with the border, or maybe put them on the top of the graph?
2)
a) the app is user configurable to use either Holo_Dark or Holo_light. So I'm using a transparent background on the renderer (see snippet below) to show that nice background shading that holo does. How to a change the axis text color? I found the mRenderer.setAxesColor(color) but it only change the line, not the text.
int color = Color.argb(0, 255, 255, 255); // Transparent colour
mRenderer.setBackgroundColor(color);
mRenderer.setMarginsColor(color);
b) the Y axis is crossing on top of it's labels, how can I offset them a bit to the left so the text is readable? (it's not with setMargins())
c) when the user zoom and pan around the chart, the chart stops updating/re-zooming the latest values on the screen and the user have to keep panning to see the latest values. I added a 'Reset Zoom' button on the ActionBar, but I can't make it work. What would be the code to make the values update again.
d) if my TimeSeires have a range of 10min (2 values per second) how can I make it only show the last 1min (for example) and if the user want's previous values he can pan back? (and the use button on c) re-start)
at the moment my rendered is setup as:
int color = Color.argb(0, 255, 255, 255); // Transparent colour
mRenderer.setBackgroundColor(color);
mRenderer.setMarginsColor(color);
mRenderer.setAxisTitleTextSize(16); // 16
mRenderer.setChartTitleTextSize(20); // 20
mRenderer.setLabelsTextSize(15); // 15
mRenderer.setLegendTextSize(15); // 15
mRenderer.setPointSize(0); // 10
mRenderer.setMargins(new int[] { 20, 30, 15, 0 });
mRenderer.setZoomButtonsVisible(false);
mRenderer.setShowAxes(true);
mRenderer.setShowLegend(true);
mRenderer.setShowGridX(true);
mRenderer.setShowLabels(true);
and each rendered added to the Multiple renderer goes like:
renderer.setDisplayChartValues(false);
mRenderer.addSeriesRenderer(renderer);
thanks a lot for all help!
Editing just to include my final code for others to see:
I've ended up ditching the legend completely and implementing my own legend floating above the chart, so the layout is a FrameLayout with a ChartView and a TableLayout.
I implemented an abstract CurvesFragment Class that is extended by a few classes.
I configure the chart during my fragment OnCreateView as:
mChartView = ChartFactory.getTimeChartView(getActivity().getApplicationContext(), mDataset, mRenderer, "HH:mm:ss"); // X axis in a time scale
// Setup chart renderer =============================
mRenderer.setLabelsTextSize(15); // Text size
mRenderer.setMargins(new int[] { 0, Utils.convertToPx(5, getActivity().getApplicationContext()), 0, 0 }); // 5dp on the left to show that little line
mRenderer.setZoomButtonsVisible(false); // bye bye zoom
mRenderer.setShowAxes(true); // show both axes
mRenderer.setShowLegend(false); // bye bye legend
mRenderer.setShowGridX(true); // X grid helps identify values
mRenderer.setShowLabels(true); // See the values
mRenderer.setYLabelsAlign(Align.LEFT); // put the Y labels on the left of the axis
if (Utils.getCurrentTheme(getActivity().getApplicationContext()) == android.R.style.Theme_Holo) {
mRenderer.setXLabelsColor(getResources().getColor(android.R.color.primary_text_dark));
mRenderer.setYLabelsColor(0, getResources().getColor(android.R.color.primary_text_dark));
mRenderer.setMarginsColor(getResources().getColor(android.R.color.background_dark));
legend.setBackgroundResource(R.drawable.border_dark);
} else {
// same as above but for Holo_light
}
// And from here proceed to add the TimeCharts with using
renderer.setDisplayChartValues(false);
On my action bar I included buttons for Pause/Resume and ResetZoom.
the Reset Zoom is simple:
mRenderer.setXAxisMax(-Double.MAX_VALUE);
mRenderer.setXAxisMin(Double.MAX_VALUE);
mRenderer.setYAxisMax(-Double.MAX_VALUE);
mRenderer.setYAxisMin(Double.MAX_VALUE);
mChartView.repaint();
There's a background service that holds references to the TimeSeries that is always reading fresh data from the WiFi and adding them to the series. That way, in the fragment there's a runnable executing every 700ms that calls repaint() as:
// Chart rendering control ========================
private Runnable updateDataSet = new Runnable() {
public void run() {
if (!chartPaused) {
double max = mRenderer.getXAxisMax(); // get renderer maximum value
double min = mRenderer.getXAxisMin(); // get renderer minimum value
// Check if the user scrolled/zoomed/panned the graph or if it's on 'auto'
if (!((max == Double.MAX_VALUE || max == -Double.MAX_VALUE) && (min == Double.MAX_VALUE || min == -Double.MAX_VALUE))) {
double newMax = mDataset.getSeriesAt(0).getMaxX(); // new max is the latest value from our series
double newMin = newMax - Math.abs(max - min); // new min value is the X range the user zoomed into
mRenderer.setXAxisMax(newMax); // set the new values
mRenderer.setXAxisMin(newMin);
}
// Logic that updates the TableLayout with the legend is placed here
mChartView.repaint();
}
getView().postDelayed(updateDataSet, 700);
}
};
and there's a ZoomListener and a PanListener that pauses the chart every time the user touches it. That way the user can zoom and pan around, but whenever it resumes the chart updating it will only scroll forward to the earliest data without changing the zoom level.
I guess the final result is pretty solid and looks nice on both Holo_Light and Holo_Dark.
Thanks a LOT for Dan for the answers and for all other devs that created the engine.
happy coding!
Question 1. You can try to play with these API calls:
mRenderer.setShowLegend(false);
or
mRenderer.setMargins(new int[] {top, left, bottom, right});
Maybe decrease the bottom value?
Question 2.
a)
mRenderer.setXLabelsColor();
mRenderer.setYLabelsColor();
b)
mRenderer.setXLabelsAlign();
c)
I think you can do the following, when the series get new values:
mRenderer.setXAxisMin();
mRenderer.setXAxisMax();
Provide the x min and x max values as the range to be displayed and call mChartView.repaint(); after that.
d) Same as for c)
As a suggestion, please try to split such long questions in several smaller ones and you may get answers faster. There aren't many ACE users to know how to answer all questions. Even the author of this library has to think about some answers :)
For removal of scrolling of that chart : mRenderer.setZoomEnabled(false); mRenderer.setPanEnabled(false);