【问题标题】:ExpandableListView - keep selected child in a "pressed" stateExpandableListView - 将选定的子项保持在“按下”状态
【发布时间】:2012-01-09 02:14:28
【问题描述】:

我在平板电脑屏幕的左侧导航中使用 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" />

listView 项被按下后,listView 项上的“state_activiated”为真。

我希望同样的技术适用于我的 expandableListView,但它没有。我用过:

getExpandableListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);

然后使用上面相同的选择器,但它不起作用。我还尝试了其他状态,例如 state_selected 和 state_checked,但它们也不起作用。

上面的选择器正确地应用了按下和未按下状态。然而,看起来使用 ExpandableListView,按下孩子后状态不会“激活”。

感谢任何帮助。谢谢!

【问题讨论】:

  • 下面我的回答有帮助吗?您没有将其标记为已接受答案是否有原因?
  • 是的,非常有帮助,谢谢(对不起,我忘了投票)。我将推迟将其标记为已接受的答案,以防有人分享一种本土的方式来达到我正在寻找的效果。再次感谢!

标签: android android-widget


【解决方案1】:

在 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 步。 在 onChildClick() 回调中调用 expandableListView.setItemChecked(index,true)

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;
}

【讨论】:

  • 如果您折叠包含所选项目的组并打开另一个组,对我来说似乎有一些奇怪的结果
  • 它在单级可扩展列表视图中运行良好,那么多级可扩展列表视图呢?
  • 在选择另一个组中的项目时,其他组的项目也被选中..
【解决方案2】:

我最终没有在子视图上使用选择器就解决了这个问题。 expandableListView 的适配器为每个组和子项获取数据。我为子数据添加了一个“选定的”布尔值。该片段负责正确地将每个孩子的选定布尔值设置为真或假。然后,在适配器的“getChildView”方法中,如果孩子的“选择”布尔值为真,我将视图的背景资源设置为我的“按下”背景。否则,我将其设置为“未按下”背景。除了在子视图上使用 textView 的选择器以在按下时更改文本颜色之外,这样做可以达到预期的效果。

以下是我的 Fragment 为维护孩子的“选定”布尔值所做的事情:

  • 将您的组/子数据作为类变量保留在您的片段中
  • onChildClick - 循环遍历子数据,设置所有“选定”= false。然后将当前组/子项的子项设置为 true。通过调用 setEmptyView 刷新 expandableListView,重置监听器,设置列表适配器。
  • onGroupClick - 循环遍历子数据,设置所有“选定”= false。然后将当前 group/child 的 child 设置为 true。
  • 在片段中显示列表的任何位置 - 填充适配器数据,设置列表适配器,在适配器上调用 notifyDataSetChanged。通过调用 setEmptyView 刷新 expandableListView,重置监听器,设置列表适配器。

除了上述之外,我还维护了一个 currentGroupPosition 和 currentChildPosition 作为类变量。使用 setRetainInstance = true 可以让它在屏幕旋转等情况下正常工作。

【讨论】:

  • 我相信调用notifyDataSetChanged 就足够了……为什么需要调用setEmptyView 来刷新视图?
【解决方案3】:

我认为没有任何内置方法可以使用内置 Android 功能来完成您尝试的操作。为了进行一些测试,我从 Android API 示例 (C:\&lt;android-sdk-directory&gt;\samples\android-15\ApiDemos\src\com\example\android\apis\view\ExpandableList3.java) 中的代码开始。

我使用了ExpandableListView.CHOICE_MODE_SINGLE。选择模式适用于选中或选择的任何View。 (ExpandableListView 源代码说它决定了一次可以选择多少项,但在ListView 中,它似乎适用于选中。)请注意,文档说在单选模式下,您无法从 getCheckedItemPosition 获得有效的结果以获取 ListView。这似乎是真的。

我的测试表明ExpandableListActivity 不会自动选中或选中被点击/触摸的项目,也不会自动将焦点放在被点击的子项上。只有 ExpandableListActivityitself 和持有该子项的组在您单击其中一个子项后会自动聚焦。

这是我的一些代码(大部分来自 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      

请注意最后一行,它强制点击的孩子处于选中状态。然后,您可以使用带有 Drawable 状态列表的 XML selector 来突出显示选中的项目。 (这并不完全有效 - 更多内容如下。)

在我的ExpandableListActivity 中的onChildClick 内部,我做了一些测试,发现如下:

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 可以在ExpandableListViews 上以某种方式工作,但我从未见过它被调用过。

我尝试更改所选子项的背景颜色并使其突出显示的第一件事效果不佳。我在selector XML 文件中为android:state_window_focused="true" 设置了一个Drawable。它有点工作,但出现了几个问题。

我尝试的第二件事是通过调用viewClicked.setSelected(true); 明确选择onChildClick 中按下的项目。这里的问题更少:

1)如果我滚动直到突出显示的项目不在视线范围内并返回,突出显示消失了,2)如果我旋转手机也会发生同样的情况,3)突出显示的项目的状态不再当它在显示屏上不再可见时立即被选中。

Android 的ExpandableListView 显然不应该像手风琴式的左侧导航菜单那样工作,尽管这似乎是一个相当明显的用途。如果您想以这种方式使用它,我认为您必须进行大量自定义并深入研究Views 的绘制方式。

祝你好运。

【讨论】:

  • 换一个灯泡需要多少Android开发团队工程师?没有,因为他们决定不支持更换灯泡,所以你必须自己更换。
【解决方案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;
    .
    .
    .
    

    覆盖 ExpandableListView 的 onChildClick

            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();
    

    覆盖 ExpandableListView 的 onGroupClick

            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));
    

    就是这样!

    【讨论】:

    • 我认为这是一个更好的解决方案,在尝试了不同的其他解决方案之后。谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-27
    • 1970-01-01
    相关资源
    最近更新 更多