views:

356

answers:

2

It all started with a simple WebView within an Android app.

This view loads info from a public URL. Nothing unusual there.

However ... if there should be a network interruption, you can easily end up with an error displayed prominently in the WebView.

Well, that's no good, right? So I searched the docs and elsewhere for a way to suppress this, or trap it ... and found no leads. With that, I inquired on #android-dev on IRC: I know about WebView.onReceivedError, but can the visual portion be suppressed? (Instead, I wanted to display a Dialog warning of said network problem, with an option to retry.)

Answer: You can't. :(

Very well. Time for plan B. Perhaps an ImageView might fill the bill?

Should there be any kind of WebView error, the ImageView can be used as a makeshift curtain. It overlays the WebView at the start (before the URL is loaded), then it fades to reveal the WebView behind it. If the WebView works, wonderful! But if onReceivedError kicks in, we put the curtain back up immediately and show the aforementioned dialog. Once WebView gets an onPageFinished, the curtain fades once more.

... and THIS ... works WONDERFULLY!

Until I was asked to support screen orientation changes. (Foreshadowing: "Things went downhill from here.")

Up until now the app was using android:screenOrientation="portrait" in the Manifest's main activity. So I removed that part, and of course we ran into the infamous "activity is being restarted" issue, which blows away and then brings back the WebView.

Of course it's not an issue per se. This is Android OS working as designed.

Also, we want the ImageView to display properly depending on orientation. Thankfully, we have alternate resources we can leverage! Meanwhile, we still have this WebView we don't want to reload. We want it to be left alone and let the view do its own thing, be it in portrait or landscape at the time.

I found a few well-explained ways to handle screen orientation, plus I have already read through author Mark Murphy's eBooks (highly recommended reading). Yet I was still stuck! With the ImageView now in the mix, none of the techniques seem to fill the bill:

  1. Forcing portrait mode is, of course, out of the question (or is it?).

  2. android:configChanges="keyboardHidden|orientation" and onConfigurationChanged seem to take care of the WebView, which blissfully handles the screen resizing gracefully on its own. Of course, the ImageView doesn't get the memo either, and it keeps using its original (portrait/landscape) resource!

    An ill-conceived call to activity.setRequestedOrientation(newConfig.orientation) within the config change handler didn't help matters.

  3. Since the WebView doesn't need to be persisted between app executions, onRetainNonConfigurationInstance looked like another possibility. Alas, that didn't seem to work either. Resetting webview with the last nonconfig instance (since that's all I really passed in) resulted in a blank WebView. Not quite what I was after.

    On the other hand, the ImageView seemed to responding nicely to orientation changes. Go figure.

  4. How about onSaveInstanceState? Same gotchas. WebView has a saveState method, but it doesn't seem to be saving it at a level deep enough to keep the transition seamless. If so, then it's probably not supposed to either, so this might be the wrong way to go too.

As to the wisdom of using configChanges, the documentation recommends this very technique, in fact:

Standard behavior for an Activity is to be destroyed and recreated when the devices orientation is changed. This will cause the WebView to reload the current page. If you don't want that, you can set your Activity to handle the orientation and keyboardHidden changes, and then just leave the WebView alone. It'll automatically re-orient itself as appropriate.

Of course I could also be committing some sort of pilot error here, but I have yet to figure out what that might be.

I have read and re-read the documentation and tried every which way to hack through this on my own. Perhaps I'm missing something (obvious?) in the docs, or the solution is non-obvious (but someone can shed light) ... or this simply isn't possible. Hoping it's not the latter.

Clues welcome/appreciated!

A: 

Glad you like the books!

In the chapter on rotation, where I suspect you got many of the approaches you tried, you'll note that the onConfigurationChanged() flavor calls setContentView() again, forcibly reloading the layout and therefore getting the proper resource. You, of course, don't want to do that per se, because that'd blow away your WebView.

However, I would expect that a call to setImageResource() in onConfigurationChanged(), for your ImageView, should pull in the proper version of your "curtain". Use that to "touch up" your image to the proper one, and see if that helps.

CommonsWare
Hi Mark (waves)! Yep, setContentView - I was avoiding that like the plague (hehe). Would you believe I have this line commented out in onConfigurationChanged? `curtain.setImageResource(R.drawable.curtain);` Reason: It didn't seem to be doing anything. I'm going to give it a closer look.
Joe D'Andrea
No dice. It's like I didn't do anything by making the call. Even if I simplify the problem by just making it about the ImageView and getting it to change over upon rotation. Hmm ...
Joe D'Andrea
Hmmmm...that's odd. As a test, make a slightly-altered copy of your curtain image under a new name, and try toggling between the two, rather than relying upon resource sets. I just want to make sure the problem is that you're not getting the right resource set. If setImageResource() on the altered image has no effect, that means your ImageView isn't getting updated for some reason, which would require a different debugging path to get fixed.
CommonsWare
OK - verrry interesting results! To keep from messing with the UI, I used onKeyDown to trap KEYCODE_BACK. Each time I tapped it, I would flip a boolean between true/false and, depending on that, I'd setImageResource to one of two images. That much worked! Then I remembered I still had the orientation detection in there, so I tried rotating the device to landscape. (All the while I'm tracing this.) Again, in onConfigurationChanged, we use setImageResource. But instead it seemed to use the pre-orientation-change image! Tapping the back button after that though? It changed the image. <!>
Joe D'Andrea
That certainly seems counterintuitive. It does suggest, though, that a workaround might be to do the setImageResource() after a short delay, triggered by onConfigurationChanged(). You could do this via postDelayed() on the ImageView widget, for example. Push it out 100ms and see what happens, that sort of thing. But, honestly, it really should have the right resource in onConfigurationChanged(), since that's kinda the point...
CommonsWare
We are so close!!!! I tried this in onConfigurationChanged (pardon the run-on sentence structure): `curtain.postDelayed(new Runnable() { public void run() { curtain.setImageResource(R.drawable.curtain); } }, 100);` ... and it works! The _first_ time. Each time I rotate after that, it doesn't kick in anymore. Correction: The runnable IS run (I land on the breakpoint every time), but after the first go-round it seems to have no effect. I even tried bumping up the delay to 500ms. No dice.
Joe D'Andrea
Next thing to try: in onConfigurationChanged(), remove and re-add the ImageView from whatever parent container you have it in. You can either inflate the ImageView from a dedicated XML layout file (perhaps incorporated via <include> in the main XML layout file), or you can just create it in Java code. Something really strange is going on with your app, but I have no way of debugging that, so I'm just lobbing out ideas in hopes that either one works as a hack or they help you stumble upon whatever might be wrong.
CommonsWare
Hmm, I must be doing something wrong now. I can include the lone ImageView-in-a-layout easily enough, but running the app locks things up (even though the included layout is being displayed), leaving me to find a way to force quit it. A minimal test case can't be far behind!
Joe D'Andrea
A: 

Did you try manipulating your Android Manifest file.

Instructions --> http://androidsfuture.blogspot.com/2010/06/developers-corner-dealing-with-screen.html#comment-form

Sana
Yep - doing _exactly_ that, in fact.
Joe D'Andrea