【问题标题】:Android ExpandableListView populated twiceAndroid ExpandableListView 填充了两次
【发布时间】:2014-12-16 00:02:52
【问题描述】:

我正在使用可展开的列表视图在菜单中分别显示选项和子选项。传入的数据是正确的,但奇怪的是,listview 似乎为每个数据条目调用了两次 getChild 和 getChildView,因此它被添加到视图中两次。它迭代,然后似乎再次迭代。

例子:

分类: -玩具 -电子产品 -汽车 -玩具 -电子产品 -汽车

预期: 分类: -玩具 -电子产品 -汽车

我对 Android 和 Java 非常陌生,所以我可能犯了一个新手错误。我已经逐步尝试找到会复制数据的点,但我能找到的只是 getChild/getChildView 被调用了两次。

这里是 expandableListView 适配器类:

    public class ExpandableListAdapter extends BaseExpandableListAdapter {
    private Context context;
    private ArrayList<String> filterHeaderList;
    // child data in format of header title, child title
    private HashMap<String, ArrayList<JSONObject>> filterOptionsMap;

    public ExpandableListAdapter(Context context, ArrayList<String> filterHeaders, HashMap<String, ArrayList<JSONObject>> filterChildListMap) {
        this.context = context;
        this.filterHeaderList = filterHeaders;
        this.filterOptionsMap = filterChildListMap;
    }

    @Override
    public String[] getChild(int groupPosition, int childPosititon) {
        JSONObject childObject = this.filterOptionsMap.get(this.filterHeaderList.get(groupPosition)).get(childPosititon);
        if (childObject != null) {
            Iterator<?> it = childObject.keys();
            String key = null;
            String value = null;
            if (it.hasNext()) {
                key = it.next().toString();
                try {
                    value = childObject.getString(key);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                String[] data = new String[2];
                data[0] = key;
                data[1] = value;
                return data;
            } else {
                return null;
            }
        }
        return null;
    }

    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return childPosition;
    }

    @Override
    public View getChildView(int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        String childNum = null;
        String childText = null;
        final String[] childData = (String[]) getChild(groupPosition, childPosition);
        if (childData != null) {
            childNum = childData[0];
            childText = childData[1];
        }
        if (convertView == null) {
            LayoutInflater infalInflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = infalInflater.inflate(R.layout.ps_filter_sort_filter_expandable_child, null);
        }

        TextView tvChildTitle = (TextView) convertView.findViewById(R.id.ps_filter_sort_filter_expandable_child_title);

        tvChildTitle.setText(childText);
        tvChildTitle.setTag(R.id.ps_filter_sort_filter_expandable_child_title + groupPosition + childPosition, childNum);
        return convertView;
    }

    @Override
    public int getChildrenCount(int groupPosition) {
        return this.filterOptionsMap.get(this.filterHeaderList.get(groupPosition)).size();
    }

    @Override
    public String getGroup(int groupPosition) {
        return this.filterHeaderList.get(groupPosition);
    }

    @Override
    public int getGroupCount() {
        return this.filterHeaderList.size();
    }

    @Override
    public long getGroupId(int groupPosition) {
        return groupPosition;
    }

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        String headerTitle = (String) getGroup(groupPosition);
        if (convertView == null) {
            LayoutInflater infalInflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = infalInflater.inflate(R.layout.ps_filter_sort_filter_expandable_parent, null);
        }

        TextView tvGroupTitle = (TextView) convertView.findViewById(R.id.ps_filter_sort_filter_expandable_parent_title);
        tvGroupTitle.setTypeface(null, Typeface.BOLD);
        tvGroupTitle.setText(headerTitle);

        return convertView;
    }

    @Override
    public boolean hasStableIds() {
        return false;
    }

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return true;
    }
}

然后我这样称呼它:

filterListAdapter = new ExpandableListAdapter(this.getActivity(), filterHeaderList, filterOptionsMap);
        // setting list adapter
        expListView.setAdapter(filterListAdapter);
        filterListAdapter.notifyDataSetChanged();

以这种方式组织数据是有原因的,我不想深入探讨。我能说的是HashMap“filterOptionsMap”是正确的,它或“filterHeaderList”没有重复

任何帮助将不胜感激。我找不到遇到类似问题的人,为此我一直在敲键盘。

谢谢。

附注 您还需要点击组标题两次以使其折叠,但要打开一次。我不知道这是否是相关的或预期的行为,但我希望它是一个点击来切换它打开或关闭。在这方面的帮助也可以加分。

这里要求的是一些与 HashMap 相关的附加代码:

private void prepareFilterListData(ArrayList<String> headerList, JSONObject[] categoryList, JSONObject[] sellerList, JSONObject[] typeList) {
    filterHeaderArrayList = headerList;
    filterOptionsMap = new HashMap<String, ArrayList<JSONObject>>();

    // Adding child data
    ArrayList<JSONObject> categories = new ArrayList<JSONObject>();
    if (categoryList != null && categoryList.length > 0) {
        for (JSONObject category : categoryList) {
            categories.add(category);
        }
    }

    ArrayList<JSONObject> sellers = new ArrayList<JSONObject>();
    if (sellerList != null && sellerList.length > 0) {
        for (JSONObject seller : sellerList) {
            sellers.add(seller);
        }
    }

    ArrayList<JSONObject> types = new ArrayList<JSONObject>();
    if (typeList != null && typeList.length > 0) {
        for (JSONObject type : typeList) {
            types.add(type);
        }
    }

    filterOptionsMap.put(filterHeaderArrayList.get(0), categories);
    filterOptionsMap.put(filterHeaderArrayList.get(1), sellers);
    filterOptionsMap.put(filterHeaderArrayList.get(2), types);
}


    filterHeaderList = Directory.getFilterOptions();
    filterCategoryList = Directory.getFilterCategories();
    filterSellerList = Directory.getFilterSellers();
    filterTypeList = Directory.getFilterTypes();

    // Forms data structures from given input
    if (filterHeaderList != null && filterCategoryList != null && filterSellerList != null && filterTypeList != null) {
        prepareFilterListData(filterHeaderList, filterCategoryList, filterSellerList, filterTypeList);


private JSONObject[] getFilterTypes(JSONObject productSearchSettings, String filterTypes) {
    JSONArray typesObj;
    JSONObject[] types;
    try {
        typesObj = productSearchSettings.optJSONArray(filterTypes);
        if (typesObj != null) {
            types = new JSONObject[typesObj.length()];
            for (int i = 0; i < typesObj.length(); i++) {
                String key = "type" + String.valueOf(i);
                String val = (String) typesObj.get(i).toString();
                types[i] = new JSONObject().put(key, val);
            }
            return types;
        } else {
            return null;
        }
    } catch (JSONException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return null;
}

private JSONObject[] getFilterSellers(JSONObject productSearchSettings, String filterSellers) {
    JSONObject sellersObj = null;
    JSONObject[] sellers;
    try {
        sellersObj = productSearchSettings.getJSONObject(filterSellers);
        if (sellersObj != null) {
            sellers = new JSONObject[sellersObj.length()];
            Iterator<?> it = sellersObj.keys();
            int i = 0;
            while (it.hasNext()) {
                String key = (String) String.valueOf(it.next().toString());
                String val = sellersObj.getString(key);
                sellers[i] = new JSONObject().put(key, val);
                i++;
            }
            return sellers;
        } else {
            return null;
        }
    } catch (JSONException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return null;
}

private JSONObject[] getFilterCategories(JSONObject productSearchSettings, String filterCategories) {
    JSONObject categoriesObj = null;
    JSONObject[] categories;
    try {
        categoriesObj = productSearchSettings.getJSONObject(filterCategories);
        if (categoriesObj != null) {
            categories = new JSONObject[categoriesObj.length()];
            Iterator<?> it = categoriesObj.keys();
            int i = 0;
            while (it.hasNext()) {
                String key = (String) String.valueOf(it.next().toString());
                String val = categoriesObj.getString(key);
                categories[i] = new JSONObject().put(key, val);
                i++;
            }
            return categories;
        } else {
            return null;
        }
    } catch (JSONException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return null;

}

如前所述,进入适配器的 hashmap 是正确的。我通过调试验证了。

【问题讨论】:

  • 对于ExpandableListAdapter,您的数据组织选择是正常的。看到getChildViewgetGroupView 的多次调用是预期行为。添加两次不是。您的适配器实现似乎没有什么突出的。您可以发布更多代码吗?基本上是与适配器或用于构造适配器的数据交互的任何代码部分。还包括它们所在的方法。
  • 我添加了额外的代码。还可能值得注意的是,我正在尝试为组视图和子视图使用自定义视图,但我认为这些视图中没有任何可能导致重复的内容。

标签: java android json expandablelistview


【解决方案1】:

嗯...很抱歉,我仍然没有看到任何导致这种行为的原因。通常,这可能是由于在不知不觉中添加了新内容造成的。通常是因为从适配器外部修改数据。

在您的情况下,适配器中的filterHeaderListDirectory.getFilterOptions() 返回的列表是相同的列表。因此,如果您修改该Directory.getFilterOptions() 列表,那么您也会无意中修改适配器。当然,该方法可能只是动态生成一个新列表,因此您甚至不必担心。

如果您确定 filterHeaderList 在 getChildView 和 getGroupView 调用期间是正确的,我不确定还有什么问题。自定义组/子视图不会导致这种情况。

我只能建议采取一些更积极的调试措施。基本上开始剥离活动/片段到裸露的骨头。消除尽可能多的变量,以便您只做最简单的情况...加载 ExpandableListView 和带有一些数据的适配器。

您也可以尝试输入一些虚假数据。只需在一组中尝试一个子项目,看看它是否仍然使所有内容翻倍。

【讨论】:

  • 非常感谢您的回答。我会投赞成票,因为这是一个很好的建议,但我想确保如果其他人有这个问题,他们可以立即看到答案。
【解决方案2】:

我终于找到了。非常感谢您的反馈和帮助。

在该组的点击事件中,我没有返回 true 以指示点击已处理。我正在使用点击事件来切换扩展。

我不知道为什么这会导致重复,并且还需要两次单击才能关闭组,但切换返回以返回组上的点击事件就大不相同了。

【讨论】:

  • 不等,我投了赞成票,然后创建了一个返回 true 的 setOnGroupClickListener,但仍然总是调用两次 getChildView。您能否更好地解释一下您是如何修复它的?
  • 您需要确保所有与视图相关的点击处理程序都得到处理。此外,getChildView() 经常在视图滚动时重复调用,等等。这是无法避免的。我遇到的问题是视图实际上被填充了两次。 getChildView() 被调用了两次,但它不会在下次调用时替换单个孩子,而是实际上会创建一个新孩子。我会比正常的孩子多一倍。和你遇到的问题一样吗?
  • 我的问题是,每次我单击一个组时,调试时,我看到所有可见组都调用了两次 getGroupView,扩展组调用了两次 getChildView。我的问题是我有一个复杂的列表,其中所有组都是不同类型的;一个包含一张图片,另外 3 个按钮,一个搜索栏等......所以 getChildView 每次都会设置两次图片,这并不好。昨天我已经“修复它”,每次只允许一个扩展组。如果我单击一个组,我会折叠所有其他组。
【解决方案3】:

我确信缺少"return true" 可能是您系统中的原因,但由于它对我不起作用,因此我找到了原因添加了一个答案:

ExpandableListView doc:

注意:您不能将值 wrap_content 用于 XML 中 ExpandableListView 的 android:layout_height 属性,如果 父母的大小也没有严格规定(例如,如果 父级是 ScrollView 你不能指定 wrap_content 因为它 也可以是任意长度。但是,您可以使用 wrap_content 如果 ExpandableListView 父级具有特定大小,例如 100 像素。

我的 ExpandableListView 实际上在带有 height="wrap_content" 的 LinearLayout 中。将其设置为match_parent,瞧,问题解决了。

我不是 100% 确定,但可能是由于未定义高度,getChildView 被调用了两次,因为父级的大小调整。

并且可能(我知道这是出于个人经验,在测试期间更改几个参数通常不是一个好主意,因为它会造成对原因的误解)您还可以在添加 @987654326 时将高度设置为 match_parent @ 并认为解决方案是后者。

如果您可以取消设置 ExpandableListView 容器的高度或将返回值设置为 false 并查看真正的原因,这将对所有人都有帮助。如果两者兼而有之,那么我们找到了 2 个原因。

【讨论】:

  • 很好的洞察力。谢谢。
  • 嘿,我知道这不是您真正想到的,但它帮助我在我的组中使用复选框(标题)。 Android 正在创建新的引用(并创建了一个新的 ViewHolder),所以我永远无法找到我检查 CheckBox 的位置。将布局更改为 match_parent 解决了这个问题(仍然不确定为什么)。无论如何,非常感谢!
  • 非常感谢你的朋友你的回答对我帮助很大,因为这个问题我被卡住了
【解决方案4】:

这对我有用:在 onGroupClick 中,删除对 parent.expandGroup(groupPosition) 的调用,并使其返回 false;

expandableListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
        @Override
        public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
            parent.expandGroup(groupPosition); // delete this
            Log.d(TAG, "onGroupClick: ");
            return false;
        }
    });

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-15
    • 2012-08-30
    • 2012-08-28
    相关资源
    最近更新 更多