Після того, як я отримав дані для одного рядка a ListView
, я хочу оновити цей один рядок.
В даний час я використовую, notifyDataSetChanged();
але це робить View
реакцію дуже повільною. Чи є інші рішення?
Відповіді:
Одним із варіантів є маніпулювання ListView
безпосередньо. Спочатку перевірте, чи є індекс оновленого рядка між getFirstVisiblePosition()
і getLastVisiblePosition()
, ці два дають вам першу і останню позиції в адаптері, які видно на екрані. Тоді ви можете отримати рядок за View
допомогою getChildAt(int index)
та змінити його.
Як Ромен Гай пояснив деякий час під час сеансу вводу-виводу Google , найефективніший спосіб оновити лише одне представлення у поданні списку - це приблизно наступне (це оновить цілі дані представлення):
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 );
}
}
}
}
sysvar_index
означає позицію рядка, який ви хочете оновити
Є ще набагато ефективніше, що ви можете зробити, якщо це відповідає вашому випадку використання.
Якщо ви змінюєте стан і можете якось назвати належне (знаючи позицію), 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
.
Тільки для запису, хто-небудь розглядав "Перегляд подання" щодо методу заміни?
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//Update the selected item
((TextView)view.findViewById(R.id.cardText2)).setText("done!");
}
newView(...)
за адаптером, це дасть мені інший погляд, який, однак, не поміщений уlistView
.