1

I am wriring an Android app, and I decided to use expandable list view. I have everything I need, list is working fine, but when I populate it, groups appear to be in different order than they should, here's an example: Unexpanded

Expanded

As you can see, child values are fine, only parent values appear in random order. Here's the full code I have:

My fragment code:

public class BuildingsFragment extends Fragment {


public BuildingsFragment() {
    // Required empty public constructor
}


public static BuildingsFragment newInstance(Context context) {
    BuildingsFragment fragment = new BuildingsFragment();

    return fragment;
}


@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);


}


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View view = inflater.inflate(R.layout.fragment_buildings, container, false);


    final ExpandableListView expandableListView = (ExpandableListView) view.findViewById(R.id.expandableListViewBuildings);
    HashMap<String, List<String>> expandableListDetail = ExpandableListDataPump.getData();
    List<String> expandableListTitle = new ArrayList<String>(expandableListDetail.keySet());
    ExpandableListAdapter expandableListAdapter = new CustomExpandableListAdapter(getContext(), expandableListTitle, expandableListDetail);
    expandableListView.setAdapter(expandableListAdapter);


    expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
        @Override
        public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {


            Fragment fragment = null;
            Class fragmentClass = BuildingDetailsFragment.class;



            try {
                fragment = (Fragment) fragmentClass.newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }

            FragmentTransaction trans = getFragmentManager().beginTransaction();


            Bundle args = new Bundle();
            args.putString("BUILDING_NAME", "test");
            fragment.setArguments(args);


            trans.replace(R.id.frameLayoutForFragments, fragment);


            trans.addToBackStack(null);


            trans.commit();


            return false;
        }
    });


    return view;
}
}

My custom adapter:

public class CustomExpandableListAdapter extends BaseExpandableListAdapter {

private Context context;
private List<String> expandableListTitle;
private HashMap<String, List<String>> expandableListDetail;

public CustomExpandableListAdapter(Context context, List<String> expandableListTitle, HashMap<String, List<String>> expandableListDetail) {
    this.context = context;
    this.expandableListTitle = expandableListTitle;
    this.expandableListDetail = expandableListDetail;
}

@Override
public Object getChild(int listPosition, int expandedListPosition) {
    return this.expandableListDetail.get(this.expandableListTitle.get(listPosition))
            .get(expandedListPosition);
}

@Override
public long getChildId(int listPosition, int expandedListPosition) {
    return expandedListPosition;
}

@Override
public View getChildView(int listPosition, final int expandedListPosition,
                         boolean isLastChild, View convertView, ViewGroup parent) {
    final String expandedListText = (String) getChild(listPosition, expandedListPosition);
    if (convertView == null) {
        LayoutInflater layoutInflater = (LayoutInflater) this.context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = layoutInflater.inflate(R.layout.list_view_buildings_row_layout, null);
    }
    TextView expandedListTextView = (TextView) convertView
            .findViewById(R.id.expandedListItem);
    expandedListTextView.setText(expandedListText);
    return convertView;
}

@Override
public int getChildrenCount(int listPosition) {
    return this.expandableListDetail.get(this.expandableListTitle.get(listPosition))
            .size();
}

@Override
public Object getGroup(int listPosition) {
    return this.expandableListTitle.get(listPosition);
}

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

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

@Override
public View getGroupView(int listPosition, boolean isExpanded,
                         View convertView, ViewGroup parent) {
    String listTitle = (String) getGroup(listPosition);
    if (convertView == null) {
        LayoutInflater layoutInflater = (LayoutInflater) this.context.
                getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = layoutInflater.inflate(R.layout.list_view_buildings_group_layout, null);
    }
    TextView listTitleTextView = (TextView) convertView
            .findViewById(R.id.listGroup);
    listTitleTextView.setTypeface(null, Typeface.BOLD);
    listTitleTextView.setText(listTitle);
    return convertView;
}

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

@Override
public boolean isChildSelectable(int listPosition, int expandedListPosition) {
    return true;
}
}

The ExpandableListDataPump I am using:

public class ExpandableListDataPump {
public static HashMap<String, List<String>> getData() {

    HashMap<String, List<String>> expandableListDetail = new HashMap<String, List<String>>();


    List<String> abuildings = new ArrayList<String>();
    abuildings.add("A-1");
    abuildings.add("A-2");
    abuildings.add("A-3");
    abuildings.add("A-5");
    abuildings.add("A-7");
    expandableListDetail.put("A", abuildings);

    List<String> bbuildings = new ArrayList<String>();
    bbuildings.add("B-1");
    bbuildings.add("B-2");
    bbuildings.add("B-3");
    bbuildings.add("B-5");
    bbuildings.add("B-6");
    expandableListDetail.put("B", bbuildings);

    List<String> cbuildings = new ArrayList<String>();
    cbuildings.add("C-1");
    cbuildings.add("C-3");
    cbuildings.add("C-4");
    cbuildings.add("C-5");
    cbuildings.add("C-6");
    expandableListDetail.put("C", cbuildings);

    List<String> dbuildings = new ArrayList<String>();
    dbuildings.add("D-1");
    dbuildings.add("D-2");
    dbuildings.add("D-3");
    dbuildings.add("D-5");
    dbuildings.add("D-7");
    expandableListDetail.put("D", dbuildings);

    return expandableListDetail;
}
}

I think that's everything related to ExpandableListView. I would really like to leave HashMap, but if there is no other way, can I just change it to TreeMap and use it's sorting function (as a last resort)? Thanks for help.

P.S. While doing my ExpandableListView I was following this tutorial (I don't know if that matters): Tutorial

ikurek
  • 604
  • 11
  • 27

2 Answers2

5

This is because HashMap doesn't maintain the order of the items as those are inserted. So everywhere in your code (Activity & Adapter) change HashMap to LinkedHashmap. Like

  private LinkedHashMap<String, List<String>> expandableListDetail;
Asad Ali Choudhry
  • 4,985
  • 4
  • 31
  • 36
1

According to the Java Specs for HashMap, the order is not guaranteed:

...This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time...

So, if you want to guarantee the order of your groups, you'll have to use a different data structure.

See this answer for a similar discussion, and the suggested usage of LinkedHashMap, which maintains insertion order.

Community
  • 1
  • 1
austinbruch
  • 323
  • 2
  • 14
  • Do I need to change anything else in my code, or can I just replace all "HashMap" with "LinkedHashMap"? – ikurek Aug 29 '16 at 06:04
  • You can just replace all "HashMap" with "LinkedHashMap". You could also abstract a bit, and replace your HashMap declarations with Map. That way, you'd only be changing the implementation type, should you change to TreeMap, or something else, in the future. Additionally, according to the documentation, LinkedHashMap is less expensive than TreeMap (which is what you said is your last resort). – austinbruch Aug 29 '16 at 23:38