Android ExpandableListView filled twice

I am using an expandable list view to display options and sub-options, respectively, in a menu. The input is correct, but oddly enough, the listview seems to call getChild and getChildView twice for each input, and so it is added to the view twice. It iterates and then seems to make its way again.

Example:

Categories: -Toys -Electronics -Passenger cars -Toys -Electronics -Autos

Expected: Categories: -Toys -Electronics -Autos

I'm really new to Android and Java, so I'm probably wrong. I took a step to try and find the point where it would duplicate data, but all I could find was that getChild / getChildView gets called twice.

Here is the expandableListView adapter class:

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

      

Then I call it like this:

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

      

There are reasons why the data is organized in a way that I would rather not get into. I can tell that HashMap "filterOptionsMap" is correct and there is no duplication or "filterHeaderList"

Any help would be greatly appreciated. I couldn't find someone who had a similar problem and I was already banging my head on the keyboard.

Thank.

PS You also need to double-tap the group header for it to collapse, but once to open it. I don't know if this is related or expected behavior, but I would hope it would be one click to open or close it. Bonus points for helping with this.

As requested, here's some additional HashMap related code:

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;

}

      

As mentioned earlier, however, the hashmap included with the adapter is correct. I checked through debug.

+3


source to share


4 answers


I finally found it. I really appreciate the feedback and your help.

In the click event for the group, I did not return true to indicate that the click was processed. I used the click event to switch the extension.



I don't know why exactly this would cause duplication, nor would it require two clicks to close the group, but toggling that return for a click event on the group did the best it could.

+5


source


I'm sure the downside "return true"

might be the cause on your system, but since it didn't work for me, I'm adding an answer as I found the cause:

ExpandableListView doc:

Note . You cannot use the wrap_content value for android: layout_height of the ExpandableListView attribute in XML unless the parent's size is also strictly defined (for example, if the parent was a ScrollView, you couldn't specify wrap_content, as it can also be of any length. However, you can use wrap_content if the ExpandableListView's Parent is of a specific size, such as 100px.

My ExpandableListView was actually inside a LinearLayout with height="wrap_content"

. Install it on match_parent

and voila, the problem is solved.



I'm not 100% sure, but it might be that with undefined height, getChildView gets called twice due to re-sizing of the parent.

And it could be (I know this for personal experience, changing a few parameters during testing is generally not a good idea as it creates misunderstandings in the reasons) that you also set the height to match_parent

, while you added return true

and thought the solution was was the last one.

It would be helpful for everyone if you could just turn off the container height of your ExpandableListView OR set the return to false

and see which one is the real cause. If both, we found 2 reasons.

+3


source


Hmmm ... Sorry I still can't see anything to trigger this behavior. Typically, this can be caused by unknowingly adding new content. Usually because one of them is modifying data from the adapter.

In your case, filterHeaderList

in the adapter, and the list returned Directory.getFilterOptions()

is the same list. Therefore, if you change this list Directory.getFilterOptions()

, you also change the adapter unintentionally. Of course, this method might just generate a new list on the fly, so it might not even bother you.

If you are sure that it is filterHeaderList

correct during the getChildView and getGroupView calls, I am not sure what else might be wrong. Custom group / child views would not have caused this.

All I can suggest is resorting to more aggressive debugging. Basically, start shooting this Activity / Fragment from the bare bone. Eliminate as many variables as possible so you just make the simplest case ... loading the ExpandableListView and adapter with some data.

You can also try submitting some fake data. Just try one child with one group to make sure it still doubles everything.

0


source


this worked for me: inside onGroupClick remove the call to parent.expandGroup (groupPosition) and return 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;
        }
    });

      

0


source







All Articles