Note that there are some explanatory texts on larger screens.

plurals
  1. POCustom dynamic graph in Android
    primarykey
    data
    text
    <p><strong>[UPDATE]</strong> To conclude this question, I implemented my graph using the following two methods (see below). <code>drawCurve()</code> receives a <code>Canvas</code> and an array of <code>float</code>. The array is properly filled (timestamps are assumed by the value index in the array) and varies from 0.0 to 1.0. The array is sent to <code>prepareWindowArray()</code> that takes a chunk of the array from position <code>windowStart</code> for <code>windowSize</code>-values, in a circular manner.</p> <p>The array used by the GraphView and by the data provider (a Bluetooth device) is the same. A Class in the middle ensures that GraphView is not reading data that are being written by the Bluetooth device. Since the GraphView always loop thru the array and redraw it at every iteration, it will update according to the data written by the Bluetooth device, and by forcing the write frequency of the Bluetooth device to the refresh frequency of the Graph, I obtain a smooth animation of my signal.</p> <p>The <code>GraphView</code>'s <code>invalidate()</code> method is called by the <code>Activity</code>, which run a <code>Timer</code> to refresh the graph at every <code>x</code> milliseconds. The frequency at which the graph is refreshed is dynamically set, so that it adapt to the flow of data from the Bluetooth device (which specify the frequency of its signal in the header of its packet).</p> <p>Find the complete code of my <code>GraphView</code> in the answer I wrote below (in the answer section). If you guys find errors or way to optimize it, please let me know; it would be greatly appreciated!</p> <pre><code>/** * Read a buffer array of size greater than "windowSize" and create a window array out of it. * A curve is then drawn from this array using "windowSize" points, from left * to right. * @param canvas is a Canvas object on which the curve will be drawn. Ensure the canvas is the * later drawn object at its position or you will not see your curve. * @param data is a float array of length &gt; windowSize. The floats must range between 0.0 and 1.0. * A value of 0.0 will be drawn at the bottom of the graph, while a value of 1.0 will be drawn at * the top of the graph. The range is not tested, so you must ensure to pass proper values, or your * graph will look terrible. * 0.0 : draw at the bottom of the graph * 0.5 : draw in the middle of the graph * 1.0 : draw at the top of the graph */ private void drawCurve(Canvas canvas, float[] data){ // Create a reference value to determine the stepping between each points to be drawn float incrementX = (mRightSide-mLeftSide)/(float) windowSize; float incrementY = (mBottomSide - mTopSide); // Prepare the array for the graph float[] source = prepareWindowArray(data); // Prepare the curve Path curve = new Path(); // Move at the first point. curve.moveTo(mLeftSide, source[0]*incrementY); // Draw the remaining points of the curve for(int i = 1; i &lt; windowSize; i++){ curve.lineTo(mLeftSide + (i*incrementX), source[i] * incrementY); } canvas.drawPath(curve, curvePaint); } </code></pre> <p>The <code>prepareWindowArray()</code> method that implement the circular behavior of the array:</p> <pre><code>/** * Extract a window array from the data array, and reposition the windowStart * index for next iteration * @param data the array of data from which we get the window * @return an array of float that represent the window */ private float[] prepareWindowArray(float[] data){ // Prepare the source array for the graph. float[] source = new float[windowSize]; // Copy the window from the data array into the source array for(int i = 0; i &lt; windowSize; i++){ if(windowStart+i &lt; data.length) // If the windows holds within the data array source[i] = data[windowStart + i]; // Simply copy the value in the source array else{ // If the window goes beyond the data array source[i] = data[(windowStart + 1)%data.length]; // Loop at the beginning of the data array and copy from there } } // Reposition the buffer index windowStart = windowStart + windowSize; // If the index is beyond the end of the array if(windowStart &gt;= data.length){ windowStart = windowStart % data.length; } return source; } </code></pre> <p><strong>[/UPDATE]</strong></p> <p>I'm making an app that read data from a Bluetooth device at a fixed rate. Everytime that I have new data, I want them to be plotted on the graph to the right, and to translate the remainder of the graph to the left in realtime. Basically, like an oscilloscope would do.</p> <p>So I made a custom View, with xy axis, a title and units. To do this, I simply draw those things on the View canvas. Now I want to draw the curve. I manage to draw a static curve from an already filled array using this method:</p> <pre><code>public void drawCurve(Canvas canvas){ int left = getPaddingLeft(); int bottom = getHeight()-getPaddingTop(); int middle = (bottom-10)/2 - 10; curvePaint = new Paint(); curvePaint.setColor(Color.GREEN); curvePaint.setStrokeWidth(1f); curvePaint.setDither(true); curvePaint.setStyle(Paint.Style.STROKE); curvePaint.setStrokeJoin(Paint.Join.ROUND); curvePaint.setStrokeCap(Paint.Cap.ROUND); curvePaint.setPathEffect(new CornerPathEffect(10) ); curvePaint.setAntiAlias(true); mCurve = new Path(); mCurve.moveTo(left, middle); for(int i = 0; i &lt; mData[0].length; i++) mCurve.lineTo(left + ((float)mData[0][i] * 5), middle-((float)mData[1][i] * 20)); canvas.drawPath(mCurve, curvePaint); } </code></pre> <p>It gives me something like this.</p> <p><img src="https://i.stack.imgur.com/1lQt9.png" alt="My custom GraphView"></p> <p>There are still things to fix on my graph (the sub-axis are not properly scaling), but these are details I can fix later.</p> <p>Now I want to change this static graph (that receives a non-dynamic matrice of values) with something dynamic that would redraw the curve every 40ms, pushing the old data to the left and plotting the new data to the right, so I could visualise in real time the information provided by the Bluetooth device.</p> <p>I know there are some graphing package that exists already, but I'm kinda noob with these things and I'd like to pratice by implementing this graph myself. Also, most of my GraphView class is done, except for the curve part.</p> <p>Second question, I'm wondering how I should send the new values to the graph. Should I use something like a FIFO stack, or can I achieve what I want with a simple matrice of doubles?</p> <p>On a side note, the 4 fields at the bottom are already dynamically updated. Well, they are kind of faking the "dynamic", they loop thru the same double matrice again and again, they don't actually take fresh values.</p> <p>Thanks for your time! If something's unclear about my question, let me know and I'll update it with more details.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload