Обновить listView с несколькими типами представления после удаления/удаления элемента

У меня есть ListView с пользовательским адаптером, который расширяет BaseAdapter и имеет 2 типа представления. Когда я запускаю свой метод adapter.removeRow(position), данные для адаптера корректно обновляются, и список отражает это, но типы представлений обновляются некорректно. Адаптер поддерживается

ArrayList<Map<String, String>> rows = new ArrayList<Map<String, String>>();

и у меня есть подмножество

List<Integer> flashSet = new ArrayList<Integer>();

который представляет собой список всех позиций с типом представления 1 (в отличие от стандартного типа представления 0).

Вот мой метод адаптера removeRow(position):

    public void removeRow(int position) {
        if (getItemViewType(position) == TYPE_FLASH) {
            flashSet.remove(position);
        }
        for (int flashPosition:flashSet) {
            System.out.println(tag+"is "+flashPosition+" going to be moved?");
             if (flashPosition > position) {
                 flashPosition -= 1;
                 System.out.println(tag+"Yes! It's been moved to "+flashPosition);
             }
        }
        rows.remove(position);
        notifyDataSetChanged();
    }

Вот мой метод getView:

@Override
    public View getView(int position, View convertView, ViewGroup parent) {
        FlashHolder flashHolder;
        ClipHolder clipHolder;
        int type = getItemViewType(position);
        if (convertView == null) {
            if (type == TYPE_CLIP) {
                convertView = rowInflater.inflate(R.layout.clip_note_row_layout, null);
                clipHolder = new ClipHolder();
                flashHolder = null;
                clipHolder.textView = (TextView)(convertView.findViewById(R.id.clip_text));
                convertView.setTag(clipHolder);
            } else {
                convertView = rowInflater.inflate(R.layout.flash_row_layout, null);
                clipHolder = null;
                flashHolder = new FlashHolder();
                flashHolder.front = (TextView)(convertView.findViewById(R.id.flash_text));
                flashHolder.back = (TextView)(convertView.findViewById(R.id.clip_text));
                convertView.setTag(flashHolder);
            }
        } else {
            if (type == TYPE_CLIP) {
                clipHolder = (ClipHolder)convertView.getTag();
                flashHolder = null;
            } else {
                clipHolder = null;
                flashHolder = (FlashHolder)convertView.getTag();
            }
        }
        if (type == TYPE_CLIP) {
            clipHolder.textView.setText(rows.get(position).get("clip"));
        } else {
            flashHolder.front.setText(rows.get(position).get("flash_text"));
            flashHolder.back.setText(rows.get(position).get("clip"));
        }
        return convertView;
    }

Я знаю, что могу создать новый adapter, дать ему обновленный ArrayList и вызвать listView.setAdapter(adapter), но это кажется излишним, когда я просто пытаюсь удалить один элемент из потенциально длинного списка. Смотрите фотографии до и после удаления: Before

Затем я удаляю первый элемент. Слово «который» было скрыто за пунктом «Давайте посмотрим», а теперь пункт «вдохновленный…» скрыт за пустым пунктом 3.

После

Итак, данные обновляются, а типы представлений — нет. Спасибо за помощь!


person willlma    schedule 13.03.2013    source источник
comment
+1 за внешний вид приложения и хорошее объяснение проблемы   -  person Nezam    schedule 13.03.2013
comment
Спасибо. Я дам вам знать, когда он будет выпущен, если вы заинтересованы.   -  person willlma    schedule 13.03.2013
comment
Вы переопределили методы getViewTypeCount() и getItemViewType() в своем адаптере?   -  person M-WaJeEh    schedule 13.03.2013
comment
Да, я понял проблему. Смотрите мой ответ ниже. Чистая глупость с моей стороны, и мне потребовалось задать вопрос здесь, чтобы понять, где я ошибаюсь.   -  person willlma    schedule 13.03.2013
comment
И я вижу несоответствие в вашем методе { и } в getView(). Пожалуйста, обновите свой вопрос, если это не ошибка кодирования.   -  person M-WaJeEh    schedule 13.03.2013
comment
Готово, спасибо. Я убрал несколько пунктов в онлайн-редакторе здесь.   -  person willlma    schedule 13.03.2013


Ответы (3)


Я понял. Это никому не будет полезно, так как я не ожидаю, что другие сделают ту же ошибку.

Я наивно думал, что делая это

    for (int flashPosition:flashSet) {
        System.out.println(tag+"is "+flashPosition+" going to be moved?");
         if (flashPosition > position) {
             flashPosition -= 1;
             System.out.println(tag+"Yes! It's been moved to "+flashPosition);
         }
    }

Я менял фактическое значение, хранящееся в List<Integer> flashSet = new ArrayList<Integer>();

На самом деле, мне нужно сделать следующее:

for (int flashPosition:flashSet) {
    System.out.println(tag+"is "+flashPosition+" going to be moved?");
     if (flashPosition > position) {
         flashSet.remove((Object)flashPosition);
         flashPosition -= 1;
         flashSet.add(flashPosition);
         System.out.println(tag+"Yes! It's been moved to "+flashPosition);
     }
}
person willlma    schedule 13.03.2013
comment
Не проще ли было бы ArrayList<MyListData>. MyListData является абстрактным классом и имеет переменную int type;. Теперь вам просто понадобятся разные конкретные реализации. Таким образом, вы можете иметь столько типов View, сколько захотите, в своем ListView, без необходимости управлять сложными и медленными механизмами поддержки индексов. Например. в вашем getItemViewType() я думаю, вы полагаетесь на метод List.contains(), тогда как в приведенном выше подходе вы можете просто вызвать return rows.get(position).type. - person M-WaJeEh; 13.03.2013
comment
Да, я использую List.contains(). Я понимаю что ты имеешь ввиду. Это хорошая идея. Как вы думаете, это улучшит производительность или вы просто думаете, что это более элегантно. Я реализую это, если мне понадобится третий тип представления. Спасибо. - person willlma; 13.03.2013
comment
Метод contains() занимает O(n) времени, чтобы найти элемент. getIteViewType() вызывается много раз, когда пользователь прокручивает ListView, и каждый раз, когда метод contains() выполняет итерацию по вашему flasSet, который, на мой взгляд, потребляет больше ресурсов. Вот что я говорю. Лучше сначала написать гибкий код, чем возвращаться и менять весь код только потому, что вы хотите добавить заголовки или причудливые строки в некоторых местах вашего ListView :) - person M-WaJeEh; 13.03.2013
comment
Также, используя этот подход, вы можете иметь совершенно разные типы данных для каждой строки вашего ListView, а не только String. Одна строка может содержать небольшие Bitmap, а другая представляет собой комбинацию boolean и String и т. д. - person M-WaJeEh; 13.03.2013
comment
Дело принято. Реализуем сейчас. Спасибо. - person willlma; 13.03.2013

Попробуйте это. После удаления или добавления элемента вам нужно вызвать обновление адаптера.

Youradapter.notifyDataSetChanged();
person MuraliGanesan    schedule 13.03.2013

используйте 1_

вместо

notifyDataSetChanged();

меня устраивает.

person Poovizhirajan N    schedule 13.03.2013
comment
К сожалению, я получаю тот же самый результат. - person willlma; 13.03.2013