title: Android: EfficientAdapter with two different Views
body: <p>I'm using an extended version of BaseAdapter based on the <a href="http://developer.android.com/guide/samples/ApiDemos/src/com/example/android/apis/view/List14.html" rel="noreferrer">EfficientAdapter example</a> from the SDK demo samples.</p>
  
  <p>My data is basically an object (<code>ListPlaces</code>) which holds an <code>ArrayList</code> with the actual list of places, accessible via <code>listPlaces.getValues()</code>. This ArrayList data is sorted by range and the <code>ArrayList</code> consist of some special items (separators), with no data, but a <code>separator</code> flag set to <code>true</code>. </p>
  
  <p>Now whenever my <code>EfficientAdapter</code> gets a data object which is a separator it returns <code>false</code> for <code>public  boolean isEnabled(int position)</code> and <code>public View getView(int position, View convertView, ViewGroup parent)</code> inflates two different layouts depending on if the current data object consists of real data or is just a separator dummy.</p>
  
  <p>This works great, if I inflate the layout every time. However, inflating the layout every time and calling <code>findViewById</code> makes the <code>ListView</code> almost unusabely slow.</p>
  
  <p>So I tried to use the EfficientAdapter with <code>ViewHolder</code> approach. But that didn't work right out of the box, because of the two different views I try to access. So whenever my <code>convertView != null</code> (the else-case) accesses the items on the layout via our <code>ViewHolder</code> and when the previous View was a separator it of course doesn't work to access a TextView there which is only available on the "real" items layout.</p>
  
  <p>So I also force my <code>getView()</code> to inflate the layout not only when <code>convertView == null</code>, but also when the previous listRow is different than the current one: <code>if (convertView == null || (listRow != listRow_previous)) { [....] }</code></p>
  
  <p>This seems to almost work now. Or at least it doesn't crash right from the beginning. But it still crashes and I don't know what I've to do different. I've tried to look into <code>convertView.getID()</code> and <code>convertView.getResources()</code>, but that wasn't really helpful so far. Maybe someone else has an idea how I can check whether my current <code>convertView</code> matches with the list item layout or the list separator layout. Thanks.</p>
  
  <p>Here's the code. Where ever there is a [...] I took out some less important code to make it easier to read and understand:</p>
  
  <pre><code>private class EfficientAdapter extends BaseAdapter {
    private LayoutInflater mInflater;
    private ListPlaces listPlaces;
  
    private ListRow listRow;
    private ListRow listRow_previous;
  
  
    public EfficientAdapter(Context context, ListPlaces listPlaces) {
        // Cache the LayoutInflate to avoid asking for a new one each time.
        mInflater = LayoutInflater.from(context);
  
        // Data
        this.listPlaces = listPlaces;
    }
  
    /**
      * The number of items in the list is determined by the number of items
      * in our ArrayList
      *
      * @see android.widget.ListAdapter#getCount()
      */
    public int getCount() {
        return listPlaces.getValues().size();
    }
  
    /**
      * Since the data comes from an array, just returning the index is
      * sufficent to get at the data. If we were using a more complex data
      * structure, we would return whatever object represents one row in the
      * list.
      *
      * @see android.widget.ListAdapter#getItem(int)
      */
    public Object getItem(int position) {
        return position;
    }
  
    /**
      * Use the array index as a unique id.
      *
      * @see android.widget.ListAdapter#getItemId(int)
      */
    public long getItemId(int position) {
        return position;
    }
  
    @Override
    public  boolean isEnabled(int position) {
        // return false if item is a separator:
        if(listPlaces.getValues().get(position).separator &gt;= 0)
            return false;
        else
            return true;
    }
  
    @Override
    public boolean  areAllItemsEnabled() {
        return false;         
    }
  
  
  
    /**
      * Make a view to hold each row.
      *
      * @see android.widget.ListAdapter#getView(int, android.view.View,
      *      android.view.ViewGroup)
      */
    public View getView(int position, View convertView, ViewGroup parent) {
  
        // Get the values for the current list element
        ListPlacesValues curValues = listPlaces.getValues().get(position);
        if (curValues.separator &gt;= 0) 
            listRow = ListRow.SEPARATOR;
        else
            listRow = ListRow.ITEM;
        Log.i(TAG,"Adapter: getView("+position+") " + listRow + " (" + listRow_previous + ") -&gt; START");
  
        // A ViewHolder keeps references to children views to avoid unneccessary calls
        // to findViewById() on each row.
        ViewHolder holder;
  
        // When convertView is not null, we can reuse it directly, there is no need
        // to reinflate it. We only inflate a new View when the convertView supplied
        // by ListView is null.
        if (convertView == null || (listRow != listRow_previous)) {
            Log.i(TAG, "--&gt; (convertView == null) at position: " + position);
            // Creates a ViewHolder and store references to the two children views
            // we want to bind data to.
            holder = new ViewHolder();
  
            if (listRow == ListRow.SEPARATOR) {
                convertView = mInflater.inflate(R.layout.taxonomy_list_separator, null);
                holder.separatorText = (TextView) convertView.findViewById(R.id.separatorText);
                convertView.setTag(holder);
                Log.i(TAG,"\tCREATE SEPARATOR: convertView ID: " + convertView.getId() + " Resource: " + convertView.getResources());
  
            }
            else {
  
                convertView = mInflater.inflate(R.layout.taxonomy_listitem, null);
                holder.name = (TextView) convertView.findViewById(R.id.name);
                holder.category = (TextView) convertView.findViewById(R.id.category);
                // [...]
  
                convertView.setTag(holder);
  
                Log.i(TAG,"\tCREATE ITEM: convertView ID: " + convertView.getId() + " Resource: " + convertView.getResources());
            }
        } else {
            // Get the ViewHolder back to get fast access to the TextView
            // and the ImageView.
            Log.i(TAG,"\tconvertView ID: " + convertView.getId() + " Resource: " + convertView.getResources());
  
            holder = (ViewHolder) convertView.getTag();
            convertView.setAnimation(null);
        }
  
        /* Bind the data efficiently with the holder */
        if (listRow == ListRow.SEPARATOR) {
            String separatorText;
            switch (curValues.separator) {
            case 0: separatorText="case 0"; break;
            case 1: separatorText="case 1"; break;
            case 2: separatorText="case 2"; break;
            // [...]
          default: separatorText="[ERROR]"; break;
            }
            holder.separatorText.setText(separatorText);
        } 
        else {
            // Set the name:
            holder.name.setText(curValues.name);
            // Set category
            String cat = curValues.classification.toString();
            cat = cat.substring(1,cat.length()-1);    // removing "[" and "]"
            if (cat.length() &gt; 35) {
                cat = cat.substring(0, 35);
                cat = cat + "...";
            }
            holder.category.setText(cat);
  
            // [...] (and many more TextViews and ImageViews to be set)
  
        }
  
        listRow_previous = listRow;
        Log.i(TAG,"Adapter: getView("+position+") -&gt; DONE");
        return convertView;
    }
  
    private class ViewHolder {
        TextView name;
        TextView category;
        // [...] -&gt; many more TextViews and ImageViews
  
        TextView separatorText;
    }
  }
  </code></pre>
  
  <p>And here my <strong>Logcat</strong> output:</p>
  
  <pre><code>  755     ListPlaces_Activity  I  onPostExecute: notifyDataSetChanged()                                                                                                
    755     ListPlaces_Activity  I  Adapter: getView(0) SEPARATOR (null) -&gt; START                                                                                        
    755     ListPlaces_Activity  I  --&gt; (convertView == null) at position: 0                                                                                             
    755     ListPlaces_Activity  I        CREATE SEPARATOR: convertView ID: 2131296317 Resource: android.content.res.Resources@437613e0                                  
    755     ListPlaces_Activity  I  Adapter: getView(0) -&gt; DONE                                                                                                          
    755     ListPlaces_Activity  I  Adapter: getView(1) ITEM (SEPARATOR) -&gt; START                                                                                        
    755     ListPlaces_Activity  I  --&gt; (convertView == null) at position: 1                                                                                             
    755     ListPlaces_Activity  I        CREATE ITEM: convertView ID: 2131296317 Resource: android.content.res.Resources@437613e0                                       
    755     ListPlaces_Activity  I  Adapter: getView(1) -&gt; DONE                                                                                                          
    755     ListPlaces_Activity  I  Adapter: getView(2) SEPARATOR (ITEM) -&gt; START                                                                                        
    755     ListPlaces_Activity  I  --&gt; (convertView == null) at position: 2                                                                                             
    755     ListPlaces_Activity  I        CREATE SEPARATOR: convertView ID: 2131296317 Resource: android.content.res.Resources@437613e0                                  
    755     ListPlaces_Activity  I  Adapter: getView(2) -&gt; DONE                                                                                                          
    755     ListPlaces_Activity  I  Adapter: getView(3) ITEM (SEPARATOR) -&gt; START                                                                                        
    755     ListPlaces_Activity  I  --&gt; (convertView == null) at position: 3                                                                                             
    755     ListPlaces_Activity  I        CREATE ITEM: convertView ID: 2131296317 Resource: android.content.res.Resources@437613e0                                       
    755     ListPlaces_Activity  I  Adapter: getView(3) -&gt; DONE                                                                                                          
    755     ListPlaces_Activity  I  Adapter: getView(4) ITEM (ITEM) -&gt; START                                                                                             
    755     ListPlaces_Activity  I        convertView ID: 2131296317 Resource: android.content.res.Resources@437613e0                                                    
    755     ListPlaces_Activity  I  Adapter: getView(4) -&gt; DONE                                                                                                          
    755     ListPlaces_Activity  I  Adapter: getView(5) ITEM (ITEM) -&gt; START                                                                                             
    755     ListPlaces_Activity  I        convertView ID: 2131296317 Resource: android.content.res.Resources@437613e0                                                    
    755     ListPlaces_Activity  I  Adapter: getView(5) -&gt; DONE                                                                                                          
    755     ListPlaces_Activity  I  Adapter: getView(6) ITEM (ITEM) -&gt; START                                                                                             
    755     ListPlaces_Activity  I        convertView ID: 2131296317 Resource: android.content.res.Resources@437613e0                                                    
    755     ListPlaces_Activity  I  Adapter: getView(6) -&gt; DONE                                                                                                          
    755     ListPlaces_Activity  I  Adapter: getView(0) SEPARATOR (ITEM) -&gt; START                                                                                        
    755     ListPlaces_Activity  I  --&gt; (convertView == null) at position: 0                                                                                             
    755     ListPlaces_Activity  I        CREATE SEPARATOR: convertView ID: 2131296317 Resource: android.content.res.Resources@437613e0                                  
    755     ListPlaces_Activity  I  Adapter: getView(0) -&gt; DONE                                                                                                          
    755     ListPlaces_Activity  I  Adapter: getView(1) ITEM (SEPARATOR) -&gt; START                                                                                        
    755     ListPlaces_Activity  I  --&gt; (convertView == null) at position: 1                                                                                             
    755     ListPlaces_Activity  I        CREATE ITEM: convertView ID: 2131296317 Resource: android.content.res.Resources@437613e0                                       
    755     ListPlaces_Activity  I  Adapter: getView(1) -&gt; DONE                                                                                                          
    755     ListPlaces_Activity  I  Adapter: getView(2) SEPARATOR (ITEM) -&gt; START                                                                                        
    755     ListPlaces_Activity  I  --&gt; (convertView == null) at position: 2                                                                                             
    755     ListPlaces_Activity  I        CREATE SEPARATOR: convertView ID: 2131296317 Resource: android.content.res.Resources@437613e0                                  
    755     ListPlaces_Activity  I  Adapter: getView(2) -&gt; DONE                                                                                                          
    755     ListPlaces_Activity  I  Adapter: getView(3) ITEM (SEPARATOR) -&gt; START                                                                                        
    755     ListPlaces_Activity  I  --&gt; (convertView == null) at position: 3                                                                                             
    755     ListPlaces_Activity  I        CREATE ITEM: convertView ID: 2131296317 Resource: android.content.res.Resources@437613e0                                       
    755     ListPlaces_Activity  I  Adapter: getView(3) -&gt; DONE                                                                                                          
    755     ListPlaces_Activity  I  Adapter: getView(4) ITEM (ITEM) -&gt; START                                                                                             
    755     ListPlaces_Activity  I        convertView ID: 2131296317 Resource: android.content.res.Resources@437613e0                                                    
    755     ListPlaces_Activity  I  Adapter: getView(4) -&gt; DONE                                                                                                          
    755     ListPlaces_Activity  I  Adapter: getView(5) ITEM (ITEM) -&gt; START                                                                                             
    755     ListPlaces_Activity  I        convertView ID: 2131296317 Resource: android.content.res.Resources@437613e0                                                    
    755          AndroidRuntime  D  Shutting down VM                                                                                                                     
    755                dalvikvm  W  threadid=3: thread exiting with uncaught exception (group=0x4001aa28)                                                                
    755          AndroidRuntime  E  Uncaught handler: thread main exiting due to uncaught exception                                                                      
    755          AndroidRuntime  E  java.lang.NullPointerException                                                                                                       
    755          AndroidRuntime  E        at com.tato.main.ListPlaces_Activity$EfficientAdapter.getView(ListPlaces_Activity.java:330)                                    
    755          AndroidRuntime  E        at android.widget.HeaderViewListAdapter.getView(HeaderViewListAdapter.java:191)                                                
    755          AndroidRuntime  E        at android.widget.AbsListView.obtainView(AbsListView.java:1255)                                                                
    755          AndroidRuntime  E        at android.widget.ListView.makeAndAddView(ListView.java:1658)                                                                  
    755          AndroidRuntime  E        at android.widget.ListView.fillDown(ListView.java:637)                                                                         
    755          AndroidRuntime  E        at android.widget.ListView.fillFromTop(ListView.java:694)                                                                      
    755          AndroidRuntime  E        at android.widget.ListView.layoutChildren(ListView.java:1502)                                                                  
    755          AndroidRuntime  E        at android.widget.AbsListView.onLayout(AbsListView.java:1112)                                                                  
    755          AndroidRuntime  E        at android.view.View.layout(View.java:6569)                                                                                    
    755          AndroidRuntime  E        at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)                                                           
    755          AndroidRuntime  E        at android.widget.LinearLayout.layoutVertical(LinearLayout.java:998)                                                           
    755          AndroidRuntime  E        at android.widget.LinearLayout.onLayout(LinearLayout.java:918)                                                                 
    755          AndroidRuntime  E        at android.view.View.layout(View.java:6569)                                                                                    
    755          AndroidRuntime  E        at android.widget.FrameLayout.onLayout(FrameLayout.java:333)                                                                   
    755          AndroidRuntime  E        at android.view.View.layout(View.java:6569)                                                                                    
    755          AndroidRuntime  E        at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)                                                           
    755          AndroidRuntime  E        at android.widget.LinearLayout.layoutVertical(LinearLayout.java:998)                                                           
    755          AndroidRuntime  E        at android.widget.LinearLayout.onLayout(LinearLayout.java:918)                                                                 
    755          AndroidRuntime  E        at android.view.View.layout(View.java:6569)                                                                                    
    755          AndroidRuntime  E        at android.widget.FrameLayout.onLayout(FrameLayout.java:333)                                                                   
    755          AndroidRuntime  E        at android.view.View.layout(View.java:6569)                                                                                    
    755          AndroidRuntime  E        at android.view.ViewRoot.performTraversals(ViewRoot.java:979)                                                                  
    755          AndroidRuntime  E        at android.view.ViewRoot.handleMessage(ViewRoot.java:1613)                                                                     
    755          AndroidRuntime  E        at android.os.Handler.dispatchMessage(Handler.java:99)                                                                         
    755          AndroidRuntime  E        at android.os.Looper.loop(Looper.java:123)                                                                                     
    755          AndroidRuntime  E        at android.app.ActivityThread.main(ActivityThread.java:4203)                                                                   
    755          AndroidRuntime  E        at java.lang.reflect.Method.invokeNative(Native Method)                                                                        
    755          AndroidRuntime  E        at java.lang.reflect.Method.invoke(Method.java:521)                                                                            
    755          AndroidRuntime  E        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)                                             
    755          AndroidRuntime  E        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:549)                                                                
    755          AndroidRuntime  E        at dalvik.system.NativeStart.main(Native Method)    
  </code></pre>