RecyclerView - получить все существующие виды/зрители
Я хочу обновить RecyclerView
, пока он отображает данные, в моем случае я показываю изображения с метками или без них.
По умолчанию я устанавливаю видимость ярлыка, когда создаю держатель вида и прекрасно, но я хочу, чтобы пользователь менял видимость ярлыков через меню, пока отображается RecyclerView
, поэтому я хочу вручную обновить видимость для всех существующих представлений в RecyclerView
.
Можно ли каким-то образом получить все существующие Views
? Мне нужны все, а не только видимые, я не хочу, чтобы более поздняя утилизация View
не обновлялась...
Ответы
Ответ 1
В классе адаптера:
Boolean isVisible = false;
public CustomAdapter(boolean isVisible) {
this.isVisible= isVisible;
}
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
...
if (isVisible){
//setVisibility(View.VISIBLE)
}else{
//setVisibility(View.INVISIBLE
}
}
public void updateVisibility(boolean newValue){
isVisible= newValue;
}
В Activity
где вы хотите обновить значение, в котором создается экземпляр адаптера:
adapter.updateVisibility(false);
adapter.notifydataSetChanged();
Ответ 2
Сначала вам нужно получить все показанные индексы представлений, а затем вам нужно просмотреть каждый из них и использовать viewHolder каждого представления:
final int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();
final int lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition();
for (int i = firstVisibleItemPosition; i <= lastVisibleItemPosition; ++i) {
ViewHolder holder = (ViewHolder) mRecyclerView.findViewHolderForAdapterPosition(i);
...
}
РЕДАКТИРОВАТЬ: кажется, он не всегда возвращает все ViewHolders, которые вы, возможно, захотите обработать. Это выглядит как более стабильное решение:
for (int childCount = recyclerView.getChildCount(), i = 0; i < childCount; ++i) {
final ViewHolder holder = recyclerView.getChildViewHolder(recyclerView.getChildAt(i));
...
}
Примечание: в некоторых случаях вам может потребоваться установить достаточно большое количество кэшируемых представлений, чтобы вы всегда возвращали одни и те же представления вместо новых. Для этого вы можете использовать что-то вроде этого:
fun RecyclerView.setMaxViewPoolSize(maxViewTypeId: Int, maxPoolSize: Int) {
for (i in 0..maxViewTypeId)
recycledViewPool.setMaxRecycledViews(i, maxPoolSize)
}
Пример использования:
recyclerView.setMaxViewPoolSize(PhoneCallRecordingListActivityViewModel.ListItem.MAX_TYPE_ITEM, Int.MAX_VALUE)
Ответ 3
- Обновление всего размера списка адаптеров с помощью
notifyDataSetChanged()
не удобно. - Получить все текущие дочерние элементы RecyclerView (т.е. Все видимые элементы) недостаточно: есть привязанные держатели вида, которые не видны: после загрузки при запуске обычно 2 или 3 элемента привязаны к последней видимой позиции (в зависимости от вашего LayoutManager),
Я просто экспериментировал, просматривая логи, которые я помещал, что RV связывает больше предметов, которые не видны, на самом деле я не нашел способа восстановить их. Я закончил кешировать в Set
или List
те ViewHolders в onBindViewHolder()
и разархивировать их в onViewRecycled()
:
private Set<AbstractViewHolder> mBoundViewHolders = new HashSet<>();
private int mVisibility = View.VISIBILE;
// So you can have access from the Activity/Fragment
public Set<AbstractViewHolder> getAllBoundViewHolders() {
return Collections.unmodifiableSet(mBoundViewHolders);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List payloads) {
// Binding code
...
holder.view.setVisibility(mVisibility);
// Caching VH
mBoundViewHolders.add((AbstractViewHolder) holder);
}
@Override
public void onViewRecycled(RecyclerView.ViewHolder holder) {
// Uncaching VH
mBoundViewHolders.remove(holder);
}
// Your method becomes
public void updateVisibility(int visibility) {
mVisibility = visibility;
for (AbstractViewHolder holder : mBoundViewHolders) {
holder.updateVisibility(visibility);
}
}
Реализуйте свой интерфейс или абстрактный ViewHolder, чтобы вы могли вызывать свой метод. Таким образом, у вас есть доступ к вашему содержимому ViewHolder.
Это то, что я делаю в моем FlexibleAdapter, чтобы отменить notifyItemRangeChanged()
элементов без вызова notifyItemRangeChanged()
. И если у пользователя есть анимация, все выполняются, плюс фон также меняется, когда я toggleActivation()
свой метод toggleActivation()
.
Если вы знаете лучший способ восстановить всю связанную VH, дайте мне знать.
Ответ 4
Все существующие представления - это те, которые видны, плюс некоторые представления, кэшированные RecyclerView
. Вы можете получить доступ ко всем видимым View
следующим образом:
View child;
for (int i = 0; i < mRecycler.getChildCount(); i++) {
child = mRecycler.getChildAt(i);
//In case you need to access ViewHolder:
mRecycler.getChildViewHolder(child);
}
Даже если вы получаете доступ к кешированным View
, они еще не привязаны к данным, поэтому вы не будете знать, должна ли метка быть видимой или нет. Просто установите видимость метки оставшегося Views
в onBindViewHolder
.
Остальная часть View
просто не существует, поэтому вы ничего не можете изменить о них, даже если для них одинаковое состояние видимости ярлыков.
Ответ 5
Все приведенные выше решения дают только видимые держатели, но на самом деле есть еще 2-3 держателя, которые связаны, но не видны. Чтобы получить их,
самый простой и точный способ, который я могу придумать, чтобы получить все владельцы:
int lastBoundHolderPosition = -1;
int lastDetachedHolderPosition = -1;
public void onBindViewHolder(final MyViewHolder holder, int position) {
lastBoundHolderPosition = position;
//Your implementation
}
public void onViewDetachedFromWindow(MyViewHolder holder){
lastDetachedHolderPosition = holder.holder.getAdapterPosition();
}
Если вы хотите, чтобы все адаптеры были видимыми (плюс Связано, но не видно):
if(lastDetachedHolderPosition == -1){
for(pos = 0; pos <= lastDetachedHolderPosition; pos++){
ViewHolder holder = (ViewHolder) mRecyclerView.findViewHolderForAdapterPosition(pos);
}
}else{
if(lastBoundHolderPosition > lastDetachedHolderPosition){ //Scroll down
//get all the bound holders
for(int pos = lastDetachedHolderPosition + 1; pos <= lastBoundHolderPosition; pos++){
ViewHolder holder = (ViewHolder) mRecyclerView.findViewHolderForAdapterPosition(pos);
}
}else{ //Scroll up
//get all the bound holders
for(int pos = lastBoundHolderPosition ; pos <= lastDetachedHolderPosition-1; pos++){
ViewHolder holder = (ViewHolder) mRecyclerView.findViewHolderForAdapterPosition(pos);
}
}
}