Динамическое добавление представлений в RecyclerView только к текущему элементу

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

Я предполагаю, что это просто потому, что ViewHolder используется повторно, поэтому добавленные элементы снова отображаются с новым элементом при загрузке.

Как бы это решить?


person Orbit    schedule 31.12.2015    source источник
comment
пожалуйста, поделитесь кодом класса вашего адаптера   -  person Gaurav Singh    schedule 04.06.2016
comment
Просто чтобы прояснить, добавляете ли вы элементы динамически в recyclerView или добавляете новые представления и динамически заполняете их данными?   -  person Anurag Singh    schedule 22.02.2017
comment
@CQM есть ответ на мой комментарий выше?   -  person Anurag Singh    schedule 23.02.2017
comment
@AnuragSingh да, но я добавил код в onViewRecycled и использовал removeAllViews для держателя просмотра. и это работало нормально.   -  person CQM    schedule 23.02.2017
comment
Для всех, кто заинтересован, мой ответ на аналогичный вопрос должен быть полезен stackoverflow.com/questions/32332381/   -  person maciekjanusz    schedule 27.02.2017
comment
Пожалуйста, поделитесь своим кодом. Есть несколько способов добиться этого, например. через адаптер или RecyclerView.ItemDecoration.   -  person Vilen    schedule 28.02.2017


Ответы (8)


Это был мой старый вопрос. За него была назначена награда, отсюда всплеск популярности и множество новых и неактуальных ответов.

Как указано в моем комментарии к этот ответ и комментарий @CQM под моим первоначальным вопросом , ответ состоит в том, чтобы переопределить метод onViewRecycled() и выполнить там все необходимые операции. Этот метод вызывается при повторном использовании представления, и здесь можно выполнять любые операции по очистке.

Документацию по этому методу можно найти здесь.

В моем случае это был вопрос удаления невидимых TextView, прикрепленных к представлению. Сам текст был удален, но вид остался. Если в представлении накапливается много невидимых TextView, которые не используются должным образом при прокрутке, прокрутка начнет отставать.

person Orbit    schedule 28.02.2017

Вам нужно отслеживать, какие представления были добавлены на основе резервных данных. Я бы, вероятно, добавил все необходимые дополнительные представления в onBindViewHolder() и удалил все, что может присутствовать в onViewRecycled(). Затем, когда вы хотите, чтобы он отображался динамически, измените любую переменную, которую вы отслеживаете, должна ли она быть видимой, и вызовите notifyItemChanged().

person RussHWolf    schedule 01.01.2016
comment
Итак, я реализовал onViewRecycled и в настоящее время просматриваю все дочерние макеты в макете, в который я также добавляю новые представления, а затем проверяю через instanceof, являются ли они типом представления, которое мне нужно удалить, и удаляю их, если это так. Есть ли более простой способ удалить все типы представления из группы просмотра? Кроме того, я не совсем уверен, что вы имели в виду под своим последним предложением. В настоящее время я добавляю новые представления в onBindViewHolder, поскольку данные для каждого нового представления содержатся в моем списке данных. Я не могу просто переключать видимость представлений, потому что добавляю их динамически. - person Orbit; 01.01.2016
comment
Жаль, что нет кода - person Jevon; 28.11.2020

Основываясь на этом:

но те ранее добавленные представления все еще там, но теперь не на том элементе.

По сути, согласно документации RecyclerView, у вас есть каждый раз сбрасывать представления внутри метода onBindViewHolder(),

скажем, у вас есть метод, который устанавливает параметр просмотра, если это ваш профиль, поэтому код для него выглядит следующим образом:

if (list.get(position).getId()==PreferenceManager.getUserID())
{
  // do some view change here
setViewParam(true);
}else
{
  // reset the view change here
setViewParam(false);
}

Итак, что вы здесь делаете, так это даете переработанному ViewHolder возможность сбросить настройки. Оставляйте комментарии, если вам нужна помощь!

person MadScientist    schedule 27.02.2017

Вы можете использовать это! setItemViewCacheSize(int size) См. здесь RecyclerViewDocumentation.

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

person Ashwin Mothilal    schedule 28.02.2017

Прежде всего, не могли бы вы поделиться еще кодом, пожалуйста?

Во-вторых, зачем вам динамически добавлять новые виды на лету? Почему бы вам не использовать другой VIEWTYPE или просто не иметь эти представления уже на вашем макете и просто сделать их видимыми/невидимыми или видимыми/исчезнувшими? (Я думаю, что так будет эффективнее).

Позвольте мне напомнить вам кое-что о RecyclerView, да, когда пользователь прокручивает viewHolder, они повторно используются (мало их можно создать, даже больше, чем нужно для заполнения экрана). Поэтому, если случилось так, что вы добавили несколько представлений к «элементу A» и прокрутили пользователя до «элемента Z», этот viewHolder можно повторно использовать для этого «элемента Z», следовательно, отображаются ранее добавленные представления.

Как вы можете это решить?

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

Надеюсь, что это поможет вам.

person murielK    schedule 28.02.2017

Сохраняйте информацию по тегам для элементов с новым дочерним элементом каждый раз, когда выполняется операция добавления нового представления. (Например, в общих настройках) Тег: создать с позицией элемента onBindViewHolder.

...
SharedPreference  sharedPref = getSharedPreference("text" + position, context);
SharedPreference.Editor editor = sharedPref.edit();
editor.putString("view", "ImageView");
...

при загрузке адаптер получает это значение и устанавливает значение по умолчанию как ноль. Я не уверен в его эффективности, но я буду работать.

...
String viewType = sharedPref.getString("view", null);
//it will return ImageView 

если вы знаете, например, некоторые возможные типы представлений, всегда будут ImageView и TextView, поэтому с некоторым оператором if все будет в порядке.

if(viewType.equals("ImageVIew")){
item(position).addView(new ImageVIew(context));
}

Удачи

person Elias Fazel    schedule 27.02.2017
comment
Не делайте этого. Вы будете очень быстро получать доступ к общим настройкам и сохранять их при прокрутке. - person Orbit; 28.02.2017

В вашем классе адаптера вашего recyclerView в методе onBindViewHolder создайте еще один адаптер и выполните те же методы для вашего нового адаптера.

Иерархия будет,

mainRecyclerView -> item1(->childRecyclerView1), item2(->childRecyclerView2), item3(->childRecyclerView3)

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

person saDashiv sinha    schedule 27.02.2017

Вы должны взять любой пустой макет, такой как Linearlayout в макете вашего дочернего элемента XML , а затем добавить представления в этот LinearLayout вашего конкретного элемента таким образом, когда вы прокручиваете Список всех ваших дочерних представлений, которые вы добавили в LinearLayout, также прокручивайте этот элемент.

person Chetan Joshi    schedule 28.02.2017