0

I've followed this, this and this to make my ListView have the best performance possible. Actually, following the third link I substancially improved the smoothness of scrolling.

The thing is that I'm using a customized layout for each row which contains 2 ImageViews (one of them unchanging, and the second one with two possible assignations of drawables), and two TextViews, so this is the definition:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/elem_list"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/chanlist_featured"
        android:layout_width="20dp"
        android:layout_height="30dp"
        android:layout_marginTop="8dp"
        android:contentDescription="@string/desc_gen_image"
        android:gravity="center" />

    <TextView
        android:id="@+id/chanlist_chan"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="left"
        android:layout_weight="13"
        android:ellipsize="marquee"
        android:maxLines="2"
        android:padding="10dp" />

    <LinearLayout 
      android:layout_width="40dp"
      android:layout_height="match_parent"
      android:orientation="vertical">

      <TextView
        android:id="@+id/chanlist_usercount"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:paddingTop="10dp"
        android:ellipsize="marquee"
        android:gravity="center"
        android:maxLines="2"
        android:textSize="11sp" />

      <ImageView
        android:layout_width="10dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:paddingTop="8dp"
        android:contentDescription="@string/desc_gen_image"
        android:background="@drawable/user" />

    </LinearLayout>
</LinearLayout>

Ok, no problem up to now. So I defined my customized ArrayAdapter whose getView() method is the following:

// This is the public class for the ViewHolder object
public static class vhItem {
  TextView maintext; 
  ImageView mainimage;
  TextView users; 
}

@Override
public View getView(final int position, View convertView, final ViewGroup parent) {
  vhItem viewHolder;

  if (convertView == null) {
    final LayoutInflater inflater = ((Activity) context).getLayoutInflater();
    convertView = inflater.inflate(resourceId, parent, false);

    viewHolder = new vhItem();
    viewHolder.maintext = (TextView) convertView.findViewById(R.id.chanlist_chan);
    viewHolder.mainimage = (ImageView) convertView.findViewById(R.id.chanlist_featured);
    viewHolder.users = (TextView) convertView.findViewById(R.id.chanlist_usercount);

    convertView.setTag(viewHolder);      
  }
  else
    viewHolder = (vhItem) convertView.getTag();

  final InfoCanal chaninfo = (InfoCanal) getItem(position);

  viewHolder.maintext.setText(chaninfo.getChanName() + "\n" + chaninfo.getTopic());        
  viewHolder.users.setText(String.valueOf(chaninfo.getUserCount()));

  /* star and star_off are the two Drawable objects that I initialized in the constructor of
     the class, so I don't have to findViewById() them each time I call this method */
  if (chaninfo.getFeatured())
    viewHolder.mainimage.setImageDrawable(star);
  else
    viewHolder.mainimage.setImageDrawable(star_off);

  return convertView; 
}

And here comes the problem: I'm trying to show around 300 list elements. As I said, the list scrolls smoothly (and most of the posts I found about improving performance of ListViews talk just about this), but the time between the initialization call of the adapter (new MyOwnArrayAdapter() ...) and the visualization of the list is enormous (about 15-20 seconds). I can simply embellish it showing a progress bar, but I wonder if I'm doing something inefficient here as I think 300 elements are not as much.

Any advice appreciated!

---------- EDIT ----------

I'm including the constructor:

public class StableArrayAdapter<T> extends ArrayAdapter<T> {
  final private Context context;
  final private int resourceId; 
  final private List<T> objects;
  final Drawable star, star_off;

  // There I store the objects to include in the list
  final private HashMap<T, Integer> mIdMap = new HashMap<T, Integer>();
  // I had to Override some methods related to the observers, that's why I store this
  final private ArrayList<DataSetObserver> observers = new ArrayList<DataSetObserver>();

  // Constructor
  public StableArrayAdapter(final Context context_, final int resourceId_, final List<T> objects_) {
    super(context_, resourceId_, objects_);

    this.context = context_;
    this.resourceId = resourceId_;
    this.objects = objects_;

    for (int i = 0; i < objects.size(); i++)
      mIdMap.put(objects.get(i), i);

    star = context.getResources().getDrawable(R.drawable.checkbox_star);
    star_off = context.getResources().getDrawable(R.drawable.checkbox_star_down);
  }

  ...
}
Community
  • 1
  • 1
nKn
  • 13,691
  • 9
  • 45
  • 62
  • If scrolling is not a problem, then your issue is not in `getView()`. It's in your adapter initialization. How do you get the data which populates your ListView? Add this code to your post. – Mike Ortiz Jan 07 '14 at 21:34
  • The data passed to the StableArrayAdapter() is got parsing an URL which has the definion of those 300 items. I'm not counting the time this takes, as it's an separate task (in a background thread, btw). Once I got those items I pass them to the constructor that I edited in my post. – nKn Jan 07 '14 at 21:44

3 Answers3

0

Just wandering - you convert List to a HashMap (with some sort of idex?), than in the getView() you do getItem(position) probably searching HashMap for the value(!!!) == position? Why not keep original list and get data elements right from it?

StrawDragon
  • 79
  • 1
  • 1
  • As you might see, the StableArrayAdapter class is parametrized (T), this is because I want to use it for several activities with the same logic but different data type, and avoid defining one extension of ArrayAdapter for each one. I use HashMap because it's very efficient, but for some of those activities (not this one particularly) I need to have stable Ids (i.e. elements must have the same id even after updating the HashSet). – nKn Jan 07 '14 at 22:16
0

I'm going to guess the issue is in the List looping you do in the constructor. I wonder if some List implementations walk their underlying data set each time to satisfy the size() method. What about caching that?

int size = objects.size();
for (int i = 0; i < size; i++) {
      mIdMap.put(objects.get(i), i);
}

Alternatively could use an Iterator

Cody Caughlan
  • 32,456
  • 5
  • 63
  • 68
  • In base of your comment I put a Log object just before and just after that loop and it took 0.12sec. to process it, so I assume this is not the problem. However, it seems strange because the content of the List appears seconds AFTER it has left the constructor... – nKn Jan 07 '14 at 22:37
0

Finally I got it. Basically, it had nothing to do with the initialization nor the getView() method. As during the processing of the StableArrayAdapter() constructor I could do some additional things in paralell, I started a Thread() prior to calling new StableArrayAdapter(), and to synchronize the result of this thread and the initialization, I forgot I had declared a CountDownLatch(), so basically the code below the initialization of the adapter was waiting for the thread to call the countDown() method.

I'm sorry guys and at the same time thank you, from the code in the initial post noone could have known this (I unintentionally ommited this part thinking it was not important).

At the same time I hope this can help someone, or at least give a clue.

nKn
  • 13,691
  • 9
  • 45
  • 62