James Meade
James Meade

Reputation: 1169

ExpandableListView has wrong groupPosition when trying to display child view in Android

I am having an issue that I have been working on for a while. I am using an ExpandableListView with a ViewHolder. I know that the ExpandableListView reuses Views and that is what I think is happening with the problem that I have.

I have 3 views in the list and if I select 1 then 2 then 3, then everything is fine and it displays the correct child view. Although when I select 3 first, all of the other child views are the same with the same groupPosition. If I select 2 first, then 3 is correct, but 1 displays the third child view.

I want them to be all fixed to the correct groupPosition so that they don't change.

Here is my adapter for the ExpandableListView:

public class MenuListAdapter extends BaseExpandableListAdapter 
{
private Context context;
private ArrayList<ListData> listData = new ArrayList<ListData>();

/**
 * Constructor for the class.
 * 
 * @param refContext This is a reference to the context of the ExpandableListAdapter being used in.
 * @param refListData This is a reference to the ArrayList of ListData that will display the item data of the menu in the list.
 */
public MenuListAdapter(Context refContext, ArrayList<ListData> refListData)
{
    context = refContext;
    listData = refListData;
}

private static class ViewHolder
{
    TextView title;
    TextView description;
    ImageView image;
    ImageButton addItem;
}

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

@Override
public int getChildrenCount(int groupPosition) 
{
    return 1; // There will only ever be 1 child in the group.
}

@Override
public Object getGroup(int groupPosition) 
{
    return groupPosition;
}

@Override
public Object getChild(int groupPosition, int childPosition) 
{
    return null;
}

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

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

@Override
public boolean hasStableIds() 
{
    return true; // Stops child views from resetting
}

@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) 
{
    final ViewHolder holder;

    if(convertView == null)
    {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(R.layout.listview_item, parent, false);

        holder = new ViewHolder();
        holder.title = (TextView) convertView.findViewById(R.id.titleTextView);
        holder.image = (ImageView) convertView.findViewById(R.id.imageImageView);
        holder.addItem = (ImageButton) convertView.findViewById(R.id.addItemImageButton);

        /* The contents of the item View is set for every View in the list. */
        holder.title.setText(listData.get(groupPosition).getTitle());
        holder.image.setImageBitmap(listData.get(groupPosition).getImageBitmap());
        holder.image.setTag(listData.get(groupPosition).getImageRes()); // Sets tag for image resource.

        holder.image.setFocusable(false);// Enables the group view to be in focus in order to still be able to select the image and group.

        holder.image.setOnClickListener(new OnClickListener() 
        {       
            @Override
            public void onClick(View v) 
            {
                Integer imageResource = (Integer) holder.image.getTag(); // Get image resource from tag.
                int resource = imageResource.intValue(); // Creates primitive image resource from object Integer.

                Intent imageIntent = new Intent(context, ImageActivity.class);
                imageIntent.putExtra("image_resource", resource); // Adds image resource as Extra to Intent.
                context.startActivity(imageIntent);
            }
        });

        holder.addItem.setFocusable(false); // Enables the group view to be in focus in order to still be able to select the button and group.

        holder.addItem.setOnClickListener(new OnClickListener() 
        {
            @Override
            public void onClick(View v) 
            {   
                /* AlertDialog that allows the user to select how many items they want of the item they selected. */
                AlertDialog.Builder alertBuilder = new AlertDialog.Builder(context);
                AlertDialog alertDialog;
                LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                View customView = layoutInflater.inflate(R.layout.item_quantity, null);

                final NumberPicker quantityPicker = (NumberPicker) customView.findViewById(R.id.quantityPicker);
                quantityPicker.setMinValue(1);
                quantityPicker.setMaxValue(10);
                quantityPicker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS); // Stops keyboard from appearing.

                alertBuilder.setTitle("Select quantity");
                alertBuilder.setView(customView);
                alertBuilder.setPositiveButton("Confirm", new DialogInterface.OnClickListener() 
                {
                    public void onClick(DialogInterface dialog, int id) 
                    {
                        int quantityAmount = quantityPicker.getValue(); // Gets value from NumberPicker.

                        /* Update checkout data. */
                        MainActivity mainActivity = (MainActivity) context;
                        mainActivity.updateCheckout(holder.title.getText().toString(), quantityAmount); // Updates checkout with added order.

                        dialog.cancel(); // Cancels AlertDialog.

                        Toast.makeText(context, "Order added to checkout!", Toast.LENGTH_SHORT).show();
                    }
                });

                alertBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() 
                {

                    @Override
                    public void onClick(DialogInterface dialog, int which) 
                    {
                        dialog.cancel(); // Cancels AlertDialog.
                    }
                });

                alertDialog = alertBuilder.create(); // Creates the AlertDialog View.
                alertDialog.show(); // Shows the AlertDialog View.
            }

        });

        convertView.setTag(holder);
    }
    else
    {
        holder = (ViewHolder) convertView.getTag();
    }

    return convertView;
}

@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) 
{
    final ViewHolder holder;

    if(convertView == null)
    {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(R.layout.listview_desc_item, parent, false);

        holder = new ViewHolder();
        holder.description = (TextView) convertView.findViewById(R.id.descriptionTextView);

        holder.description.setText(listData.get(groupPosition).getDescription()); // Sets description of item View.

        System.out.println("Group position: " + groupPosition);
        System.out.println("Child position: " + childPosition);

        convertView.setTag(holder);
    }
    else
    {
        holder = (ViewHolder) convertView.getTag();
    }

    return convertView;
}

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

}

Please can someone help me with this issue.

Upvotes: 5

Views: 3904

Answers (2)

Tomer Shemesh
Tomer Shemesh

Reputation: 13345

the issue is the view recycling and you are only updating the view the first time you inflate the view try this

if(convertView == null)
{
    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    convertView = inflater.inflate(R.layout.listview_desc_item, parent, false);

    holder = new ViewHolder();


    convertView.setTag(holder);
}


    holder = (ViewHolder) convertView.getTag();
    holder.description = (TextView) convertView.findViewById(R.id.descriptionTextView);

    holder.description.setText(listData.get(groupPosition).getDescription());
    System.out.println("Group position: " + groupPosition);
    System.out.println("Child position: " + childPosition);

Upvotes: 4

Emmanuel
Emmanuel

Reputation: 13223

There are several issues:

You are returning null for getChild().

You are returning the group position instead of the Object that backs that group.

getChildrenCount() returns 1. This will only return one item for the child. You should return the total children count.

If you look at the getChild() gives you the parent and child position. You should use a data structure that adapts to that pattern.

Here is an example of how I implemented an ExpandableListView some time ago. It can be better. For example, passing the Context to the constructor is not needed.

Upvotes: 0

Related Questions