notifyDataSetChanged() не обновляется правильно из-за проблем с привязкой ArrayList к адаптеру

Я прочитал следующее:

ArrayList не обновляется после использования .clear()
notifydataSetChanged работает, но показывает только 1 результат в списке Android
notifyDataSetChanged() не влияет на мой адаптерView
Android: notifyDataSetChanged(); не работает
notifydatasetchanged() не работает после onbackpressed()
Android notifyDataSetChanged не работает
Android notifyDataSetChanged() не работает

И все еще не могу заставить мой адаптер обновляться из нового набора данных.

Мой код в режиме отладки выглядит следующим образом:

public class PetInformationActivity extends AppCompatActivity
implements ConfirmDialogFragment.ConfirmDialogListener, MedicalInformationFragment.OnFragmentInteractionListener{

private static List<Assignment> listAssignments = new ArrayList<>();
private static AssignmentsAdapter mAdapter;
private static PetInformationViewModel sPetInformationViewModel;
.
.
.
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
.
.
.
/**
* get the providers' IDs assigned to the pet: fillAssignmentsArray()
*/
listAssignments = sPetInformationViewModel.getAssignedProviders(petId);

На данный момент выполняется отладка:
mAdapter = null
listAssignments = (ArrayList@5411) size = 0

Log.d(TAG, "listAssignments has " + listAssignments.size() + " Assignment objects in it");  
Log.d(TAG, "listAssignments is: " + listAssignments);   

На данный момент выполняется отладка:
mAdapter = null
listAssignments = (ArrayList@5461) size = 1
0 = (Assignment@5496)"Assignment{mType='Ветеринар' , mProviderName='красный ветеринар'}"

mAdapter = new AssignmentsAdapter(this, listAssignments);   

На данный момент выполняется отладка:
mAdapter = (AssignmentsAdapter@5499)
listAssignments = (ArrayList@5461) size = 1
0 = (Assignment@5496)"Assignment{mType ='Ветеринар', mProviderName='красный ветеринар'}"

mRecyclerView.setAdapter(mAdapter);
.
.
.
}

public void onResume(){
super.onResume();
Log.d(TAG, "Entered: onResume");
listAssignments.clear();
Log.d(TAG, "listAssignments is: " + listAssignments);

На данный момент выполняется отладка:
mAdapter = (AssignmentsAdapter@5499)
listAssignments = (ArrayList@5461) size = 0**

listAssignments = sPetInformationViewModel.getAssignedProviders(petId);
Log.d(TAG, "listAssignments is: " + listAssignments);

На данный момент выполняется отладка:
mAdapter = (AssignmentsAdapter@5499)
listAssignments = (ArrayList@5594) size = 1
0 = (Assignment@5607)"Assignment{mType ='Ветеринар', mProviderName='красный ветеринар'}"

mAdapter.notifyDataSetChanged();
}  

В этот момент экран пуст, хотя должен показывать текст, показанный в Assignment@5607. Похоже, что notififyDataSetChanged() уведомил mAdapter об изменении текста, показанного в onCreate (ArrayList@5461), на очистку в onResume (ArrayList@5461), но не о новом вызове данных в onResume (ArrayList@5594).

Похоже, это указывает на то, что когда mAdapter был инициализирован для listAssignments, он был настроен на использование ArrayList по адресу @5461. Когда в onResume был сделан вызов новых данных, при а) возврате после использования кнопки «Назад» и (б) при первом запуске этого действия новый ArrayList был создан по другому адресу.

Поэтому кажется, что я неправильно включил свой ArrayList, поскольку все обновления listAssignments приведут к созданию совершенно нового объекта ArrayList, который никогда не будет использоваться для обновления mAdapter, который всегда будет обновляться до исходного ArrayList по адресу @5461.

Я искал информацию о том, как правильно привязать адаптер к ArrayList, но я не нашел ничего, что намекало бы на большее, чем я сделал. Я предполагаю, что целью notifyDataSetChanged() является обновление с использованием существующего адаптера, чтобы существующий адаптер не нужно было уничтожать, а новый создавался при каждом изменении набора данных, как показано здесь:

notifyDataSetChanged() не может обновить адаптер onClick


person Jeff    schedule 18.01.2019    source источник


Ответы (2)


Ссылка, которую вы передаете в ArrayAdapter, отличается от указанной здесь:

listAssignments = sPetInformationViewModel.getAssignedProviders(petId);

Что касается ArrayAdapter, listAssignments, на который он ссылается, был очищен OnResume, и к нему никогда ничего не добавлялось. Он ничего не знает о новой ссылке listAssignments.

Попробуйте сделать это:

public void onResume()
{
    super.onResume();
    Log.d(TAG, "Entered: onResume");
    listAssignments.clear();
    Log.d(TAG, "listAssignments is: " + listAssignments);
    List<Assignment> newList = sPetInformationViewModel.getAssignedProviders(petId);
    listAssignments.addAll(newList);
    mAdapter.notifyDataSetChanged();
}
person pnavk    schedule 18.01.2019
comment
ХОРОШО. Это работает при отображении правильного значения при запуске Activity. Однако предполагается, что notifyDataSetChanged() в onResume (как вы предложили) повторно отображает новое значение при возврате из другого действия. Поскольку это новое значение получено из базы данных SQLite, значение из этого действия не передается, значение получается в onResume, как показано в вашем предложении. Однако это не работает, отображается старое значение, а новый адрес ArrayList новый, а не @5461. Поэтому адаптер никогда не обновляется! Нужно ли каждый раз заменять адаптер на новый? Другие сообщения говорят, что нет. - person Jeff; 20.01.2019
comment
@Jeff - проверили ли вы, что элементы, возвращаемые вашей ViewModel в OnResume, отличаются, когда вы возвращаетесь из другого действия? Что бы это ни стоило, если вы хотите обновить свой список, когда вы возвращаетесь из другого действия, лучше использовать startActivityForResult и onActivityResult, потому что OnResume работает для множества других сценариев, включая запуск действия, ротацию устройств и т. д. См. этот ответ: stackoverflow.com/a/37227482/2754727 - person pnavk; 20.01.2019
comment
Да, адрес ArrayList — 5461 при запуске Activity и 5767 при возврате из вызываемого Activity с использованием onBackPressed() из вызываемого Activity. Я буду изучать эти методы, однако вызываемая активность просто сохраняет значение в таблице БД, к которой затем осуществляется доступ, как показано в вашем onResume(), значение не передается обратно в вызывающую активность. Есть ли что-то кроме List‹String›, которое должно использовать один и тот же базовый адрес на протяжении всей жизни вызывающего действия (PetInformationActivity), или есть способ установить его базовый адрес так, чтобы он никогда не менялся? - person Jeff; 20.01.2019
comment
Я заставил его работать, добавив: mAdapter = new AssignmentsAdapter(this, listAssignments); mRecyclerView.setAdapter(mAdapter); к вашему предложению onResume. Однако этот обходной путь может быть очень неэффективным и не устраняет тот факт, что notifyDataSetChanged() не работает, поскольку адрес ArrayList изменяется при каждом вызове, и поэтому последний набор данных никогда не используется для обновления отображения. - person Jeff; 20.01.2019
comment
@Jeff - вы можете попробовать открыть метод внутри вашего AssignmentsAdapter, который изменяет ссылку на список, которую он содержит внутри. Это может работать без необходимости инициализировать новый адаптер - person pnavk; 24.01.2019

Хорошо, я иду на риск и говорю, что notifyDataSetChanged() не работает для ArrayList, потому что при каждой загрузке его адрес меняется, см. мой исходный пост и комментарии к pnavk, чтобы увидеть подробности. Насколько я понимаю, Java не предоставляет средств для фиксирования адреса ArrayList, как C или C++, с помощью указателя, поэтому адаптер устанавливается в ячейку памяти исходного объекта ArrayList и поэтому указывает на одни и те же данные при каждом notifyDataSetChanged(), даже если Java использовала новую область памяти для этого объекта ArrayList или создала совершенно новый объект ArrayList с тем же именем.

Поэтому мой ответ на обновление RecyclerView из нового набора данных в ArrayList состоит в том, чтобы сбросить адаптер в onResume как новый, а затем переназначить этот адаптер вашему RecyclerView, как я упоминал в своем ответе на pnavk:

mAdapter = new AssignmentsAdapter(this, listAssignments); 
mRecyclerView.setAdapter(mAdapter);
person Jeff    schedule 20.01.2019