Подтвердить что ты не робот

ExpandableListView - сохранить выбранный дочерний объект в "нажатом" состоянии

Я использую ExpandableListView в левом навигаторе для экрана планшета.

Когда пользователь нажимает дочерний элемент группы в моем расширяемом списке, я хотел бы сохранить дочерний элемент в нажатом состоянии, чтобы пользователь знал, для какого ребенка отображается содержимое правой руки.

Для ListView я смог выполнить этот эффект с помощью этой строки в коде:

getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);

а затем применяя этот селектор:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="true" android:drawable="@drawable/menu_item_pressed" />
<item android:state_pressed="false" android:state_focused="false" android:drawable="@drawable/menu_item_normal" />
<item android:state_pressed="true" android:drawable="@drawable/menu_item_pressed" />
<item android:state_focused="true" android:state_pressed="false" android:drawable="@drawable/menu_item_pressed" />

"state_activiated" истинно в элементе listView после нажатия элемента listView.

Я надеялся, что эта же техника будет работать для моего расширяемогоListView, но это не так. Я использовал:

getExpandableListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);

а затем использовал тот же селектор выше, но он не работает. Я также пробовал другие состояния, такие как state_selected и state_checked, и они тоже не работают.

Селектор выше правильно применяет нажатое и не нажатое состояние. Похоже, что при использовании ExpandableListView состояние не активируется после нажатия дочернего элемента.

Любая помощь приветствуется. Спасибо!

4b9b3361

Ответ 1

Я решил решить эту проблему, не используя селектор в дочернем представлении. Адаптер для expandableListView принимает данные для каждой группы и дочернего элемента. Я добавил "выбранный" логический для дочерних данных. Фрагмент заботится о правильной установке каждого дочернего элемента, выбранного логическим, на значение true или false. Затем в адаптере "getChildView" метод, если дочерний "выбранный" логический значение true, я устанавливаю фоновый ресурс представления на мой "нажатый" фон. В противном случае я установил его на "не нажатый" фон. Выполнение этого в дополнение к использованию селектора для textView на дочернем представлении для изменения цвета текста при нажатии обеспечивает желаемый эффект.

Ниже перечислены вещи, которые мой Фрагмент делает для того, чтобы ребенок "выбрал" логический:

  • сохраняйте данные группы/ребенка как переменную класса в вашем фрагменте
  • onChildClick - перебирать дочерние данные, устанавливая все "selected" = false. Затем установите для дочернего элемента текущей группы /child значение true. Обновите expandableListView, вызвав setEmptyView, reset прослушиватели и установите адаптер списка.
  • onGroupClick - перебирает дочерние данные, устанавливая все "selected" = false. Затем установите для дочернего элемента текущей группы /child значение true.
  • где бы вы ни находились в своем фрагменте, где вы показываете свой список. - Заполните данные адаптера, установите адаптер списка, вызовите notifyDataSetChanged на адаптер. Обновите expandableListView, вызвав setEmptyView, reset прослушиватели и установите адаптер списка.

В дополнение к вышеперечисленному я поддерживаю переменные currentGroupPosition и currentChildPosition как класс. Использование setRetainInstance = true позволяет правильно работать с такими вещами, как поворот экрана.

Ответ 2

В 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 [index = 0]

  • Детский элемент 1
  • Детский элемент 2
  • Детский элемент 3 [index = 3]

Группа 2 [index = 4]

  • Детский элемент 1
  • Детский элемент 2
  • Детский элемент 3 [index = 7]

Группа 3 [index = 8]

  • Детский элемент 1
  • Детский элемент 2
  • Детский элемент 3 [index = 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;
}

Ответ 3

Я не думаю, что есть встроенный способ делать то, что вы пытаетесь использовать со встроенными функциями Android. Чтобы провести некоторое тестирование, я начал с кода из примеров API Android (C:\<android-sdk-directory>\samples\android-15\ApiDemos\src\com\example\android\apis\view\ExpandableList3.java).

Я использовал ExpandableListView.CHOICE_MODE_SINGLE. Режим выбора применяется к тому, что отмечен или выбран флажок View. (Исходный код ExpandableListView говорит, что он определяет, сколько элементов может быть выбрано одновременно, но в ListView, похоже, оно относится к тому, что отмечено.) Также обратите внимание, что документ говорит, что вы не можете получить действительный результат от getCheckedItemPosition для ListView в режиме одного выбора. Это кажется правдой.

Мое тестирование показало, что ExpandableListActivity автоматически не делает выбранный или затронутый элемент нажатым/затронутым отмеченным или выбранным, и он автоматически не дает фокус выбранному ребенку. Только сам ExpandableListActivity и группа, содержащая дочерний элемент, автоматически фокусируются после нажатия одного из своих детей.

Вот некоторые из моих кодов (большая часть из них взята из примеров Android):

@Override
public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    final String NAME = "NAME";
    final String IS_EVEN = "IS_EVEN";

    List<Map<String, String>> groupData = new ArrayList<Map<String, String>>();
    List<List<Map<String, String>>> childData = new ArrayList<List<Map<String, String>>>();

    for (int i = 0; i < 12; i++) {

        Map<String, String> curGroupMap = new HashMap<String, String>();
        groupData.add(curGroupMap);

        curGroupMap.put(NAME, "Group " + i);
        curGroupMap.put(IS_EVEN, (i % 2 == 0) ? "This group is even" : "This group is odd");

        List<Map<String, String>> children = new ArrayList<Map<String, String>>();

        for (int j = 0; j < 12; j++) {

            Map<String, String> curChildMap = new HashMap<String, String>();
            children.add(curChildMap);

            curChildMap.put(NAME, "Child " + j);
            curChildMap.put(IS_EVEN, (j % 2 == 0) ? "This child is even" : "This child is odd");
        }

        childData.add(children);
    }

    SimpleExpandableListAdapter adapter = new SimpleExpandableListAdapter(

        this,
        groupData,
        android.R.layout.simple_expandable_list_item_1,
        new String[] {NAME, IS_EVEN},
        new int[] {android.R.id.text1, android.R.id.text2},
        childData,
        R.layout.child, 
        new String[] {NAME, IS_EVEN},
        new int[] {android.R.id.text1, android.R.id.text2}
        );

    ExpandableListView expListView = (ExpandableListView)findViewById(android.R.id.list);

    setListAdapter(adapter);
    expListView.setChoiceMode(ExpandableListView.CHOICE_MODE_SINGLE);
    expListView.setOnChildClickListener(new OnChildClickListener() {

        @Override
        public boolean onChildClick(ExpandableListView expListView, View viewClicked, int groupPosition, int childPosition, long childRowId) {

            viewClicked.setSelected(true); // the only way to force a selection of the clicked item      

Обратите внимание на самую последнюю строку, которая заставляет дочерний щелчок иметь выбранное состояние. Затем вы можете использовать XML selector со списком состояний Drawable, чтобы выделить выбранный элемент, когда он выбран. (Это не работает полностью - подробнее об этом ниже.)

Внутри onChildClick в моем ExpandableListActivity, я провел некоторое тестирование и нашел следующее:

groupPosition; // index of group within the ExpandableListViewActivity           
childRowId; // for my particular ExpandableListView, this was the index of the child in the group
childPosition; // for my particularExpandableListView, this was also the index of the child in the group

expListView.getCheckedItemPosition(); // null - from ListView class
expListView.getSelectedView(); // null - from AbsListView class
expListView.getSelectedItemId(); // a long, based on the packed position - from AdapterView class
expListView.getSelectedId(); // -1 - from ExpandableListView class, calls getSelectedPosition()
expListView.getSelectedPosition(); // -1 - from ExpandableListView class, calls getSelectedItemPosition()
expListView.getSelectedItemPosition(); // -1 - from AdapterView class
expListView.getFocusedChild(); // null - from ViewGroup class
expListView.getItemIdAtPosition(1); // a long (View ID) - from AdapterView class
viewClicked.isFocused()); // false
viewClicked.isPressed()); // false
viewClicked.isSelected()); // true only if we explicitly set it to true
expListView.isFocused()); // true
expListView.isPressed()); // false
expListView.isSelected()); // false
expListView.getCheckedItemPosition())); // -1 - from ListView
expListView.getSelectedId())); // -1 - from ExpandableListView  

/* expListView.getChildAt(childPosition) is not helpful, because it looks at the rendered 
   LinearLayout with a bunch of TextViews inside it to get the index and find the child,
   and you won't generally know where to look for the child you want */

Я также попытался создать новый OnItemClickListener и реализовать его метод onItemClick. Хотя документация предполагает, что OnItemClickListener будет работать в некоторой емкости на ExpandableListView s, я никогда не видел, чтобы она вызывалась.

Первое, что я попытался изменить цвет фона на выбранном дочернем элементе и сохранить его выделенным, не помогло. Я установил Drawable для android:state_window_focused="true" внутри XML файла selector. Это сработало, но представило несколько проблем.

Вторая вещь, которую я пробовал, - это явно выбрать элемент, нажатый в onChildClick, вызывая viewClicked.setSelected(true);. Проблем здесь было меньше:

1) Если я прокручивал до тех пор, пока выделенный элемент не был вне поля зрения и не вернулся к нему, подсветка исчезла, 2) То же самое произошло бы, если бы я повернул телефон, и 3) Состояние выделенного элемента больше не было выбранный, как только он больше не будет виден на дисплее.

Android ExpandableListView, видимо, не предназначался для работы как меню навигации в стиле аккордеона, хотя это кажется довольно очевидным для него. Если вы хотите использовать его таким образом, я думаю, вам придется сделать довольно много настроек и выкапывать, как рисуется View.

Удачи.

Ответ 4

Объявить глобально:

View view_Group;

yourExpandableListView.setChoiceMode(ExpandableListView.CHOICE_MODE_SINGLE);
yourExpandableListView.setOnChildClickListener(new OnChildClickListener() {

    @Override
    public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { 
    v.setSelected(true); 
    if (view_Group != null) {
        view_Group.setBackgroundColor(Default Row Color Here);
    }
    view_Group = v;
    view_Group.setBackgroundColor(Color.parseColor("#676767"));      
}

Ответ 5

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

Создайте собственную модель меню, например

public class DrawerExpandedMenuModel {

String iconName = "";
int id = -1;
int iconImg = -1;
boolean isSelected = false;
.
.
.

Переопределить onChildClick ExpandableListView

        expandableList.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
        @Override
        public boolean onChildClick(ExpandableListView expandableListView, View view, int groupPosition, int childPosition, long l) {
            for (int k = 0; k < listDataHeader.size(); k++){
                listDataHeader.get(k).isSelected(false);
                if (listDataChild.containsKey(listDataHeader.get(k))) {
                    List<DrawerExpandedMenuModel> childList = listDataChild.get(listDataHeader.get(k));
                    if (childList != null) {
                        for (int j = 0; j < childList.size(); j++) {
                            if (k == groupPosition && j == childPosition) {
                                childList.get(j).isSelected(true);
                            } else {
                                childList.get(j).isSelected(false);
                            }
                        }
                    }
                }
            }
            mMenuAdapter.notifyDataSetChanged();

Переопределить onGroupClick из ExpandableListView

        expandableList.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
        @Override
        public boolean onGroupClick(ExpandableListView expandableListView, View view, int groupPosition, long l) {
            for (int k = 0; k < listDataHeader.size(); k++){
                if (k == groupPosition) {
                    listDataHeader.get(k).isSelected(true);
                } else {
                    listDataHeader.get(k).isSelected(false);
                }
                if (listDataChild.containsKey(listDataHeader.get(k))) {
                    List<DrawerExpandedMenuModel> childList = listDataChild.get(listDataHeader.get(k));
                    if (childList != null) {
                        for (int j = 0; j < childList.size(); j++) {
                            childList.get(j).isSelected(false);
                        }
                    }
                }
            }
            mMenuAdapter.notifyDataSetChanged();

Переопределить getGroupView и getChildView

public class DrawerExpandableListAdapter extends BaseExpandableListAdapter {
.
.
.
  @Override
public View getGroupView(final int groupPosition, final boolean isExpanded, View convertView, final ViewGroup parent) {
    if (headerTitle.isSelected() == true) convertView.setBackgroundColor(mContext.getResources().getColor(R.color.gray));
    else convertView.setBackgroundColor(mContext.getResources().getColor(R.color.transparent));
.
.
.
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
    if (childObj.isSelected() == true) convertView.setBackgroundColor(mContext.getResources().getColor(R.color.gray));
    else convertView.setBackgroundColor(mContext.getResources().getColor(R.color.transparent));

Что это!