Android ListView Обновить одну строку - PullRequest
76 голосов
/ 23 января 2010

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

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

Ответы [ 6 ]

88 голосов
/ 03 апреля 2012

Как объяснил Ромен Гай некоторое время назад во время сеанса ввода-вывода 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");

В коде мы доверяем.

30 голосов
/ 23 января 2010

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

27 голосов
/ 09 мая 2013

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

// 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);
}
6 голосов
/ 17 января 2012

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

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

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

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

                }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...