Dialog animation messed up by webview: android bug

2019-05-24 12:04发布

问题:

I made a dialog with both Enter and Exit slow animations. But the dialog contains a webview myMsg (that loads a local file, so no delay) and messes up the animation.

With the code below (no webview), the dialog works perfectly, animating both at Enter and at Exit. However, if I uncomment the line //builder.setView(myMsg), the Exit animation still works perfectly, but the Enter animation is not performed (or performed too fast). Funny thing, if I minimize the app and maximize it again, the Enter dialog animation is performed fine.

It's like the webview load messes the Enter animation. I tried to show the dialog after the webview is loaded, with the same results.

Isn't that crazy?? Any clue of what is happening, and how to solve it? Any help would be appreciated.

Here's the relevant part of the code:

    WebView myMsg = new WebView(context);
    myMsg.loadUrl("file:///android_asset/page.html");

    AlertDialog.Builder builder = new AlertDialog.Builder(context);
    builder.setIcon(R.drawable.ic_launcher);
    //builder.setView(myMsg);
    builder.setTitle("Some Title");
    final AlertDialog dialog = builder.create();

    WindowManager.LayoutParams lp = dialog.getWindow().getAttributes();
    lp.windowAnimations = R.style.AnimateDialog;

    dialog.show();
    dialog.getWindow().setAttributes(lp);

and in styles,

<style name="AnimateDialog">
    <item name="android:windowEnterAnimation">@anim/in_left</item>
    <item name="android:windowExitAnimation">@anim/out_left</item>
</style>

EDIT

I tried with a setWebViewClient, but without luck (without the line dialog.getWindow().setLayout... the Enter animation simply does not work):

    WebView myMsg = new WebView(context);
    myMsg.getSettings().setJavaScriptEnabled(true);
    myMsg.loadUrl("file:///android_asset/" + page);
    myMsg.setBackgroundColor(0);
    myMsg.setSoundEffectsEnabled(true);

    AlertDialog.Builder builder = new AlertDialog.Builder(context);
    builder.setIcon(R.drawable.ic_launcher);
    builder.setTitle("SomeTitle");
    builder.setView(myMsg);
    final AlertDialog dialog = builder.create();

    myMsg.setWebViewClient(new WebViewClient() {
        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            dialog.show();
            dialog.getWindow().setWindowAnimations(R.style.AnimateDialog);
            dialog.getWindow().setLayout(WindowManager.LayoutParams.FILL_PARENT, WindowManager.LayoutParams.FILL_PARENT);
        }
    }

EDIT2

I also tried with Dialog instead of AlertDialog, with an xml layout, getting identical problems (tried also WebView.loadData instead of WebView.loadUrl, same problems):

    final Dialog d = new Dialog(context);
    d.setContentView(R.layout.viewhtml);
    d.setTitle("SomeTitle");
    // Without this, Enter animation does not work
    d.getWindow().setLayout(WindowManager.LayoutParams.FILL_PARENT,
            WindowManager.LayoutParams.FILL_PARENT);
    WebView wv = (WebView) d.findViewById(R.id.webview);
    wv.loadUrl("file:///android_asset/" + page);
    wv.setBackgroundColor(0);
    d.getWindow().setWindowAnimations(R.style.AnimateDialog);
    wv.setWebViewClient(new WebViewClient() {
        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            d.show();
        }
    });

and this is Dialog xml layout file:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

<WebView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/webview"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:soundEffectsEnabled="true" >
</WebView>

</LinearLayout>

EDIT3

I just realized that the same problem happens when using a TextView with TextView.setText(Html.fromHTML...) instead of a WebView. Moreover, if I add dialog.getWindow().setLayout(600,800) after dialog.show(), the animation is performed as expected. So it seems that the problem is that the animation is not performed without knowing beforehand the dialog size?

回答1:

I have a similar problem but instead of putting animation, I want to disable it because it is messing up with the apparent load time of my Dialog with WebView. I was actually able to disable the main window animation Enter/Exit as it is no longer fading in/out but the problem is the content of the WebView is most of the time delayed by the Slide animation of the Dialog which I am also trying to disable (I really thought if you disable the window animation, all animations should be disabled).

Based on my tests, this is actually a Jellybean bug but it is most obvious in Android 4.3 and the extra animation is messing up my popups. There is no problem with ICS though.

If you try to increase the default animation time via Android Settings -> Developer Options -> Window animation scale, say to 5 or even 10, you will notice that the content is now even further delayed in showing. But if you turn off the animation, the display will be fast. I believe your animation might work too if the default animation is disabled.

I think this behavior is a Jelly bean bug which is only present when you use a WebView inside a Dialog. The dialog puts the WebView content after the animation time even though it should already had been disabled sliding it as it grows in height.

Another solution I tried is to cache the Dialog so that it can be reused. My problem goes away but it is sometimes not resizing properly to the actual HTML content. Now if your content is mostly the same length when displayed, you could probably just cache your Dialog.

I hope I have given you much clues.



回答2:

The reason why your dialog animation seemed to be messed up by the WebView is the asynchronous nature of the loading of WebView's content. So putting the loadUrl, loadData or even loadDataWithBaseURL before dialog.show() is not a guarantee that the content is already fully loaded even if the content comes from local file or string. There is still a slight delay (normally split of a second or less than a second).

If you want to truly display the dialog only after the WebView content is fully loaded, you will have to show the dialog inside onPageFinished of WebViewClient. I have tested this and it worked fine with ICS, but of course with a slight delay from the time the user activated the dialog and the availability of WebView content. If the slight delay is fine then this solution could be for you.

You could modify your code this way (Updated to include setWebViewClient):

WebView myMsg = new WebView(context);

AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setIcon(R.drawable.ic_launcher);
builder.setView(myMsg);
builder.setTitle("Some Title");
final AlertDialog dialog = builder.create();

// hook a listener for page load complete
WebViewClient webviewclient = new WebViewClient() {
    @Override  
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);
        dialog.show(); // show the dialog here
    }
};
myMsg.setWebViewClient(webviewclient);

myMsg.loadUrl("file:///android_asset/page.html");
WindowManager.LayoutParams lp = dialog.getWindow().getAttributes();
lp.windowAnimations = R.style.AnimateDialog;

//dialog.show(); // do not show until content is truly available
dialog.getWindow().setAttributes(lp);

I have tested it with my Nexus 7 with Android 4.3, but the showing of the content is still delayed by the system animation time just like I had discussed in my previous suggestion so you will have to solve the same problem I had on Android 4.3.