Android ListView Refresh Single Row

После того, как я получил данные для одной строки ListView, я хочу обновить эту единственную строку.

В настоящее время я использую notifyDataSetChanged();, но это заставляет View реагировать очень медленно. Есть ли другие решения?

Ответ 1

Один из вариантов заключается в том, чтобы напрямую манипулировать ListView. Сначала проверьте, находится ли индекс обновленной строки между getFirstVisiblePosition() и getLastVisiblePosition(), эти два дают вам первую и последнюю позиции в адаптере, которые видны на экране. Затем вы можете получить строку View с помощью getChildAt(int index) и изменить ее.

Ответ 2

Как 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");

В коде, которому мы доверяем.

Ответ 3

Этот более простой метод работает хорошо для меня, и вам нужно знать только индекс позиции, чтобы получить представление:

// 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);
}

Ответ 4

Следующий код работал у меня. Обратите внимание, что при вызове 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 );
            }
        }
    }
}  

Ответ 5

Есть еще более эффективная вещь, которую вы можете сделать, если она подходит для вашего прецедента.

Если вы меняете состояние и можете каким-то образом называть его правильным (зная позицию) 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.

Ответ 6

Только для записи кто-нибудь рассмотрел "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!");

                }