Monday, March 7, 2011

View visibility bug on Android 2.3.3

So I've been trying to work around a strange bug that's shown up on Android 2.3.3, which only affects the Aquarium customize screen.  Basically, when you visit that screen on 2.3.3, you can't open the three drawers that contain the customization controls.  Pressing the 'props', 'background', and 'fish' buttons doesn't do anything.

I replicated the issue by installing a nightly build of CyanogenMod onto an Evo 4G.  Starting in on figuring out the problem, I was confused and still am.  Here's how the code works.

Upon initializing CustomizeActivity, I load the UI layout, I set up a bunch of button handlers, then towards the bottom select the three detail windows and hide them.  The detail windows are the ones that contain the fish thumbnails, the background prev/next buttons, etc.  The code looks like this:

     LinearLayout ll;
     ll = (LinearLayout)findViewById( );
     ll.setVisibility( View.INVISIBLE );

So, that makes the details invisible by default.  Later on, you push the 'fish' button, and this code happens:

     LinearLayout ll = (LinearLayout)findViewById( );
     if( ll.getVisibility() != View.VISIBLE )
          ll.setVisibility( View.VISIBLE );
          ll.setVisibility( View.INVISIBLE );

And that's it, the whole logic.  It's very simple.

This has worked fine for most of a year.  However, what's happening in 2.3.3 is that if that initial setVisibility call occurs, the buttons can never make the view visible again, no matter what.  However, if I remove the initial call making them invisible, then the toggle (both on and off) works just fine.  I'm mystified by this.

I've tried a fairly wide range of things at this point:
  • Add a half-second delay before calling setVisibility, in case it's still initializing somewhere.
  • Move the initial setVisibility call to the button handler's constructor.
  • Don't check getVisibility and always have the button force to visible.
  • Call setVisibility( View.VISIBLE ) before setting them to invisible.
  • Make the initial setVisibility call in onResume instead of the constructor.
  • Setting visibility to invisible by default in the XML file.
I've tried other variants too I'm fairly sure, all with the same result.  I have no idea what separates a call to setVisibility in these other locations from the ones on the button (which work, so long as it's not being set somewhere else first).  I'm still working on it but for now may end up falling back to having the drawers default to open, which is a bit complicated looking upon initial view.


So, what I did eventually to work around this:  I gave up on View.INVISIBLE and started using View.GONE instead.  This is problematic since GONE actually removes the elements from the layout, and since everything's relative that makes the layout change.  I fixed that by playing the three details panels inside three invisible layout panels (all three of which have a width of fill_parent and a weight of 1).  These extra panels always stick around, so removing the detail panels entirely doesn't cause the layout to shift.



  1. Hi Jeremy. I have an application that's suffering from the exact same problem with Gingerbread. I thought I was crazy, but I'm glad to see someone else is running through the same issue. I'm going to try your solution soon with View.GONE and see if it works.

  2. I had the same problems, thanks so much for the fix.