title: Android Listview: Can't get reliable checking behavior for CHOICE_MODE_SINGLE
body: <p>Here's what I mean by weird:</p>
  
  <ol>
  <li>Items usually take 2 presses to have their background colour change correctly.</li>
  <li>When the list is changed, Android doesn't seem to be correctly invalidating list views as the re-used views that were checked have the checked background on the new items (that I have confirmed through the debugger are <em>not</em> checked).</li>
  <li>Multiple items can have the look of being selected, and in fact "correctly" uncheck when I click on them again, except for the fact that the <code>ListView</code> reports only the most-recent item as checked.</li>
  <li>The first time I check an item, I can't uncheck it by clicking it.</li>
  <li>Things work more-or-less fine if I change the choice mode to <code>ListView.CHOICE_MODE_MULTIPLE</code> except, of course, that I don't want multiple selection.</li>
  </ol>
  
  <p>I'm using a custom adapter and a custom layout. Oh, also, targeting 4.0.3 for now. Here's the code for the list:</p>
  
  <pre class="lang-java prettyprint-override"><code>ListView categoryList = (ListView) findViewById(R.id.categoryList);
  categoryList.setAdapter(categoryAdapter);
  categoryList.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
  categoryList.setItemsCanFocus(false);
  categoryList.setOnItemClickListener(categoryAdapter);
  </code></pre>
  
  <p>Here's the click listener:</p>
  
  <pre class="lang-java prettyprint-override"><code>public void onItemClick(AdapterView&lt;?&gt; parent, View view, int position, long id) {              
      ListView listView = (ListView) parent;  
      RemoteListItem remoteListItem = (RemoteListItem) view.getTag();
  
      if (remoteListItem.isEnabled()) {
          remoteListItem.action(view);
      }
      view.invalidate(); /added out of sheer desperation              
  }
  </code></pre>
  
  <p>Here's the extended version of RelativeLayout I'm using:</p>
  
  <pre class="lang-java prettyprint-override"><code>package com.sastraxi.machineshop.ui;
  
  import android.content.Context;
  import android.graphics.drawable.Drawable;
  import android.util.AttributeSet;
  import android.util.Log;
  import android.widget.Checkable;
  import android.widget.RelativeLayout;
  
  /**
   * RelativeLayout that implements the Checkable interface.
   * Set this view's tag as a Checkable, and this layout will delegate
   * Checkable's interface methods to the tag object. 
   */
  public class CheckableRelativeLayout extends RelativeLayout implements Checkable {
  
      @Override
      public boolean isClickable() {
          return false;
      }
  
      public CheckableRelativeLayout(Context context) {
          super(context);
      }
  
      public CheckableRelativeLayout(Context context, AttributeSet attrs) {
          super(context, attrs);
      }
  
      public CheckableRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
          super(context, attrs, defStyle);
      }
  
      /**
       * Delegates to (Checkable) getTag().
       */
      public boolean isChecked() {        
          try {
              Checkable checkableTag = (Checkable) getTag();
              return checkableTag.isChecked();
          } catch (ClassCastException e) {
              Log.w("CheckableRelativeLayout", "Tag is not an instance of Checkable; this object won't do anything useful.");
          } catch (NullPointerException e) {
              Log.w("CheckableRelativeLayout", "Tag is null; this object won't do     anything useful.");
          }       
          return false;
      }
  
      /**
       * Delegates to (Checkable) getTag().
       */
      public void setChecked(boolean checked) {
          try {
              Checkable checkableTag = (Checkable) getTag();
              checkableTag.setChecked(checked);
              invalidate();
          } catch (ClassCastException e) {
              Log.w("CheckableRelativeLayout", "Tag is not an instance of Checkable; this object won't do anything useful.");
          } catch (NullPointerException e) {
              Log.w("CheckableRelativeLayout", "Tag is null; this object won't do anything useful.");
          }
      }
  
      /**
       * Delegates to (Checkable) getTag().
       */
      public void toggle() {
          try {
              Checkable checkableTag = (Checkable) getTag();
              checkableTag.toggle();
              invalidate();
          } catch (ClassCastException e) {
              Log.w("CheckableRelativeLayout", "Tag is not an instance of Checkable; this object won't do anything useful.");
          } catch (NullPointerException e) {
              Log.w("CheckableRelativeLayout", "Tag is null; this object won't do anything useful.");
          }
      }
  
      private static final int[] CHECKED_STATE_SET = {
          android.R.attr.state_checked
      };
  
      /**
       * Reflect the delegate Checkable's state in this View's state set.
       */
      @Override    
      protected int[] onCreateDrawableState(int extraSpace) {
          final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
          if (isChecked()) {
              mergeDrawableStates(drawableState, CHECKED_STATE_SET);
          }     
          return drawableState;
      }
  
  }
  </code></pre>
  
  <p>Here's the list item type it's proxying to:</p>
  
  <pre class="lang-java prettyprint-override"><code>public abstract class RemoteListItem implements Checkable {
  
      private final String name;
      private final String extra;
      private boolean enabled = true;
      private boolean selected = false;
  
      public boolean isChecked() {
          return selected;
      }
  
      public void toggle() {
          selected = !selected;
      }
  
      public void setChecked(boolean checked) {
          selected = checked;
      }
  
      public RemoteListItem(String name, String extra) {
          this.name = name;
          this.extra = extra;     
      }
  
      public String getExtra() {
          return extra;
      }
  
      public String getName() {
          return name;
      }
  
      public abstract void action(View viewInList);
  
      public boolean isEnabled() {
          return enabled;
      }
  
      public void setEnabled(Boolean enabled) {
          this.enabled = enabled;
          RemoteListAdapter.super.notifyDataSetChanged();
      }
  
      public boolean isSelectable() {
          return true;
      }
  
  }
  </code></pre>
  
  <p>Here's the layout that's being expanded for the items:</p>
  
  <pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
  &lt;com.sastraxi.machineshop.ui.CheckableRelativeLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
      android:layout_height="?android:attr/listPreferredItemHeightSmall"
      android:padding="12dp"  
      android:gravity="center_vertical"
      android:background="@drawable/listitem_background"&gt;
  
      &lt;TextView
          android:id="@+id/key"
          android:layout_height="wrap_content"
          android:layout_width="wrap_content"
          android:textAppearance="?android:attr/textAppearanceMedium"
          android:layout_alignParentLeft="true"    
          android:inputType="none"   
      /&gt;
  
      &lt;TextView
          android:id="@+id/value"
          android:layout_height="wrap_content"
          android:layout_width="match_parent"
          android:gravity="right"
          android:textAppearance="?android:attr/textAppearanceMedium"
          android:textColor="@color/faded_text_colour"
          android:layout_alignParentRight="true"
          android:inputType="none"
      /&gt;
  
      &lt;ProgressBar
           android:id="@+id/progress"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           style="@android:style/Widget.ProgressBar.Small"
           android:layout_marginTop="5dip"
           android:layout_marginRight="2dip"
           android:gravity="right"
           android:visibility="gone"
           android:layout_alignParentRight="true"/&gt;
  
  &lt;/com.sastraxi.machineshop.ui.CheckableRelativeLayout&gt;
  </code></pre>
  
  <p>Also, @drawable/listitem_background is a state list, which is where the checked background colour comes from. I feel so lost as to why things aren't working the way I expect them to. Seems like I'm missing a <code>view.invalidate()</code> somewhere, but I can't fathom where.</p>