RecyclerView findViewHolder null для элементов вне экрана

[Впервые спрашиваю и впервые кодер Android, поэтому я прошу прощения, если я делаю что-то не так]

Я пытаюсь изменить значение в представлении предметов на моем переработчике. Однако, если этот элемент зрения находится за пределами экрана, findViewBy[layout,adapter or id] будет иметь значение null.

public void setUserActive(UserListAdapter adapter, RecyclerView recyclerView, int position, View v) {recyclerView.findViewHolderForItemId(adapter.getItemId(position));
    TextView txtActive = (TextView) vh.itemView.findViewById(R.id.user_isActive);
    txtActive.setText("Active");
    UserListAdapter.ViewHolder inactivevh = (UserListAdapter.ViewHolder) recyclerView.findViewHolderForItemId(adapter.getItemId(adapter.getActiveUserPosition()));
    TextView txtInActive = (TextView) inactivevh.itemView.findViewById(R.id.user_isActive);
    txtInActive.setText("");
    adapter.setActiveUserPosition(position);
}

Я пробовал несколько разных способов, используя

  UserListAdapter.ViewHolder vh = (UserListAdapter.ViewHolder) recyclerView.findViewHolderForLayoutPosition(position);

или же

UserListAdapter.ViewHolder inactivevh = (UserListAdapter.ViewHolder) recyclerView.findViewHolderForAdapterPosition(position)

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

Я установил для адаптера значение hasStableIDs = true, но notifydatasetchanged тоже не помогает.

1 ответ

Решение

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

Значение: реализовать ваш адаптер таким образом, чтобы onBindViewHolder умеет получать новейшие данные. Похоже, вы все настроены и можете использовать это:

public void setUserActive(UserListAdapter adapter, RecyclerView recyclerView, int position) {
    // Retrieve the old active user ID so we can mark him deactivated.
    final int oldUserActivePosition = adapter.getActiveUserPosition();

    // Setup the adapter with current data.
    adapter.setActiveUserPosition(position);

    // Do NOT use stable IDs and this method. It just doesn't work as expected.
    // recyclerView.findViewHolderForItemId(adapter.getItemId(position));

    // Activate new user.
    UserListAdapter.ViewHolder holder = (UserListAdapter.ViewHolder) recyclerView.findViewHolderForAdapterPosition(position);
    if (holder == null) {
        // The item is off screen, no need to update it yet.
    } else {
        // Update the on screen item. The logic is already implemented in the adapter.
        adapter.onBindViewHolder(holder, position);
    }

    // Deactivate previous user.
    holder = (UserListAdapter.ViewHolder) recyclerView.findViewHolderForAdapterPosition(oldUserActivePosition);
    if (holder != null) {
        adapter.onBindViewHolder(holder, oldUserActivePosition);
    }
}

Упростите блок if-else в вашем коде, конечно.

РЕДАКТИРОВАТЬ:

Я только что понял, насколько излишне сложным является оригинальное решение. Пока адаптер может реагировать на все события, которые вам нужны в onBindViewHolder и привязка действительно проста, этого будет достаточно:

public void setActiveUserPosition(int position) {
    if (mActiveUserPosition != position) {
        int oldPosition = mActiveUserPosition;
        mActiveUserPosition = position;
        notifyItemChanged(oldPosition);
        notifyItemChanged(position);
    }
}

Все, что вам нужно позвонить сейчас, это adapter.setActiveUserPosition(position); который оказывает setUserActive метод устарел.

Другие вопросы по тегам