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 состояние не активируется после нажатия дочернего элемента.
Любая помощь приветствуется. Спасибо!
Ответы
Ответ 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));
Что это!