Плохая производительность при непрерывном рисовании в 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