Как я могу обновить одну строку в ListView?

У меня есть ListView который отображает новости. Они содержат изображение, заголовок и текст. Изображение загружается в отдельный stream (с очередью и всеми), и когда изображение загружается, я теперь вызываю notifyDataSetChanged() в адаптере списка, чтобы обновить изображение. Это работает, но getView() получает вызов слишком часто, так как notifyDataSetChanged() вызывает getView() для всех видимых элементов. Я хочу обновить только один элемент в списке. Как мне это сделать?

Проблемы с моим текущим подходом:

  1. Прокрутка медленная
  2. У меня есть анимация с постепенным увеличением изображения, которое происходит каждый раз, когда загружается одно новое изображение в списке.

Я нашел ответ, благодаря вашей информации Мишель. Вы действительно можете получить правильный вид, используя View#getChildAt(int index) . Уловка состоит в том, что он начинает отсчет с первого видимого элемента. Фактически, вы можете получать только видимые предметы. Вы решаете это с помощью ListView#getFirstVisiblePosition() .

Пример:

 private void updateView(int index){ View v = yourListView.getChildAt(index - yourListView.getFirstVisiblePosition()); if(v == null) return; TextView someText = (TextView) v.findViewById(R.id.sometextview); someText.setText("Hi! I updated you manually!"); } 

Этот вопрос задан в Google I / O 2010, вы можете посмотреть его здесь:

Мир ListView, время 52:30

В принципе, объясняет Ромайн Гай, чтобы вызвать getChildAt(int) в ListView чтобы получить представление и (я думаю) вызов getFirstVisiblePosition() чтобы узнать корреляцию между позицией и индексом.

Ромен также указывает на проект под названием « Полки» в качестве примера, я думаю, что он может означать метод ShelvesActivity.updateBookCovers() , но я не могу найти вызов getFirstVisiblePosition() .

УДИВИТЕЛЬНЫЕ ОБНОВЛЕНИЯ:

RecyclerView исправит это в ближайшем будущем. Как указано в http://www.grokkingandroid.com/first-glance-androids-recyclerview/ , вы сможете вызвать методы, чтобы точно указать изменение, например:

 void notifyItemInserted(int position) void notifyItemRemoved(int position) void notifyItemChanged(int position) 

Кроме того, каждый захочет использовать новые представления на основе RecyclerView, потому что они будут вознаграждены красивой анимацией! Будущее выглядит потрясающе! 🙂

Вот как я это сделал:

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

 @Override public View getView(int position, View convertView, ViewGroup parent) { View view = super.getView(position, convertView, parent); view.setTag(getItemId(position)); return view; } 

Для обновления проверьте каждый элемент списка, если вид с данным идентификатором есть, он отображается, поэтому мы выполняем обновление.

 private void update(long id) { int c = list.getChildCount(); for (int i = 0; i < c; i++) { View view = list.getChildAt(i); if ((Long)view.getTag() == id) { // update view } } } 

Это на самом деле проще, чем другие методы и лучше, когда вы имеете дело с идентификаторами не позиций! Также вы должны вызвать обновление для элементов, которые становятся видимыми.

Ответы ясны и правильны, я добавлю идею для случая CursorAdapter .

Если вы подclassифицируете CursorAdapter (или ResourceCursorAdapter или SimpleCursorAdapter ), то вы можете либо реализовать ViewBinder либо переопределить bindView() и newView() , они не получают текущий индекс элемента списка в аргументах. Поэтому, когда некоторые данные поступают, и вы хотите обновлять соответствующие элементы видимого списка, как вы знаете их индексы?

Мое обходное решение заключалось в следующем:

  • сохранить список всех созданных представлений элементов списка, добавить элементы в этот список из newView()
  • когда поступают данные, повторяйте их и посмотрите, какой из них нужно обновить – лучше, чем делать notifyDatasetChanged() и notifyDatasetChanged() их все

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

 int wantedPosition = 25; // Whatever position you're looking for int firstPosition = linearLayoutManager.findFirstVisibleItemPosition(); // This is the same as child #0 int wantedChild = wantedPosition - firstPosition; if (wantedChild < 0 || wantedChild >= linearLayoutManager.getChildCount()) { Log.w(TAG, "Unable to get view for desired position, because it's not being displayed on screen."); return; } View wantedView = linearLayoutManager.getChildAt(wantedChild); mlayoutOver =(LinearLayout)wantedView.findViewById(R.id.layout_over); mlayoutPopup = (LinearLayout)wantedView.findViewById(R.id.layout_popup); mlayoutOver.setVisibility(View.INVISIBLE); mlayoutPopup.setVisibility(View.VISIBLE); 

Для RecycleView используйте этот код.

сначала получить class модели как глобальный, как этот объект classа модели

 SampleModel golbalmodel=new SchedulerModel(); 

и инициализировать его глобальным

получить текущую строку представления модели, инициализируя ее для глобальной модели

 SampleModel data = (SchedulerModel) sampleList.get(position); golbalmodel=data; 

установите измененное значение в метод глобального объекта модели, который будет установлен, и добавьте функцию notifyDataSetChanged для меня

 golbalmodel.setStartandenddate(changedate); notifyDataSetChanged(); 

Вот связанный с этим вопрос с хорошими ответами.

Я использовал код, который предоставил Erik, отлично работает, но у меня есть сложный пользовательский адаптер для моего списка, и я столкнулся с двойной реализацией кода, который обновляет интерфейс. Я попытался получить новый вид из моего метода getView адаптера (arraylist, который содержит данные списка, уже обновлен / изменен):

 View cell = lvOptim.getChildAt(index - lvOptim.getFirstVisiblePosition()); if(cell!=null){ cell = adapter.getView(index, cell, lvOptim); //public View getView(final int position, View convertView, ViewGroup parent) cell.startAnimation(animationLeftIn()); } 

Он работает хорошо, но я не знаю, является ли это хорошей практикой. Поэтому мне не нужно реализовывать код, который обновляет элемент списка два раза.

Мое решение: если оно правильно *, обновите данные и видимые элементы без повторного рисования всего списка. Else notifyDataSetChanged.

Правильный – oldData size == новый размер данных и старые идентификаторы данных и их порядок == новые идентификаторы данных и порядок

Как:

 /** * A View can only be used (visible) once. This class creates a map from int (position) to view, where the mapping * is one-to-one and on. * */ private static class UniqueValueSparseArray extends SparseArray { private final HashMap m_valueToKey = new HashMap(); @Override public void put(int key, View value) { final Integer previousKey = m_valueToKey.put(value,key); if(null != previousKey) { remove(previousKey);//re-mapping } super.put(key, value); } } @Override public void setData(final List data) { // TODO Implement 'smarter' logic, for replacing just part of the data? if (data == m_data) return; List oldData = m_data; m_data = null == data ? Collections.EMPTY_LIST : data; if (!updateExistingViews(oldData, data)) notifyDataSetChanged(); else if (DEBUG) Log.d(TAG, "Updated without notifyDataSetChanged"); } /** * See if we can update the data within existing layout, without re-drawing the list. * @param oldData * @param newData * @return */ private boolean updateExistingViews(List oldData, List newData) { /** * Iterate over new data, compare to old. If IDs out of sync, stop and return false. Else - update visible * items. */ final int oldDataSize = oldData.size(); if (oldDataSize != newData.size()) return false; DBObject newObj; int nVisibleViews = m_visibleViews.size(); if(nVisibleViews == 0) return false; for (int position = 0; nVisibleViews > 0 && position < oldDataSize; position++) { newObj = newData.get(position); if (oldData.get(position).getId() != newObj.getId()) return false; // iterate over visible objects and see if this ID is there. final View view = m_visibleViews.get(position); if (null != view) { // this position has a visible view, let's update it! bindView(position, view, false); nVisibleViews--; } } return true; } 

и, конечно же,

 @Override public View getView(final int position, final View convertView, final ViewGroup parent) { final View result = createViewFromResource(position, convertView, parent); m_visibleViews.put(position, result); return result; } 

Игнорировать последний параметр bindView (я использую его, чтобы определить, нужно ли мне перерабатывать растровые изображения для ImageDrawable).

Как уже упоминалось выше, общее количество «видимых» представлений - это примерно то количество, которое подходит на экране (игнорируя изменения ориентации и т. Д.), Поэтому нет большого размера памяти.

именно я использовал это

 private void updateSetTopState(int index) { View v = listview.getChildAt(index - listview.getFirstVisiblePosition()+listview.getHeaderViewsCount()); if(v == null) return; TextView aa = (TextView) v.findViewById(R.id.aa); aa.setVisibility(View.VISIBLE); } 

Я составил другое решение, такое как метод RecyclyerView void notifyItemChanged(int position) , создать class CustomBaseAdapter следующим образом:

 public abstract class CustomBaseAdapter implements ListAdapter, SpinnerAdapter { private final CustomDataSetObservable mDataSetObservable = new CustomDataSetObservable(); public boolean hasStableIds() { return false; } public void registerDataSetObserver(DataSetObserver observer) { mDataSetObservable.registerObserver(observer); } public void unregisterDataSetObserver(DataSetObserver observer) { mDataSetObservable.unregisterObserver(observer); } public void notifyDataSetChanged() { mDataSetObservable.notifyChanged(); } public void notifyItemChanged(int position) { mDataSetObservable.notifyItemChanged(position); } public void notifyDataSetInvalidated() { mDataSetObservable.notifyInvalidated(); } public boolean areAllItemsEnabled() { return true; } public boolean isEnabled(int position) { return true; } public View getDropDownView(int position, View convertView, ViewGroup parent) { return getView(position, convertView, parent); } public int getItemViewType(int position) { return 0; } public int getViewTypeCount() { return 1; } public boolean isEmpty() { return getCount() == 0; } { } } 

Не забудьте также создать class mDataSetObservable для переменной mDataSetObservable в CustomAdapterClass , например:

 public class CustomDataSetObservable extends Observable { public void notifyChanged() { synchronized(mObservers) { // since onChanged() is implemented by the app, it could do anything, including // removing itself from {@link mObservers} - and that could cause problems if // an iterator is used on the ArrayList {@link mObservers}. // to avoid such problems, just march thru the list in the reverse order. for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onChanged(); } } } public void notifyInvalidated() { synchronized (mObservers) { for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onInvalidated(); } } } public void notifyItemChanged(int position) { synchronized(mObservers) { // since onChanged() is implemented by the app, it could do anything, including // removing itself from {@link mObservers} - and that could cause problems if // an iterator is used on the ArrayList {@link mObservers}. // to avoid such problems, just march thru the list in the reverse order. mObservers.get(position).onChanged(); } } } 

в classе CustomBaseAdapter существует метод notifyItemChanged(int position) , и вы можете вызвать этот метод, когда хотите обновить строку везде, где хотите (от нажатия кнопки или в любом месте, где вы хотите вызвать этот метод). И voila!, Ваш единственный ряд будет обновляться мгновенно ..

  • удалить несколько элементов в пользовательском listview
  • Android - ListView для загрузки большего количества предметов по достижении конца
  • Удалить элементы ListView на Android
  • Отображение текущего выбора в списке
  • Android: кнопка «Радио» в пользовательском списке
  • Как добавить нижний колонтитул в ListView?
  • Как получить выбранные элементы из списка Multi Select List
  • Как выделить строку в ListView в Android?
  • Пользовательский getFilter в пользовательском ArrayAdapter в android
  • notifyDataSetChanged () делает обновление списка и прокручивает подсказки назад
  • Программно перейдите к определенной позиции в Android ListView
  • Давайте будем гением компьютера.