[ACTUALIZAR] Para concluir esta pregunta, implementé mi gráfica utilizando los dos métodos siguientes (consulte a continuación). drawCurve()
recibe un Canvas
y una matriz de float
. La matriz está llena correctamente (las marcas de tiempo son asumidas por el índice de valor en la matriz) y varía de 0.0 a 1.0. La matriz se envía al prepareWindowArray()
que toma una porción de la matriz desde la posición windowStart
para los valores windowSize
, de forma circular.Gráfico dinámico personalizado en Android
La matriz utilizada por GraphView y por el proveedor de datos (un dispositivo Bluetooth) es la misma. Una clase en el medio asegura que GraphView no esté leyendo datos que están siendo escritos por el dispositivo Bluetooth. Como GraphView siempre recorre el conjunto y lo vuelve a dibujar en cada iteración, se actualizará de acuerdo con los datos escritos por el dispositivo Bluetooth, y al forzar la frecuencia de escritura del dispositivo Bluetooth a la frecuencia de actualización del Gráfico, obtengo un animación de mi señal.
invalidate()
método El GraphView
's es llamado por el Activity
, que ejecuta un Timer
para refrescar el gráfico en cada x
milisegundos. La frecuencia a la que se actualiza el gráfico se establece dinámicamente, de modo que se adapta al flujo de datos del dispositivo Bluetooth (que especifica la frecuencia de su señal en el encabezado de su paquete).
Encuentra el código completo de mi GraphView
en la respuesta que escribí a continuación (en la sección de respuestas). Si encuentran errores o una forma de optimizarlo, háganmelo saber; ¡podria ser muy apreciado!
/**
* 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 > 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 < windowSize; i++){
curve.lineTo(mLeftSide + (i*incrementX), source[i] * incrementY);
}
canvas.drawPath(curve, curvePaint);
}
El prepareWindowArray()
método que implementan el comportamiento circular de la matriz:
/**
* 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 < windowSize; i++){
if(windowStart+i < 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 >= data.length){
windowStart = windowStart % data.length;
}
return source;
}
[/ ACTUALIZACIÓN]
estoy haciendo una aplicación que lee los datos de un dispositivo Bluetooth en una tasa fija. Cada vez que tengo datos nuevos, quiero que se tracen en el gráfico de la derecha y que traduzcan el resto del gráfico a la izquierda en tiempo real. Básicamente, como haría un osciloscopio.
Así que hice una vista personalizada, con eje xy, un título y unidades. Para hacer esto, simplemente dibujo esas cosas en el lienzo de la Vista. Ahora quiero dibujar la curva. Me las arreglo para dibujar una curva estática de una matriz ya está lleno con este método:
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 < mData[0].length; i++)
mCurve.lineTo(left + ((float)mData[0][i] * 5), middle-((float)mData[1][i] * 20));
canvas.drawPath(mCurve, curvePaint);
}
Me da algo como esto.
Todavía hay cosas que arreglar en mi gráfico (las sub-eje no están correctamente escala), pero estos son detalles que puedo arreglar después.
Ahora quiero cambiar este gráfico estático (que recibe una matriz de valores no dinámica) con algo dinámico que redibuja la curva cada 40ms, empujando los datos antiguos hacia la izquierda y trazando los datos nuevos a la derecha, para poder visualizar en tiempo real la información proporcionada por el dispositivo Bluetooth.
Sé que ya hay algún paquete de gráficos que existe, pero soy un poco novato con estas cosas y me gustaría hacer prácticas al implementar este gráfico yo mismo.Además, la mayor parte de mi clase GraphView está hecha, excepto por la parte de la curva.
Segunda pregunta, me pregunto cómo debería enviar los nuevos valores al gráfico. ¿Debo usar algo así como una pila FIFO, o puedo lograr lo que quiero con una simple matrice de dobles?
En una nota lateral, los 4 campos en la parte inferior ya se actualizan dinámicamente. Bueno, están simulando la "dinámica", recorren la misma doble matrice una y otra vez, en realidad no toman valores nuevos.
¡Gracias por su tiempo! Si algo no está claro sobre mi pregunta, hágamelo saber y lo actualizaré con más detalles.
¿Puedes dar el enlace para todos los proyectos? este aspecto muy interesante! –