MpandroidChart в реальном времени сглаживание сюжета при обновлении записей со статическим просмотром и живым курсоромAndroid

Форум для тех, кто программирует под Android
Ответить
Anonymous
 MpandroidChart в реальном времени сглаживание сюжета при обновлении записей со статическим просмотром и живым курсором

Сообщение Anonymous »

Я разрабатываю аудио-приложение BLE в реальном времени на Android. Скорость выборки аудио составляет 16 000 образцов в секунду, и я получаю данные в 100 пакетах в секунду, каждый пакет имеет 160 образцов (так что ровно 16 кГц). < /P>
Для визуализации я понижаю данные от 16 кГц до 500 Гц и настройте его с помощью Linechart MpandroidChart. Диаграмма настроена на: < /p>
static (не разбрызгиваемое) < /p>
Фиксированная ширина просмотра < /p>
Повторное использование объектов ввода для оптимизации производительности < /p>
показывает «пробел курсора», чтобы указать текущую обновленную позицию. Позиция обновления достигает конца видимого видоубийта, график графиков - она либо прыгает, мерцает, либо кратко показывает противоречивые значения. < /p>
Вот код (я сделал демо -версию, моделируя аудиоданные с помощью цикла): < /p>

Код: Выделить всё

@Composable
fun LiveWaveformChart(
modifier: Modifier = Modifier,
visibleEntries: Int = 2000
) {
val context = LocalContext.current
val chart = remember { LineChart(context) }

val cursorPosition = remember { mutableIntStateOf(0) }
val entriesBuffer = remember { mutableStateListOf() }

val gapSize = remember { (visibleEntries * 0.02).toInt().coerceAtLeast(5) }
val cursorOffset = remember { (visibleEntries * 0.9975).toInt() }

LaunchedEffect(visibleEntries) {
entriesBuffer.clear()
entriesBuffer.addAll(List(visibleEntries) { Entry(it.toFloat(), 0f) })
setupChart(chart, visibleEntries)
}

DisposableEffect(Unit) {
onDispose {
dataPointer = 0
}
}

AndroidView(
factory = { chart },
modifier = modifier.fillMaxSize()
)

LaunchedEffect(Unit) {
withContext(Dispatchers.Default) {
while (true) {
delay(10)
val audioData = ShortArray(5) { Random.nextInt(-1000, 1000).toShort() }
withContext(Dispatchers.Main) {
plotAudio(
chart = chart,
audio = audioData,
entriesBuffer = entriesBuffer,
cursorPosition = cursorPosition,
visibleEntries = visibleEntries,
gapSize = gapSize
)
}
}

}
}
}

private fun setupChart(chart: LineChart, visibleEntries: Int) {
chart.doOnLayout {
val xGridLines = it.width / 60
val yGridLines = it.height / 60
applyDynamicGrid(chart, xGridLines, yGridLines)
}

chart.apply {
description.isEnabled = false
setTouchEnabled(false)
isDragEnabled = false
setScaleEnabled(false)
setBackgroundColor(Color.WHITE)
isAutoScaleMinMaxEnabled = true
axisRight.isEnabled = false
legend.isEnabled = false
setHardwareAccelerationEnabled(true)
setViewPortOffsets(0f, 0f, 0f, 0f)
setMaxVisibleValueCount(visibleEntries)

data = LineData().apply {
setDrawValues(false)
}
}
}

private var dataPointer = 0

fun plotAudio(
chart: LineChart,
audio: ShortArray,
entriesBuffer: MutableList,
cursorPosition: MutableState,
visibleEntries: Int,
gapSize: Int
) {
for (i in audio.indices) {
if (dataPointer >= visibleEntries) dataPointer = 0
entriesBuffer[dataPointer].y = audio[i].toFloat()
dataPointer++
}

cursorPosition.value = (dataPointer) % visibleEntries

val gapStart = cursorPosition.value
val gapEnd = (cursorPosition.value + gapSize) % visibleEntries

val (entriesBeforeGap, entriesAfterGap) = calculateGapEntries(
entriesBuffer,
visibleEntries,
gapStart,
gapEnd
)

val dataSet1 = createLineDataSet(entriesBeforeGap, "BeforeGap")
val dataSet2 = createLineDataSet(entriesAfterGap, "AfterGap")

chart.data?.clearValues()
chart.data?.addDataSet(dataSet1)
chart.data?.addDataSet(dataSet2)

chart.data?.notifyDataChanged()
chart.notifyDataSetChanged()
chart.setVisibleXRangeMaximum(visibleEntries.toFloat())
chart.moveViewToX(dataPointer.toFloat())
chart.invalidate()
}

private fun calculateGapEntries(
buffer: List,
visibleEntries: Int,
gapStart: Int,
gapEnd: Int
): Pair {
return if (gapStart <  gapEnd) {
buffer.subList(0, gapStart) to buffer.subList(gapEnd, visibleEntries)
} else {
val before = buffer.subList(gapEnd, gapStart)
val after = buffer.subList(0, gapEnd) + buffer.subList(gapStart, visibleEntries)
before to after
}
}

private fun createLineDataSet(entries: List, label: String): LineDataSet {
return LineDataSet(entries, label).apply {
mode = LineDataSet.Mode.LINEAR
setDrawCircles(false)
setDrawValues(false)
color = Color.RED
lineWidth = 1f
}
}

private fun applyDynamicGrid(chart: LineChart, xGridLines: Int, yGridLines: Int) {
chart.xAxis.apply {
setDrawLabels(false)
setDrawGridLines(true)
setDrawAxisLine(false)
labelCount = xGridLines
granularity = 1f
isGranularityEnabled = true
gridColor = Color.LTGRAY
gridLineWidth = 0.4f
}

chart.axisLeft.apply {
setDrawLabels(false)
setDrawGridLines(true)
setDrawAxisLine(false)
axisMinimum = -32768f
axisMaximum = 32767f
labelCount = yGridLines
granularity = (axisMaximum - axisMinimum) / yGridLines
isGranularityEnabled = true
gridColor = Color.LTGRAY
gridLineWidth = 0.4f
}
}
Как правильно реализовать плавный курсор или зазор в непротягивающемся некручивающемся в режиме реального времени MpandroidChart, не вызывая глюки или мерцающие, когда текущий индекс оборачивается вокруг ширины диаграммы?

Подробнее здесь: https://stackoverflow.com/questions/797 ... c-viewport
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»