Когда я пытаюсь читать из SQLite и использую adapter.NotifyDataSetChanged. Я не вижу никаких изменений в моем recyclerView. Основная идея заключается в поиске в SQLite, где имя содержит значение из textView. Затем снова заполнить мой адаптер.
Я создаю экземпляр
private List<InventoryPreviewClass> mItems;
private RecyclerView mRecyclerView;
adpInventoryPreview adapter;
Затем внутри метода onCreate()
mItems = db.Query<InventoryPreviewClass>("select * from InventoryPreviewClass where CategoryID = " + CategoryID + ""); //Here i am reading from sqlite
mRecyclerView.HasFixedSize = true;
var layout = new GridLayoutManager(this, InventoryRowsPerLine, GridLayoutManager.Vertical, false);
mRecyclerView.SetLayoutManager(layout);
adapter = new adpInventoryPreview(mItems);
mRecyclerView.SetAdapter(adapter);
Пока код работает. Мой recyclerView заполнен моими элементами из sqlite.
Вот метод, когда пользователь вводит что-то в TextView
private void EtSearchAlwaysOn_TextChanged(object sender, Android.Text.TextChangedEventArgs e)
{
mItems = db.Query<InventoryPreviewClass>("select * from InventoryPreviewClass where InventoryItemName like '%" + etSearchAlwaysOn.Text.ToUpper() + "%'");
adapter.NotifyDataSetChanged();
}
Когда я что-то печатаю, в моем recyclerView ничего не меняется. Почему это происходит?
Единственный способ, которым я обнаружил, что это работает, - это сбросить мои элементы внутри моего адаптера, например:
mItems = db.Query<InventoryPreviewClass>("select * from InventoryPreviewClass where InventoryItemName like '%" + etSearchAlwaysOn.Text.ToUpper() + "%'");
adapter = new adpInventoryPreview(mItems);
mRecyclerView.SetAdapter(adapter);
Почему это происходит? Я не думаю, что второй метод является правильным.
Это происходит потому, что вы назначаете новый объект mItems
В первый раз, когда вы создаете список mItems
, вы передаете его своему адаптеру, когда в следующий раз, когда вы получаете ответ от своей базы данных SQLite, создается новый экземпляр списка, поскольку вы назначаете его объекту.
Что вам нужно сделать, это
adapter.updateItems(newItems)
items.clear()
, а затем добавить новые элементы, которые вы передали с помощью items.addAll(newItems)
notifyDataSetChanged()
внутри самого адаптера, и он заработает.В вашем адаптере это будет выглядеть так
public void updateItems(final List<InventoryPreviewClass> newItems) {
items.clear();
items.addAll(newItems);
notifyDataSetChanged();
}
и тогда вы можете назвать это так
updatedList = db.Query<InventoryPreviewClass>("select * from InventoryPreviewClass where CategoryID = " + CategoryID + "");
adapter.updateItems(updatedList);
Я думаю, вы упустили момент, что это типичный случай передачи по значению, а не по ссылке.
Да, это кажется лучшим подходом. Спасибо @droidchef
Уведомить адаптер после установки адаптера.
mItems = db.Query<InventoryPreviewClass>("select * from InventoryPreviewClass where InventoryItemName like '%" + etSearchAlwaysOn.Text.ToUpper() + "%'");
adapter = new adpInventoryPreview(mItems);
mRecyclerView.SetAdapter(adapter);
adapter.notifyDataSetChanged();
Ваш ответ работает! Нужно ли утилизировать адаптер перед созданием нового, чтобы избежать утечек памяти?
Это неправильный способ сделать это, потому что вы будете уничтожать адаптер каждый раз. Вы потеряете полную цель иметь кеш представления вашего держателя представления в recyclerview, если вы будете уничтожать адаптер каждый раз.
Каков наилучший подход для достижения этого @droidchef
Проверьте мой ответ, я упомянул подход. Идея не в том, чтобы создавать адаптер снова и снова. Это повлияет на производительность, поскольку вашему адаптеру придется заново создавать представления для вашего представления ресайклера.
вы должны установить элементы в своем адаптере, создайте такой сеттер:
private List<InventoryPreviewClass> mItems; . . . public void setItems(List<InventoryPreviewClass> items) { mItems=items; }
а затем обновите свой метод поиска следующим образом
private void EtSearchAlwaysOn_TextChanged(object sender,
Android.Text.TextChangedEventArgs e)
{
mItems = db.Query<InventoryPreviewClass>("select * from InventoryPreviewClass
where InventoryItemName like '%" + etSearchAlwaysOn.Text.ToUpper() + "%'");
adapter.setItems(mItems);
adapter.NotifyDataSetChanged();
}
Это проблема byRef. Вы передаете указатель памяти 1 на свой адаптер и говорите ему следить за изменениями.
Затем вы загружаете список и перенаправляете указатель памяти на точку 2. Затем вы сообщаете адаптеру, что отслеживаемая память по указателю 1 изменилась.
У вас есть два варианта.
Спасибо, Сэм. Я предпочитаю метод 2. Мне он кажется более гибким. Нужно ли утилизировать адаптер перед этим? Сообщить каким-то образом, что предыдущей точки в памяти больше не существует? Чтобы избежать утечек памяти?
хороший вопрос. Я никогда не беспокоюсь о старом списке, поскольку, когда вы перенаправляете его в новое место, сборщик мусора должен выполнять свою работу.
@ Сэм, я думаю, он говорит об утилизации адаптера. Dmo вы не должны утилизировать, адаптер вообще в этом случае, потому что тогда вы потеряете на кеше viewholder, который создает ваш адаптер.
ой извините, не уловил. Нет, вам не нужно утилизировать адаптер. Спасибо дроидшеф. Я думал, ты спрашиваешь о старом списке.
1-й: передайте
mItems
мимоref
2-й: вместо того, чтобы пытаться сбросить весь адаптер, реализуйте подклассFilter
(developer.android.com/reference/android/widget/Фильтр) и используйте его в своемRecyclerView.Adapter
, чтобы позволить отфильтрованному наборуmitems
динамически обновлять вашRecyclerView
, чтобы предотвратить «мигание» пользовательского интерфейса. " (постоянная очистка всего представления при каждом нажатии клавиши пользователем, которое обновляет запрос sql). 3-й: Просмотрите мой ответ здесь о том, как делать выборочные уведомления: stackoverflow.com/a/50692948/4984832