После того, как я получил данные для одной строки ListView
, я хочу обновить эту единственную строку.
В настоящее время я использую notifyDataSetChanged();
, но это заставляет View
реагировать очень медленно. Есть ли другие решения?
После того, как я получил данные для одной строки ListView
, я хочу обновить эту единственную строку.
В настоящее время я использую notifyDataSetChanged();
, но это заставляет View
реагировать очень медленно. Есть ли другие решения?
Один из вариантов заключается в том, чтобы напрямую манипулировать ListView
. Сначала проверьте, находится ли индекс обновленной строки между getFirstVisiblePosition()
и getLastVisiblePosition()
, эти два дают вам первую и последнюю позиции в адаптере, которые видны на экране. Затем вы можете получить строку View
с помощью getChildAt(int index)
и изменить ее.
Как Romain Guy объяснил некоторое время назад во время Google I/O сеанс, самый эффективный способ только обновить один вид в представлении списка - это что-то вроде следующего (это обновление всего представления данных):
ListView list = getListView();
int start = list.getFirstVisiblePosition();
for(int i=start, j=list.getLastVisiblePosition();i<=j;i++)
if(target==list.getItemAtPosition(i)){
View view = list.getChildAt(i-start);
list.getAdapter().getView(i, view, list);
break;
}
Предполагая, что target
- это один элемент адаптера.
Этот код извлекает ListView
, затем просматривает отображаемые в настоящее время виды, сравнивает элемент target
, который вы ищете, с каждым отображаемым элементом просмотра, и если ваша цель среди них, получите закрывающий вид и выполните переход getView
, чтобы обновить отображение.
В качестве побочного примечания invalidate
не работает, как ожидают некоторые люди, и не будет обновлять представление, например getView, notifyDataSetChanged
перестроит весь список и в итоге вызовет getView
для каждого отображаемого элемента и invalidateViews
также повлияет на связку.
Наконец, можно получить дополнительную производительность, если ему нужно только изменить дочерний элемент представления строки, а не всю строку, например getView
. В этом случае следующий код может заменить list.getAdapter().getView(i, view, list);
(пример для изменения текста TextView
):
((TextView)view.findViewById(R.id.myid)).setText("some new text");
В коде, которому мы доверяем.
Этот более простой метод работает хорошо для меня, и вам нужно знать только индекс позиции, чтобы получить представление:
// mListView is an instance variable
private void updateItemAtPosition(int position) {
int visiblePosition = mListView.getFirstVisiblePosition();
View view = mListView.getChildAt(position - visiblePosition);
mListView.getAdapter().getView(position, view, mListView);
}
Следующий код работал у меня. Обратите внимание, что при вызове GetChild() вы должны компенсировать первый элемент в списке, поскольку он относится к нему.
int iFirst = getFirstVisiblePosition();
int iLast = getLastVisiblePosition();
if ( indexToChange >= numberOfRowsInSection() ) {
Log.i( "MyApp", "Invalid index. Row Count = " + numberOfRowsInSection() );
}
else {
if ( ( index >= iFirst ) && ( index <= iLast ) ) {
// get the view at the position being updated - need to adjust index based on first in the list
View vw = getChildAt( sysvar_index - iFirst );
if ( null != vw ) {
// get the text view for the view
TextView tv = (TextView) vw.findViewById(com.android.myapp.R.id.scrollingListRowTextView );
if ( tv != null ) {
// update the text, invalidation seems to be automatic
tv.setText( "Item = " + myAppGetItem( index ) + ". Index = " + index + ". First = " + iFirst + ". Last = " + iLast );
}
}
}
}
Есть еще более эффективная вещь, которую вы можете сделать, если она подходит для вашего прецедента.
Если вы меняете состояние и можете каким-то образом называть его правильным (зная позицию) mListView.getAdapter().getView()
, он будет наиболее эффективным из всех.
Я могу продемонстрировать действительно простой способ сделать это, создав анонимный внутренний класс в классе ListAdapter.getView()
. В этом примере у меня есть TextView
, показывающий текст "новый", и этот вид имеет значение GONE
при щелчке элемента списка:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// assign the view we are converting to a local variable
View view = convertView;
Object quotation = getItem(position);
// first check to see if the view is null. if so, we have to inflate it.
if (view == null)
view = mInflater.inflate(R.layout.list_item_quotation, parent, false);
final TextView newTextView = (TextView) view.findViewById(R.id.newTextView);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mCallbacks != null)
mCallbacks.onItemSelected(quotation.id);
if (!quotation.isRead()) {
servicesSingleton.setQuotationStatusReadRequest(quotation.id);
quotation.setStatusRead();
newTextView.setVisibility(View.GONE);
}
}
});
if(quotation.isRead())
newTextView.setVisibility(View.GONE);
else
newTextView.setVisibility(View.VISIBLE);
return view;
}
Фреймворк автоматически использует правильный position
, и вам придется беспокоиться о его повторном вызове перед вызовом getView
.
Только для записи кто-нибудь рассмотрел "View view" на методе переопределения?
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//Update the selected item
((TextView)view.findViewById(R.id.cardText2)).setText("done!");
}