Android ListView Оновити Однорядковий


77

Після того, як я отримав дані для одного рядка a ListView, я хочу оновити цей один рядок.

В даний час я використовую, notifyDataSetChanged();але це робить Viewреакцію дуже повільною. Чи є інші рішення?

Відповіді:


30

Одним із варіантів є маніпулювання ListViewбезпосередньо. Спочатку перевірте, чи є індекс оновленого рядка між getFirstVisiblePosition()і getLastVisiblePosition(), ці два дають вам першу і останню позиції в адаптері, які видно на екрані. Тоді ви можете отримати рядок за Viewдопомогою getChildAt(int index)та змінити його.


3
Дякую. Це могло б спрацювати, якщо я редагую поточний макет дитини. Але що, якщо я хочу змінити макет взагалі (надувши інший xml)? Якщо я зателефоную newView(...)за адаптером, це дасть мені інший погляд, який, однак, не поміщений у listView.
хаді

1
@hadi ви повинні поглянути на цей відповідь: stackoverflow.com/a/11568271/858626
Jose_GD

90

Як Ромен Гай пояснив деякий час під час сеансу вводу-виводу 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");

У коді ми довіряємо.


17
Ймовірно, вам слід просто зв’язати свою іншу відповідь stackoverflow.com/a/9987616/1026132 замість копіювання та вставки сюди. Це допомогло б зменшити кількість дублікатів вмісту в SO.
Nacho Coloma

2
як реалізувати це з різним типом подання?
Zyoo

1
У відповіді посилання на "сеанс вводу-виводу Google" мертве.
Панг

здається дивним! Я спробую це сьогодні
Ракеш Патіл

можливо, іноді вам потрібно використовувати "list.getAdapter (). getView (i - list.getHeaderviewCount, view, list);" замість "list.getAdapter (). getView (i, view, list);"
QuinnChen

28

Цей простіший метод добре працює для мене, і вам потрібно знати лише індекс позиції, щоб привернути увагу:

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

2
Працює як шарм, +1
Рутгер

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

6

Наступний код працював для мене. Зверніть увагу, що під час виклику 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означає позицію рядка, який ви хочете оновити
Хтось десь

2

Є ще набагато ефективніше, що ви можете зробити, якщо це відповідає вашому випадку використання.

Якщо ви змінюєте стан і можете якось назвати належне (знаючи позицію), 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.


0

Для мене працював нижній рядок:

Arraylist.set (позиція, новий ModelClass (значення));

Adapter.notifyDataSetChanged ();


-2

Тільки для запису, хто-небудь розглядав "Перегляд подання" щодо методу заміни?

   public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

                    //Update the selected item
                    ((TextView)view.findViewById(R.id.cardText2)).setText("done!");

                }

Питання полягало в тому, як оновлювати один рядок у ListView у будь-який момент. onItemClick викликається лише тоді, коли користувачі клацають елемент, і тому цього недостатньо
Thizzer
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.