Я создал пример RecyclerView на основе руководства по созданию списков и карточек . У моего адаптера есть реализация паттерна только для надувания макета.
Проблема в плохой скорости прокрутки. Это в RecycleView всего с 8 элементами.
В некоторых тестах я подтвердил, что в Android L эта проблема не возникает. Но в версии KitKat снижение производительности заметно.
Ответы:
Недавно я столкнулся с той же проблемой, поэтому вот что я сделал с последней библиотекой поддержки RecyclerView:
Замените сложный макет (вложенные представления, RelativeLayout) новым оптимизированным ConstraintLayout. Активируйте его в Android Studio: перейдите в SDK Manager -> вкладка SDK Tools -> Support Repository -> проверьте ConstraintLayout для Android и Solver для ConstraintLayout. Добавьте к зависимостям:
compile 'com.android.support.constraint:constraint-layout:1.0.2'
Если возможно, сделайте все элементы RecyclerView одинаковой высоты . И добавить:
recyclerView.setHasFixedSize(true);
Используйте методы кеширования чертежей RecyclerView по умолчанию и настройте их в соответствии с вашим случаем. Для этого вам не нужна сторонняя библиотека:
recyclerView.setItemViewCacheSize(20); recyclerView.setDrawingCacheEnabled(true); recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
Если вы используете много изображений , убедитесь, что их размер и степень сжатия оптимальны . Масштабирование изображений также может повлиять на производительность. Есть две стороны проблемы - используемое исходное изображение и декодированное растровое изображение. Следующий пример дает вам подсказку, как декодировать изображение, загруженное из Интернета:
InputStream is = (InputStream) url.getContent(); BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.RGB_565; Bitmap image = BitmapFactory.decodeStream(is, null, options);
Самая важная часть уточняющая
inPreferredConfig
- она определяет, сколько байтов будет использовано для каждого пикселя изображения. Имейте в виду, что это предпочтительный вариант. Если исходное изображение имеет больше цветов, оно все равно будет декодировано с другой конфигурацией.Убедитесь, что onBindViewHolder () как можно дешевле . Вы можете установить OnClickListener один раз
onCreateViewHolder()
и вызвать через интерфейс прослушиватель вне адаптера, передавая выбранный элемент. Таким образом, вы не будете постоянно создавать лишние объекты. Также проверьте флаги и состояния, прежде чем вносить какие-либо изменения в представление здесь.viewHolder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Item item = getItem(getAdapterPosition()); outsideClickListener.onItemClicked(item); } });
При изменении данных попробуйте обновить только затронутые элементы . Например, вместо того, чтобы аннулировать весь набор данных
notifyDataSetChanged()
, при добавлении / загрузке дополнительных элементов просто используйте:С веб-сайта разработчика Android :
Но если вам нужно его использовать, поддерживайте свои элементы с уникальными идентификаторами :
adapter.setHasStableIds(true);
Даже если вы все сделаете правильно, есть вероятность, что RecyclerView по-прежнему работает не так гладко, как хотелось бы.
источник
recyclerView.setItemViewCacheSize(20);
может улучшить производительность. НоrecyclerView.setDrawingCacheEnabled(true);
иrecyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
хоть! Я не уверен, что это что-то изменит. ЭтоView
особые вызовы, которые позволяют программно получать кэш чертежа в виде растрового изображения и использовать его в дальнейшем в своих интересах.RecyclerView
похоже, ничего не делает с этим.Я решил эту проблему, добавив следующий флаг:
https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#setHasStableIds(boolean)
источник
Я обнаружил по крайней мере один паттерн, который может убить вашу производительность. Помните, что
onBindViewHolder()
это часто называют . Таким образом, все, что вы делаете в этом коде, может привести к остановке вашей производительности. Если ваш RecyclerView выполняет какие-либо настройки, очень легко случайно добавить медленный код в этот метод.Я менял фоновые изображения каждого RecyclerView в зависимости от позиции. Но загрузка изображений требует небольшой работы, в результате чего мой RecyclerView работает медленно и рывками.
Создание кеша для изображений творит чудеса;
onBindViewHolder()
теперь просто изменяет ссылку на кешированное изображение вместо того, чтобы загружать его с нуля. Теперь RecyclerView движется вперед.Я знаю, что не у всех будет эта проблема, поэтому я не утруждаю себя загрузкой кода. Но, пожалуйста, рассматривайте любую работу, которая выполняется в вашем компьютере,
onBindViewHolder()
как потенциальное узкое место для плохой производительности RecyclerView.источник
В дополнение к подробному ответу @Galya я хочу заявить, что, хотя это может быть проблема с оптимизацией, также верно и то, что включение отладчика может сильно замедлить работу.
Если вы делаете все, чтобы оптимизировать свой,
RecyclerView
но он по-прежнему не работает гладко, попробуйте переключить свой вариант сборки наrelease
и проверьте, как он работает в среде, не связанной с разработкой (с отключенным отладчиком).Со мной случилось так, что мое приложение работало медленно в
debug
варианте сборки, но как только я переключился наrelease
вариант, оно работало плавно. Это не означает, что вам следует разрабатыватьrelease
вариант сборки, но полезно знать, что всякий раз, когда вы будете готовы отправить свое приложение, оно будет работать нормально.источник
Я говорил о
RecyclerView
спектакле России. Здесь представлены слайды на английском языке и записанное видео на русском языке .Он содержит набор приемов (некоторые из них уже описаны в ответе @Darya ).
Вот краткое изложение:
Если
Adapter
элементы имеют фиксированный размер, установите:recyclerView.setHasFixedSize(true);
Если объекты данных могут быть представлены как long (
hashCode()
например), тогда установите:adapter.hasStableIds(true);
и выполните:
// YourAdapter.java
@Override
public long getItemId(int position) {
return items.get(position).hashcode(); //id()
}
В этом случае
Item.id()
не будет работать, потому что он останется таким же, даже еслиItem
содержимое изменилось.PS В этом нет необходимости, если вы используете DiffUtil!
Используйте правильно масштабированное растровое изображение. Не изобретайте велосипед и не используйте библиотеки.
Подробнее как выбрать здесь .
Всегда используйте последнюю версию
RecyclerView
. Например, значительно улучшена производительность25.1.0
- prefetch.Больше информации здесь .
Используйте DiffUtill.
DiffUtil просто необходим .
Официальная документация .
Упростите макет вашего предмета!
Маленькая библиотека для обогащения TextViews - TextViewRichDrawable
Смотрите слайды для более подробного объяснения.
источник
Я не совсем уверен, что использование
setHasStableId
флага решит вашу проблему. Судя по предоставленной вами информации, проблема с производительностью может быть связана с проблемой памяти. Производительность вашего приложения с точки зрения пользовательского интерфейса и памяти весьма взаимосвязана.На прошлой неделе я обнаружил, что в моем приложении происходит утечка памяти. Я обнаружил это, потому что через 20 минут использования моего приложения я заметил, что пользовательский интерфейс работает очень медленно. Закрытие / открытие активности или прокрутка RecyclerView с кучей элементов было очень медленным. После мониторинга некоторых моих пользователей в производстве с помощью http://flowup.io/ я обнаружил следующее:
Время кадра было действительно очень высоким, а количество кадров в секунду действительно очень низким. Вы можете видеть, что для рендеринга некоторых кадров требуется около 2 секунд: S.
Пытаясь понять, что вызывает это плохое время кадра / fps, я обнаружил, что у меня проблема с памятью, как вы можете видеть здесь:
Даже когда среднее потребление памяти было близко к 15 МБ, приложение сбрасывало кадры.
Вот как я обнаружил проблему с пользовательским интерфейсом. У меня была утечка памяти в моем приложении, вызывающая множество событий сборщика мусора, и это вызывало плохую производительность пользовательского интерфейса, потому что виртуальной машине Android пришлось останавливать мое приложение, чтобы собирать память каждый отдельный кадр.
Глядя на код, у меня была утечка внутри настраиваемого представления, потому что я не отменял регистрацию слушателя из экземпляра Android Choreographer. После выхода фикса все стало нормально :)
Если ваше приложение теряет кадры из-за проблемы с памятью, вам следует проверить две распространенные ошибки:
Проверьте, выделяет ли ваше приложение объекты внутри метода, вызываемого несколько раз в секунду. Даже если это распределение может быть выполнено в другом месте, где ваше приложение становится медленным. Примером может служить создание новых экземпляров объекта внутри пользовательского метода представления onDraw на onBindViewHolder в держателе представления представления recycler. Проверьте, регистрирует ли ваше приложение экземпляр в Android SDK, но не выпускает его. Регистрация слушателя в событии шины также может быть возможной утечкой.
Отказ от ответственности: инструмент, который я использовал для мониторинга своего приложения, находится в стадии разработки. У меня есть доступ к этому инструменту, потому что я один из разработчиков :) Если вам нужен доступ к этому инструменту, мы скоро выпустим бета-версию! Вы можете присоединиться к нам на нашем веб-сайте: http://flowup.io/ .
Если вы хотите использовать разные инструменты, вы можете использовать: traveview, dmtracedump, systrace или монитор производительности Andorid, интегрированный в Android Studio. Но помните, что эти инструменты будут контролировать ваше подключенное устройство, а не остальные ваши пользовательские устройства или установки ОС Android.
источник
Также важно проверить родительский макет, в который вы помещаете Recyclerview. У меня были аналогичные проблемы с прокруткой, когда я тестировал recyclerView во nestedscrollview. Представление, которое прокручивается в другом представлении, при прокрутке может снижаться производительность при прокрутке
источник
В моем случае я обнаружил, что заметной причиной задержки является частая загрузка внутри
#onBindViewHolder()
метода с возможностью рисования . Я решил это, просто загрузив изображения как Bitmap один раз внутри ViewHolder и получив доступ к нему из упомянутого метода. Это все, что я сделал.источник
В моем RecyclerView я использую растровые изображения для фона моего item_layout.
Все, что сказал @Galya, правда (и я благодарю его за отличный ответ). Но у меня они не работали.
Вот что решило мою проблему:
BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 2; Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
Для получения дополнительной информации прочтите этот ответ .
источник
В моем случае у меня есть сложные дочерние элементы recyclerview. Это повлияло на время загрузки активности (~ 5 секунд для рендеринга активности)
Я загружаю адаптер с помощью postDelayed () -> это даст хороший результат для рендеринга активности. после активности, отображающей мою загрузку recyclerview плавно.
Попробуйте этот ответ,
recyclerView.postDelayed(new Runnable() { @Override public void run() { recyclerView.setAdapter(mAdapter); } },100);
источник
В комментариях я вижу, что вы уже реализуете
ViewHolder
шаблон, но я опубликую здесь пример адаптера, который использует этотRecyclerView.ViewHolder
шаблон, чтобы вы могли убедиться, что интегрируете его аналогичным образом, опять же, ваш конструктор может варьироваться в зависимости от ваших потребностей, здесь это пример:public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> { Context mContext; List<String> mNames; public RecyclerAdapter(Context context, List<String> names) { mContext = context; mNames = names; } @Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { View view = LayoutInflater.from(viewGroup.getContext()) .inflate(android.R.layout.simple_list_item_1, viewGroup, false); return new ViewHolder(view); } @Override public void onBindViewHolder(ViewHolder viewHolder, int position) { //Populate. if (mNames != null) { String name = mNames.get(position); viewHolder.name.setText(name); } } @Override public int getItemCount() { if (mNames != null) return mNames.size(); else return 0; } /** * Static Class that holds the RecyclerView views. */ static class ViewHolder extends RecyclerView.ViewHolder { TextView name; public ViewHolder(View itemView) { super(itemView); name = (TextView) itemView.findViewById(android.R.id.text1); } } }
Если у вас возникли проблемы с работой,
RecyclerView.ViewHolder
убедитесь, что у вас есть соответствующие зависимости, которые вы всегда можете проверить в Gradle.Надеюсь, это решит вашу проблему.
источник
Это помогло мне получить более плавную прокрутку:
переопределить onFailedToRecycleView (держатель ViewHolder) в адаптере
и остановить все текущие анимации (если есть). "animateview" .clearAnimation ();
не забудьте вернуть истину;
источник
Я решил это с помощью этой строки кода
recyclerView.setNestedScrollingEnabled(false);
источник
Добавляя к ответу @Galya, в привязке viewHolder я использовал метод Html.fromHtml (). очевидно, это влияет на производительность.
источник
Я решил эту проблему, используя только одну строку в библиотеке Пикассо
.поместиться()
Picasso.get().load(currentItem.getArtist_image()) .fit()//this wil auto get the size of image and reduce it .placeholder(R.drawable.ic_doctor) .into(holder.img_uploaderProfile, new Callback() { @Override public void onSuccess() { } @Override public void onError(Exception e) { Toast.makeText(context, "Something Happend Wrong Uploader Image", Toast.LENGTH_LONG).show(); } });
источник