У меня есть RecyclerView
работающий с FirestoreRecyclerAdapter
в моем приложении. ViewHolders
реализуют расширяемое меню "быстрых действий", которое расширяется при нажатии на элемент, и идеальным поведением является расширение только одного элемента за раз. Развертывание одного элемента должно свернуть все остальные элементы в адаптере.
Когда на экране достаточно элементов, чтобы вместить их все, это прекрасно работает. Но если список расширяется за пределы экрана, когда я пытаюсь перебрать элементы адаптера, я получаю NullPointerException
когда отсоединенные или неэкранные ViewHolders пытаются вызвать getParent()
.
Как я могу перебрать все ViewHolders в адаптере или, наоборот, перебрать только видимые?
Соответствующий код адаптера:
@Override
protected void onBindViewHolder(@NonNull BrewViewHolder holder,
int position, @NonNull Brew brew) {
// Bind Views
holder.bind(brew);
// Set expander ClickListener
holder.card.setOnClickListener(v -> {
for (int i = 0; i < mAdapter.getItemCount(); i++) {
if (i != position) {
// ERROR THROWN HERE
BrewViewHolder vh = ((BrewViewHolder) recyclerView.getChildViewHolder(recyclerView.getChildAt(i)));
vh.expanded = false;
}
}
holder.expanded = !holder.expanded;
notifyItemChanged(position);
});
}
Соответствующий код ViewHolder:
public class BrewViewHolder extends RecyclerView.ViewHolder {
// Expanded state
public boolean expanded = false;
...
public BrewViewHolder(@NonNull final View itemView) {
super(itemView);
...
// Set visibility of quick actions based on expanded state
quickActions.setVisibility(expanded ? View.VISIBLE : View.GONE);
}
}
Заранее спасибо!
РЕДАКТИРОВАТЬ: Как отметил Гилберто, это очень дорого. Пошел с версией решения Андрея:
private int expandedItemIndex = -1;
@Override
protected void onBindViewHolder(@NonNull BrewViewHolder holder, int position,
@NonNull Brew brew) {
// Bind Views
holder.bind(brew);
// Set expander ClickListener
holder.card.setOnClickListener(v -> {
if (position == expandedItemIndex) {
holder.expanded = false;
expandedItemIndex = -1;
} else {
holder.expanded = true;
if (expandedItemIndex != -1) {
BrewViewHolder otherHolder = ((BrewViewHolder) recyclerView.getChildViewHolder(
recyclerView.getChildAt(expandedItemIndex)));
otherHolder.expanded = false;
}
expandedItemIndex = position;
}
notifyItemRangeChanged(0, recyclerView.getChildCount());
});
}
Вы не должны повторять все представления. Вам нужно просто сохранить индекс расширенного представления и вызвать notifyDataSetChanged
или notifyItemChanged
.
private int expandedItemIndex = -1;
@Override
protected void onBindViewHolder(@NonNull BrewViewHolder holder,
final int position, @NonNull Brew brew) {
// Bind Views
holder.bind(brew);
// Set expander ClickListener
holder.card.setOnClickListener(v -> {
if (position == expandedItemIndex) {
notifyItemChanged(position);
expandedItemIndex = -1;
} else {
if (expandedItemIndex != -1) {
notifyItemChanged(expandedItemIndex);
}
expandedItemIndex = position;
notifyItemChanged(position);
}
});
if (position == expandedItemIndex) {
// Expand
holder.quickActions.setVisibility(View.VISIBLE);
} else {
// Collapse
holder.quickActions.setVisibility(View.GONE);
}
}
If (vh! = Null) {vh.expanded(false);}
Или же
Используй попробуй, лови)))
mAdapter.getItemCount()
возвращает общее количество элементов, раздутых или нет.
Решено путем итерации по i < recyclerView.getChildCount()
и проверки очистки VH в onViewRecycled(ViewHolder holder)
:
@Override
protected void onBindViewHolder(@NonNull BrewViewHolder holder, int position, @NonNull Brew brew) {
// Bind Views
holder.bind(brew);
// Set expander ClickListener
holder.card.setOnClickListener(v -> {
for (int i = 0; i < recyclerView.getChildCount(); i++) {
if (i != position) {
BrewViewHolder otherHolder = ((BrewViewHolder) recyclerView.getChildViewHolder(recyclerView.getChildAt(i)));
otherHolder.expanded = false;
}
}
holder.expanded = !holder.expanded;
notifyItemRangeChanged(0, recyclerView.getChildCount());
});
}
@Override
public void onViewRecycled(@NonNull BrewViewHolder holder) {
super.onViewRecycled(holder);
holder.expanded = false;
}
notifyItemChanged(positionOfItemOffScreen)
? Кроме того, вы уведомляете, прежде чем элемент действительно изменился ...notifyItemYYY
методы обновления представления не сразу, поэтому все правильно.