Пользовательский динамический график в Android

[ОБНОВЛЕНИЕ] Чтобы завершить этот вопрос, я реализовал свой график, используя следующие два метода (см. Ниже). drawCurve () получает Canvas и массив float . Массив правильно заполнен (временные метки принимаются индексом значения в массиве) и варьируется от 0,0 до 1.0.Массив отправляется в prepareWindowArray () , которая принимает фрагмент массива из позиции windowStart для значений windowSize по кругу.

Массив, используемый GraphView и поставщиком данных (устройством Bluetooth), одинаков. Класс в середине гарантирует, что GraphView не читает данные, которые записываются устройством Bluetooth. Поскольку GraphView всегда проходит цикл через массив и перерисовывать его на каждой итерации, он будет обновляться в соответствии с данными, записанными устройством Bluetooth, и путем принудительной записи частоты Когда устройство Bluetooth соответствует частоте обновления графика, я получаю плавную анимацию своего сигнала.

Метод GraphView invalidate () вызывается действием Activity , которое запускает таймер для обновления графика в каждые x миллисекунды.Частота обновления графика устанавливается динамически, чтобы он адаптировался к потоку данных от устройства Bluetooth (которые указывают частоту его сигнала в заголовке его пакета).

Найдите полный код моего GraphView в ответе, который я написал ниже (в разделе ответов). Если вы, ребята, найдете ошибки или способы их оптимизации, дайте мне знать; был бы очень признателен!

/**
 * 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);

}

Метод prepareWindowArray () , реализующий циклическое поведение массива:

/**
 * 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;
}

[/ UPDATE]

Я делаю приложение, которое считывает данные с устройства Bluetooth с фиксированной скоростью. Каждый раз, когда у меня появляются новые данные, я хочу, чтобы они отображались на графике справа, а оставшаяся часть графика переводилась влево в реальном времени. В принципе, как осциллограф.

Итак, я создал собственный вид с осью xy, заголовком и единицами измерения. Для этого я просто рисую эти объекты на холсте просмотра. Теперь я хочу нарисовать кривую. Мне удается нарисовать статическую кривую из уже заполненного массива, используя этот метод:

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

Это дает мне что-то вроде этого.

My custom GraphView

На моем графике есть еще кое-что, что нужно исправить (суб-ось не масштабируется должным образом), но это детали, которые я могу исправить позже.

Теперь я хочу изменить этот статический график (который получает нединамическую матрицу значений) на что-то динамическое, которое перерисовывало бы кривую каждые 40 мс, сдвигая старые данные влево и отображая новые данные вправо, поэтому Я мог визуализировать в реальном времени информацию, предоставляемую устройством Bluetooth.

Я знаю, что уже существует какой-то пакет для построения графиков, но я в некотором роде новичок в этих вещах и хотел бы поработать, реализовав этот график сам. Кроме того, большая часть моего класса GraphView выполнена, за исключением части кривой.

Второй вопрос. Мне интересно, как мне отправить новые значения на график. Должен ли я использовать что-то вроде стека FIFO, или я могу добиться того, чего хочу, с помощью простой матрицы удвоений?

Кстати, 4 поля внизу уже динамически обновляются. Что ж, они как бы подделывают «динамику», они снова и снова проходят через одну и ту же двойную матрицу, на самом деле они не принимают новые значения.

Спасибо за уделенное время! Если что-то неясно по моему вопросу, дайте мне знать, и я обновлю его, добавив более подробную информацию.

7
задан AntoineG 16 November 2011 в 18:44
поделиться