Плохая производительность при непрерывном рисовании в CustomView

Случай использования:

Мне нужно нарисовать сотни строк и несколько фрагментов текста на моем представлении. Мне нужно дать эффект прокрутки, для которого я захватываю событие ACTION_MOVE и перерисовываю все строки с обновленными точками. Чтобы получить результат желания, я попытался с разными подходами, но никто не работает по назначению.

Подход 1

Я создал собственный класс, который расширяет View. Все чертежи и вычисления выполняются непосредственно в моем методе onDraw(). Поскольку в методе onDraw() выполняется так много операций, производительность приложения очень низкая. Я даже проверил производительность с помощью профиля GPU рендеринга, и я вижу, что линии очень высокие.

Подход 2

Я создал растровое изображение и после рисования всех строк на моем растровом изображении в другом потоке, я использовал postInvalidate() для рисования растрового изображения в методе onDraw():

mBufferedBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);   
mBufferedBitmap.eraseColor(Color.TRANSPARENT);    
Canvas mBufferedCanvas = new Canvas(mBufferedBitmap);               
drawLines(mBufferedCanvas)    
postInvalidate();

Так как я стираю весь предыдущий рисунок на растровом изображении и рисую новые строки с обновленными точками, на экране появляется мерцание.

Подход 3

Я попытался расширить свой собственный класс до SurfaceView и выполнить все операции над объектом canvas в другом потоке. Но поскольку SurfaceView использует CPU для операций рисования, производительность будет низкой в ​​мобильных телефонах с низкой конфигурацией.

Может ли кто-нибудь вести меня, как достичь этой задачи с лучшей производительностью?

Ответ 1

Для достижения хорошей производительности можно использовать подход 1.

Пример, который звучит близко к вашему случаю использования (рисование линий, небольшой текст и наличие этого обновления при движении жестов) MPAndroidChart. Это библиотека с открытым исходным кодом, которая обеспечивает высокую производительность (см. следующее сравнение, если вы хотите получить статистику)

Класс для изучения - это классы Renderer, так как они содержат код в onDraw(Canvas c) подтипов диаграммы. Вы можете увидеть некоторые из трюков, используемых для достижения высокой производительности:

  • Не выделяйте в цикле рендеринга. Вместо этого выделяйте вне цикла и повторно использовать/перерабатывать переменные. См. LineChartRenderer строка 199
  • Используйте буферизацию. Например, в MPAndroidChart точки для четырех углов баров на гистограмме буферизуются, а буферный массив повторно используется. См. BarBuffer класс.
  • Используйте собственные функции Canvas (drawPath, drawLine и т.д.)

Полный список советов по оптимизации рендеринга можно найти в Android Performance Руководство по медленному рендерингу

Ответ 2

Подход 2 - лучший. Если вы видите мерцание, это означает, что растровое изображение рисуется на экране после его удаления и перед тем, как вы нарисуете все линии. Если это так, используйте другое растровое изображение и выполните двойную буферизацию:

ScreenBitmap - это то, что рисуется на экране OffScreenBitmap используется для рисования в фоновом режиме.

Нарисуйте все ваши строки и текст на OffScreenBitmap, и как только закончите, скопируйте его в ScreenBitmap. в onDraw нарисуйте ScreenBitmap.

Создайте эти растровые изображения один раз (обычно в onSizeChanged), чтобы в onDraw не было alloocation