Проблема с открытым коллапсом ExpandableListView

Я использую настраиваемый адаптер расширяемого списка. Я выделяю дочерний элемент, когда пользователь нажимает на него. Это работает нормально, пока пользователь не откроет/скроет группу. Скажем, пользователь касается элемента 1 группы 2. Это выделяет элемент 1 группы 2. Затем пользователь открывает группу 1. Теперь выделен элемент 2 группы 3. Я провел несколько тестов, выбирая разные элементы, и не могу найти шаблон, на который будет переходить выделенная строка. Иногда он вверху списка, иногда внизу. У меня возникли проблемы с выяснением логики, чтобы вставить onGroupExpandListener и onGroupCollapseListener моей активности, чтобы повторно выделить правильное представление. Любые идеи?

РЕДАКТИРОВАТЬ: текущий код внутри моего onChildClickListener

if (groupPosition == 0){ 
      switch(childPosition) {
      case 0: 
        previouslySelectedView.setBackgroundResource(R.color.transparent);
        currentlySelectedView.setBackgroundResource(R.color.blue); 
        break;

Один и тот же код для всех групп/детей


person yellavon    schedule 26.05.2011    source источник
comment
Опубликуйте свой код, чтобы мы могли увидеть, делаете ли вы что-то, что может вызвать это.   -  person Ted Hopp    schedule 26.05.2011


Ответы (3)


Выбор элементов в ExpandableListView осуществляется через плоский список (абсолютно позиционированный). Таким образом, если вновь открытая группа находится перед текущим выбором и имеет меньше потомков, то выбор будет двигаться вверх, и наоборот. Я предлагаю установить для режима выбора значение none и реализовать логику onclick/expand для самостоятельной обработки фокуса — например, внедрить теги для представлений и установить текущий выделенный элемент с помощью тега.

Вот несколько предложений:

  1. В ExpandableListView.OnChildClickListener сначала вы выполняете ExpandableListView.findViewWithTag(theTag), чтобы проверить представления с таким тегом и снять с них отметку (также setTag(null)) и восстановить фон. Затем для элемента нажал setTag(theTag) и изменил фон на выбранный. Конечно, вы можете использовать другую логику и отметить несколько элементов. Обратите внимание, что после уничтожения представления вы потеряете выбор (например, во время расширения).
  2. У вас есть какая-то пользовательская карта или что-то, что будет содержать уникальный идентификатор вида и (не) отмеченного состояния. Это лучшее решение, которое позволит поддерживать постоянный выбор при прокрутке и расширении.
  3. В бэк-адаптере ввести «отмеченное» состояние. Таким образом, маркировка будет сохраняться даже между запуском/остановкой приложения. Однако это не очень хороший подход, потому что выбор — это скорее поведение пользовательского интерфейса.

В настоящее время я делаю выбор ExpandableListView с выбором режима множественного списка. Но поскольку, как я уже сказал, выбор осуществляется по позициям, мне пришлось пожертвовать с точки зрения функциональности, а именно, я очищаю выбор всякий раз, когда производится манипуляция. Предыдущая реализация была с пользовательской картой, содержащей выбранные идентификаторы, и, честно говоря, это был лучший подход.

Вот как я получаю выбранные идентификаторы (помните, что я использую множественный режим выбора):

final SparseBooleanArray checkedPositions = expList.getCheckedItemPositions();
final ExpandableListAdapter adapter = expList.getExpandableListAdapter();
List<Long> checkedIds = new ArrayList<Long>();
if (packedPositionType == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
    for (int i = checkedPositions.size() - 1; i >= 0; i--) {
        if (checkedPositions.valueAt(i)) {
            checkedIds.add(adapter.getGroupId(checkedPositions.keyAt(i)));
        }
    }
}

В вашем случае, тем не менее, вы захотите проверить упакованные позиции CHILD. Также обратите внимание, что мой адаптер имеет стабильные (уникальные) идентификаторы. Если у вас нет стабильных идентификаторов, вы можете положиться на метод getPackedPositionForChild() ExpandableListView и сохранить упакованную позицию как отмеченную.

person Kamen    schedule 26.05.2011
comment
Я провел небольшое исследование тегов. Похоже, это может быть решением, но как программно присвоить элементу списка тег? - person yellavon; 26.05.2011
comment
как получить уникальный идентификатор просмотра, на который нажали? Это переменная, которая является частью представления? Кроме того, как только я сохраняю этот идентификатор, я не понимаю, как я могу вернуться к представлению. код sudo будет похож на View v = findViewWithUniqueID(uniqueID) ? - person yellavon; 27.05.2011
comment
Уникальный идентификатор может быть установлен адаптером как тег, или вы можете использовать вышеупомянутые упакованные позиции, которые в основном представляют собой расширяемый вариант просмотра списка позиций просмотра списка. - person Kamen; 28.05.2011

Правильный способ сделать это (Решит прыгающую подсветку при коллапсе группы)

Сделайте следующее в ExpandableListView:

Шаг 1. Установите одиночный режим выбора (можно сделать в xml как android:choiceMode = "singleChoice")

Шаг 2. Используйте XML-файл селектора в качестве фона (android:listSelector = "@drawable/selector_list_item")

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
   android:exitFadeDuration="@android:integer/config_mediumAnimTime">

   <item android:drawable="@android:color/holo_blue_bright" android:state_pressed="true"/>
   <item android:drawable="@android:color/holo_blue_light" android:state_selected="true"/>
   <item android:drawable="@android:color/holo_blue_dark" android:state_activated="true"/>

</selector>

Шаг 3. Вызов expandableListView.setItemChecked(index,true) в обратном вызове onChildClick().

index — индекс дочернего элемента, основанный на 0, который рассчитывается следующим образом.

Группа 1 [индекс = 0]

  • Дочерний элемент 1
  • Дочерний элемент 2
  • Дочерний элемент 3 [индекс = 3]

Группа 2 [индекс = 4]

  • Дочерний элемент 1
  • Дочерний элемент 2
  • Дочерний элемент 3 [индекс = 7]

Группа 3 [индекс = 8]

  • Дочерний элемент 1
  • Дочерний элемент 2
  • Дочерний элемент 3 [индекс = 11]

Если у нас также есть заголовки списка, они также будут учитывать значения индекса.

Вот рабочий пример:

@Override
public boolean onChildClick(ExpandableListView parent, View v,
        int groupPosition, int childPosition, long id) {
    ...

    int index = parent.getFlatListPosition(ExpandableListView.getPackedPositionForChild(groupPosition, childPosition));
    parent.setItemChecked(index, true);


    return true;
}
person Himanshu Likhyani    schedule 19.03.2013
comment
Это работает, однако выделение теряется при сворачивании группы, содержащей выбранный элемент. - person Jack Pettinger; 21.01.2014

Мое решение проблемы коллапса:

mDrawerExpandableList.setOnGroupCollapseListener(new OnGroupCollapseListener() {
    @Override
    public void onGroupCollapse(int groupPosition) {
        if(ExpandableListView.getPackedPositionGroup(selectedFragmentId) == groupPosition) {
            mDrawerExpandableList.setItemChecked(mDrawerExpandableList.getFlatListPosition(ExpandableListView.getPackedPositionForGroup(groupPosition)), true);
        }
    }
});
mDrawerExpandableList.setOnGroupExpandListener(new OnGroupExpandListener() {
    @Override
    public void onGroupExpand(int groupPosition) {
        if(ExpandableListView.getPackedPositionGroup(selectedFragmentId) == groupPosition) {
            mDrawerExpandableList.setItemChecked(((ExpandableListView)drawerListView).getFlatListPosition(selectedFragmentId), true);
        }
    }
});
person Thomas G.    schedule 02.06.2014