Month: July 2011

Android Gallery convertView is always null

As the title says, a long standing bug has been hindering experience for the developers who use Android’s out of the box solution for a Gallery component.

The problem actually lies inside the getView method which is inherited from BaseAdapter->Adapter. One of the parameters the method is expecting is a “recycled” instance of their adapter view, but it never arrives. It allows the developer to re-use the old view that was just swiped off the stage, instead of instantiating a new view. Instantiating a new view here is an expensive process during the time of scrolling. You want this time to be seamless to the user, not choppy if they scroll too fast back and forth. During every swipe, you quickly have to instantiate multiple instances while every view that falls off the viewing area literally falls into an abyss of nothingness, never to be seen again.

I solved this problem by managing my own Array of views in the Adapter class and indexed them based on position. Looking back at it now, it was somewhat of a lazy solution for reasons I go into below….but it works. 😉

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
    public SmallGameVO[] featuredItems;
 
    private SmallGameVO tempGame;
 
    private View[] viewArray;
 
    public FeaturedAdapter(Context c, SmallGameVO[] featuredGames) {
 		featuredItems = featuredGames;
 		viewArray =  new View[featuredItems.length];
    }
 
    public int getCount() {
        return featuredItems.length;
    }
 
    public Object getItem(int position) {
        return position;
    }
 
    public long getItemId(int position) {
        return position;
    }
 
 
    public View getView(int position, View convertView, ViewGroup parent) {	   	
  		LayoutInflater linflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);			
  		LinearLayout tempView = null;
 
                if(convertView != null){
    		        NVDebug.log("re-using the convertView!");
    		        tempView = convertView;
    	        }else{
                        //no convertView, need to do the heavy lifting ourselves
                        //check our viewArray to see if we have a view at the position or we need to create a new one
 
				if(viewArray[position] != null){					
					tempView = (LinearLayout) viewArray[i];
					break;					
				} else {					
					tempView = (LinearLayout) linflater.inflate(R.layout.feature_gallery_capsule, null);
					viewArray[position] = tempView;
					break;					
				}
			}	
    	         }
 
    	        NVWebImageView img = (NVWebImageView) tempView.findViewById(R.id.galleryImage);
 
		if(position <= featuredItems.length - 1){			
			tempGame = featuredItems[position];
			if(img.imageUrl != tempGame.getFeaturedImage()){
				img.setImageUrl(tempGame.getFeaturedImage());
				img.loadImage();
			}		
		}else{
			tempView = new LinearLayout(parent.getContext());
		}    	
 
        return tempView;
    }

This wouldn’t be the best idea for situations where you have a larger number of items(20+) in your gallery. In that situation, I would recommend implementing you’re own “cache” of views at your disposal. Depending on the size of the view, you really only have 4-5 views in the viewing area at one time, so it’s pretty easy to see how you could manage them swiping to the n-th scale if needed with an array of just 10 views. Luckily, my gallery will never go higher than 10-15.

So that’s it, nothing too fancy. Just instead of relying on the view to be there, waste some space and make your own array of views. 🙂