Make my Custom ArrayAdapter ListView expandable?

1.2k views Asked by At

I have a custom ListView that I fill with my custom ArrayAdapter. I read and parse json data from URLs and fill the ListView with this. It works perfectly when I read the first file. Now I want to be able to add more data (older posts) to my ListView, though. I've added a "Load More" button as a footer to the ListView, when I've loaded the new data though and try to scroll through it I get this error:

java.lang.IndexOutOfBoundsException: Invalid index 25, size is 25

At first I didn't understand at all why this was, now I realize it's that my ListView isn't expandable. When I search google for expandable ListViews the custom adapters extend BaseExpandableListAdapter, but I'm not sure how to implement that in my application.. My custom arrayadapter is defined like this:

private class StreamAdapter extends ArrayAdapter<Message>

Message being one of my classes.

This is how I display the data:

private void DisplayData() {
    // If the request was successfull then notify the adapter to display the data
    if(success) {

        if(dataAdapter.isEmpty())
        {
            dataAdapter.addAll(dataList);
            dataAdapter.notifyDataSetChanged();
        }
        // If the listview isn't empty we want to erase what's there and then add the older posts to display
        else
        {
            /** Add currently displayed messages to a temporary arraylist, clear the listview, then add both arraylists in the correct order **/
            int size = messageList.getTop();
            ArrayList<Message> temp = new ArrayList<Message>();

            for(int i = 0; i < size; i++)
            {
                temp.add(dataAdapter.getItem(i));
            }
            dataAdapter.clear();
            temp.addAll(dataList);

            dataAdapter.addAll(temp);
            dataAdapter.notifyDataSetChanged();
        }
    }
}

The whole temp-ArrayList-thing was the only way I could come up with to get the newly loaded data (but oldest posts) at the bottom of the listview instead of at the top.

Is there any way to easily make my ListView expandable or do I have to rewrite my entire StreamAdapter class?

Kristina

EDIT: Here's the code for my StreamAdapter class, I left parts out though as I'm fairly sure it's not relevant and just adds to a bunch of code.

private class StreamAdapter extends ArrayAdapter {

public StreamAdapter(Context context, int textViewResourceId, ArrayList<Message> dataList) 
{
    super(context,textViewResourceId, dataList);
}

private class ViewHolder {
    ImageView thumbnail;
    TextView display_name;
    TextView username;
    TextView created_at;
    TextView text;
    ImageView comments;
    TextView nr_comments;
    @SuppressWarnings("unused")
    ImageView like;
    TextView nr_likes;
    @SuppressWarnings("unused")
    ImageView starred;
    TextView nr_starred;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder = null;

    if(convertView == null)
    {
        LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = vi.inflate(R.layout.data_row, null);


        holder = new ViewHolder();
        holder.thumbnail = (ImageView) convertView.findViewById(R.id.thumbnail);
        holder.display_name = (TextView) convertView.findViewById(R.id.display_name);               
        holder.username = (TextView) convertView.findViewById(R.id.username);
        holder.created_at = (TextView) convertView.findViewById(R.id.created_at);
        holder.text = (TextView) convertView.findViewById(R.id.text);
        holder.comments = (ImageView) convertView.findViewById(R.id.comments);
        holder.nr_comments = (TextView) convertView.findViewById(R.id.nr_comments);
        holder.like = (ImageView) convertView.findViewById(R.id.like);
        holder.nr_likes = (TextView) convertView.findViewById(R.id.nr_likes);
        holder.starred = (ImageView) convertView.findViewById(R.id.starred);
        holder.nr_starred = (TextView) convertView.findViewById(R.id.nr_starred);

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

    final Message data = dataList.get(position);
    holder.thumbnail.setImageDrawable(getResources().getDrawable(R.drawable.owl_thumbnail));
    holder.display_name.setText(data.created_by.getDisplay_name());
    holder.username.setText("@"+data.created_by.getUsername());
    holder.created_at.setText(data.getCreated_at());
    holder.nr_comments.setText(Integer.toString(data.getComments().length));
    holder.nr_likes.setText(Integer.toString(data.getLiked_by().length));
    holder.nr_starred.setText(Integer.toString(data.getStarred_by().length));


    // Code to make certain text in the text-textview clickable

    // Clickhandler for the username (display_name)
    holder.display_name.setOnClickListener(new OnClickListener() {
        // Code
    });

    // Clickhandler for comments
    holder.comments.setOnClickListener(new OnClickListener() {
        //Code
    });

    return convertView;
}

}

3

There are 3 answers

12
vipul mittal On BEST ANSWER

Instead of adding new data to adapter items try getting a reference to your data list from adapter and add this data into it for ex:

private class StreamAdapter extends ArrayAdapter {

private  ArrayList<Message> dataList;
public StreamAdapter(Context context, int textViewResourceId, ArrayList<Message> dataList) 
{
    super(context,textViewResourceId, dataList);
    this.dataList=dataList;
}

public  ArrayList<Message> getDataList(){
   return dataList;
}

public void setDataList(ArrayList<Message> dataList){
   this.dataList=dataList;
}

...
...

}

And in when you update it

private void DisplayData() {
    // If the request was successfull then notify the adapter to display the data
    if(success) {

            ArrayList<Message> temp = dataAdapter.getDataList();

            for(int i = 0; i < dataList.size(); i++)
            {
                temp.add(i,dataList.get(i));
            }

            dataAdapter.notifyDataSetChanged();
        }
}

This should work, try it and let me know.

Also override following function inside adapter

@Override
    public int getCount() {
        return dataList.size();
    }

EDIT

private void DisplayData() {
    // If the request was successfull then notify the adapter to display the data
    if(success) {

            ArrayList<Message> temp = dataAdapter.getDataList();
            dataList.addAll(temp);
            dataAdapter.setDataList(dataList);

            dataAdapter.notifyDataSetChanged();
        }
}

EDIT 2

private void DisplayData() {
    // If the request was successfull then notify the adapter to display the data
    if(success) {

            ArrayList<Message> temp = dataAdapter.getDataList();
            temp.addAll(dataList);
            dataAdapter.notifyDataSetChanged();
        }
}
0
Mohamed_AbdAllah On

Try to replace

int size = messageList.getTop();

with

int size = dataAdapter.getCount();
0
fiipi On

No, you shouldn't use ExpandableListAdapter, and you don't have to provide a new implementation of the adapter. The exception you're getting is probably due to a bad handling of an array, as you would have already guessed. I had a similar problem, but i was using adapters with Cursor (querying a database) so I think that notifydataSetChanged() should be enough for the adapter to reload data and display them after the adapter was filled with the additional data.

ExpandableListAdapter is used when you have to display data in the PARENT -> CHILDREN way, so with ExpandableListView you see a list of parent items, each one with multiple child items, each parent expands/collapses (and thus shows/hides children) when you click on it.