Search code examples
androidgarbage-collectionlistadapter

Best way to do getView on ListAdapter to minimize garbage collection


I've made a CustomListAdapater which works fine, however, I get quite a few garbage collection messages popping up when I scroll through the list quickly.

Is there a best practice to making a efficient custom ListAdapter?

Here's what I have t the moment what works, but isn't great.

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    RelativeLayout centreView;
    //Get the current alert object

    if (convertView == null) {
        centreView = new RelativeLayout(getContext());
        LayoutInflater vi;
        vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        vi.inflate(resource, centreView, true);
    }
    else
    {
        centreView = (RelativeLayout) convertView;
    }

    TextView name = (TextView) centreView.findViewById(R.id.class_name);
    TextView venue = (TextView) centreView.findViewById(R.id.class_venue);
    TextView time = (TextView) centreView.findViewById(R.id.class_time);
    TextView room = (TextView) centreView.findViewById(R.id.class_room);
    TextView id = (TextView) centreView.findViewById(R.id.class_id);
    TextView dayTV = (TextView) centreView.findViewById(R.id.class_day);
    TextView desc = (TextView) centreView.findViewById(R.id.class_description);
    TextView json = (TextView) centreView.findViewById(R.id.class_json);

    String timeString;
    try {
        JSONObject thisRow = items.get(position);
        name.setText(thisRow.getString("className"));

        dayTV.setText(thisRow.getString("theDate"));
        json.setText(thisRow.toString());
        venue.setText(thisRow.getString("venue"));
        final String ID = thisRow.getString("id");
        id.setText(ID);


        desc.setText(thisRow.getString("description"));


        timeString = thisRow.getString("startTime");
        timeString += " - "+thisRow.getString("endTime");
        time.setText(timeString);



        room.setText(thisRow.getString("room"));


        final Button alter = (Button) centreView.findViewById(R.id.is_alt);
        alter.setVisibility(View.INVISIBLE);
        final JSONArray alterations = thisRow.getJSONArray("alteration");

        if (alterations.getJSONObject(0).getString("show").equals("yes")) {
            alter.setVisibility(View.VISIBLE);
            alter.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    ActivityViewTimetable actVT = (ActivityViewTimetable) getContext();
                    actVT.showAlterations(alterations);
                }
            });
        }


    } catch (JSONException e) {
        Log.e("FUApp", "JSON Error (TimetableItemsArrayAdapter): " + e.getMessage());
    }

    return centreView;
}

PS. Some of the TextView i'm referencing arn't needed anymore, I'm just sorting out which ones at the moment.

EDIT: This is what I've ended up with

static class ViewHolder {
    TextView name;
    TextView venue;
    TextView time;
    TextView room;
    TextView id;
    TextView dayTV;
    TextView desc;
    TextView json;
    JSONObject jsonObj;
    String classNameString;
    String theDate;
    String venueString;
    String idString;
    String description;
    String startTime;
    String endTime;
    String roomString;
    JSONArray alterations;
}

and

 if (convertView == null) {
        centreView = new RelativeLayout(getContext());
        LayoutInflater vi;
        vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        vi.inflate(resource, centreView, true);
        holder = new ViewHolder();
        holder.name = (TextView) centreView.findViewById(R.id.class_name);
        holder.venue = (TextView) centreView.findViewById(R.id.class_venue);
        holder.time = (TextView) centreView.findViewById(R.id.class_time);
        holder.room = (TextView) centreView.findViewById(R.id.class_room);
        holder.id = (TextView) centreView.findViewById(R.id.class_id);
        holder.dayTV = (TextView) centreView.findViewById(R.id.class_day);
        holder.desc = (TextView) centreView.findViewById(R.id.class_description);
        holder.json = (TextView) centreView.findViewById(R.id.class_json);
        holder.jsonObj = items.get(position);
        try {
            holder.classNameString = holder.jsonObj.getString("className");
            holder.theDate = holder.jsonObj.getString("theDate");
            holder.venueString = holder.jsonObj.getString("venue");
            holder.idString = holder.jsonObj.getString("id");
            holder.description = holder.jsonObj.getString("description");
            holder.startTime = holder.jsonObj.getString("startTime");
            holder.endTime = holder.jsonObj.getString("endTime");
            holder.roomString = holder.jsonObj.getString("room");
            holder.alterations = holder.jsonObj.getJSONArray("alteration");
        }
        catch (JSONException e) {
            Log.e("FUApp", "JSONError getting details: "+e.getMessage());
        }
        centreView.setTag(holder);
    }

Using this my list doesn't lag at all.


Solution

  • Use a View Holder

    Your code might call findViewById() frequently during the scrolling of ListView, which can slow down performance. Even when the Adapter returns an inflated view for recycling, you still need to look up the elements and update them. A way around repeated use of findViewById() is to use the "view holder" design pattern.

    http://developer.android.com/training/improving-layouts/smooth-scrolling.html

    You can initialize Layout Infalter in the constructor of your Adapter class

    Edit:

     ViewHolder holder;
     if (convertView == null) {
        holder = new ViewHolder();
        convertView = inflater.inflate(R.layout.mylayouttoinflate, null);
        // initialize views like holder.text = (TextView)convertView.findViewByID(R.id.text);
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }
      // set text to text view
      holder.text.setText("hi");
     return convertView;