Как в ExpandableListView сохранять/восстанавливать view вместо пересоздания?

Рейтинг: 0Ответов: 1Опубликовано: 04.09.2014

Есть ExpandableList. В нем все группы разные, child'ы у каждой группы тоже свои, причем некоторые имеют довольно нагруженный layout (присутствуют "трогательные" элементы - кнопки, чекбоксы, spinner), так что при прокрутке список притормаживает. При этом групп и child'ов мало (хотя в экран и не входят), поэтому я считаю разумным не создавать для них View каждый раз, а сохранить после первого создания и далее просто восстанавливать (тем более что при одном обновлении списка он каждый view не по одному разу запрашивает).
Проблема состоит в следующем:
Когда view каждый раз создается, то все работает правильно: нажатия отрабатываются и все остальные взаимодействия с экраном срабатывают сразу и правильно. Но вот когда я сохраняю view и в getChildView возвращаю этот сохраненный, происходит следующее. Когда я нажимаю кнопку, располагающуюся внутри, то сразу ничего не происходит. Только когда я начинаю прокручивать список, тогда отрабатывается нажатие на ту кнопку. Еще одна особенность, о которой стоит упомянуть: сразу после появления (например, я прокрутил список, чтобы child исчез с экрана, а потом обратно, соответственно getChildView достал сохраненный view) первое касание child отрабатывает сразу (как должно), а второе и последующие опять так же. Все описанное справедливо и для остальных clickable элементов.
Немного кода адаптера:

CartExpandableListAdapter extends BaseExpandableListAdapter {
    ArrayList children; // массив с сохраненными view
...

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (children.size()<=groupPosition) children.add(new ArrayList<View>());
        switch (groupPosition){
            case 0: // dishes
// дальше создается view для группы, их пока не трогал
        }
        return convertView;
    }

    @Override
    public View getChildView(int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (children.size()>groupPosition)
        {
            if (((ArrayList)children.get(groupPosition)).size()>childPosition) {
                convertView = (View)((ArrayList)children.get(groupPosition)).get(childPosition);
                return convertView;
            }
        }
        else children.add(groupPosition,new ArrayList<View>());
        boolean createView = true;

        switch (groupPosition){
            case 0:
// тут создается view в зависимости от группы, которой он принадлежит

        }
        ((ArrayList) children.get(groupPosition)).add(convertView);
        return convertView;
    }

Подозреваю, что причина описанного явления где-то в природе класса View. Его я, возможно, знаю недостаточно хорошо. Я перепробовал установку для view статусов focusable, activated, ещё setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES), ничего не менялось. Ума не приложу, отчего такое поведение.

Ответы

▲ 1Принят

Не нужно ручками их кешировать. Это правило. Вы его нарушили и получили странное. На самом деле нужно просто проверять convertView, которое Вам приходит в getChildView и если оно не NULL, то просто инициализировать правильными значениями. Но так как у Вас разные плашки (элементы списка) в списке, то список должен знать, какую именно плашку подсунуть. Для этого есть два воспомогательные метода getChildTypeCount и getChildType. Первый возвращает количество "различных типов плашек". Второй возвращает, какой тип плашки нужен в данном месте. Если правильно подсунете тип, то в getChildView придет view нужного типа.

а getChildView будет такой

public View getChildView(int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (convertView == NULL) {
          // тут создаете view)
        }
        а тут обычная инициализация всех полей.
        return convertView;
    }