Android WebView: gestion des changements d'orientation

147

Le problème est la performance après la rotation. Le WebView doit recharger la page, ce qui peut être un peu fastidieux.

Quelle est la meilleure façon de gérer un changement d'orientation sans recharger la page à partir de la source à chaque fois?

glennanthonyb
la source
3
Par "rechargement à partir de la source", voulez-vous dire qu'il télécharge à nouveau la page, ou simplement la restitue?
Jeremy Logan
La solution référencée par Blackhex a résolu mon problème.
Italo Borssatto
6
Je me demande en quoi mon application est si spéciale qu'aucune de ces réponses ne fonctionne ...
RobinJ

Réponses:

91

Si vous ne souhaitez pas que WebView se recharge lors des changements d'orientation, remplacez simplement onConfigurationChanged dans votre classe Activity:

@Override
public void onConfigurationChanged(Configuration newConfig){        
    super.onConfigurationChanged(newConfig);
}

Et définissez l'attribut android: configChanges dans le manifeste:

<activity android:name="..."
          android:label="@string/appName"
          android:configChanges="orientation|screenSize"

pour plus d'informations, voir:
http://developer.android.com/guide/topics/resources/runtime-changes.html#HandlingTheChange

https://developer.android.com/reference/android/app/Activity.html#ConfigurationChanges

Totach
la source
4
J'ai testé mais trouvé que cela ne fonctionne pas, définir l'attribut configChanges comme ci-dessous fonctionnera. android: configChanges = "keyboardHidden | orientation"
virsir
18
faire cela est en fait découragé, comme cela a été mentionné à maintes reprises
Matthias
3
Matthias: Ce n'est pas vraiment vrai - voir Javadoc pour WebView.
krtek
Deux choses à noter à propos de cette solution: 1) Si vous avez une activité abstraite avec une WebView, l' configChangesattribut est ajouté à la sous-classe Activité 2) Si votre application dépend de plusieurs projets, l' configChangesattribut est ajouté au manifeste de celui au en haut de l'arborescence des dépendances (qui peut ne pas être le projet contenant la classe Activity).
Amanda S
4
Un tel onConfigurationChangedremplacement de méthode est inutile.
Miha_x64
69

Edit: cette méthode ne fonctionne plus comme indiqué dans la documentation


Réponse originale:

Cela peut être géré en écrasant onSaveInstanceState(Bundle outState)votre activité et en appelant à saveStatepartir de la vue Web:

   protected void onSaveInstanceState(Bundle outState) {
      webView.saveState(outState);
   }

Ensuite, récupérez-le dans votre onCreate après que la vue Web ait été regonflée bien sûr:

public void onCreate(final Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.blah);
   if (savedInstanceState != null)
      ((WebView)findViewById(R.id.webview)).restoreState(savedInstanceState);
}
KWright
la source
26
Cela ne fonctionne pas sur le changement d'orientation - un écran vide est affiché. Les méthodes saveState \ restoreState sont appelées mais cela n'aide pas.
Oleksii Malovanyi
1
Voici l'extrait de code onCreate: setContentView (R.layout.webview); mWebView = (WebView) findViewById (R.id.webview); if (savedInstanceState == null) {mWebView.getSettings (). setJavaScriptEnabled (true); mWebView.setWebViewClient (nouveau SimpleWebViewClient ()); mWebView.loadUrl (Consts.URL_AUTHORIZATION_OAUTH); } else {mWebView.restoreState (savedInstanceState); }
Oleksii Malovanyi
12
De la documentation de saveState: Veuillez noter que cette méthode ne stocke plus les données d'affichage pour cette WebView. Le comportement précédent pourrait potentiellement fuir des fichiers ...
brillenheini
5
"Veuillez noter que cette méthode ne stocke plus les données d'affichage pour cette WebView" - developer.android.com/reference/android/webkit/WebView.html
Steven Mark Ford
Quelqu'un a-t-il des suggestions sur ce qu'il faut faire maintenant que "cette méthode ne stocke plus les données d'affichage pour cette WebView" afin d'empêcher le rechargement d'un formulaire WebView?
Lucas P.
24

La meilleure réponse à cela est de suivre la documentation Android trouvée ici Fondamentalement, cela empêchera Webview de se recharger:

<activity android:name=".MyActivity"
      android:configChanges="keyboardHidden|orientation|screenSize|layoutDirection|uiMode"
      android:label="@string/app_name">

Vous pouvez éventuellement corriger les anomalies (le cas échéant) en remplaçant onConfigurationChangeddans l'activité:

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    // Checks the orientation of the screen
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
    }
}
Tinashe
la source
6
Vous n'avez pas besoin de la méthode onConfigurationChanged (), j'en suis presque sûr, juste l'attribut supplémentaire dans le manifeste. Je suppose que le support natif pour cela a été fourni par WebView, et c'est pourquoi cette réponse est maintenant la meilleure (car cela n'a pas fonctionné lorsque la question a été posée à l'origine)
Booger
1
J'approuve @Booger, vous n'avez pas besoin de remplacer la méthode onConfigurationChanged (). Tout ce dont vous avez besoin est d'ajouter android: configChanges = "orientation | screenSize" dans le fichier manifest.xml.
ozanurkan
5

J'ai essayé d'utiliser onRetainNonConfigurationInstance (retournant le WebView ), puis de le récupérer avec getLastNonConfigurationInstance pendant onCreate et de le réattribuer.

Cela ne semble pas encore fonctionner. Je ne peux pas m'empêcher de penser que je suis vraiment proche! Jusqu'à présent, j'obtiens juste une WebView vierge / fond blanc à la place. Publier ici dans l'espoir que quelqu'un puisse aider à pousser celui-ci au-delà de la ligne d'arrivée.

Peut-être que je ne devrais pas passer le WebView . Peut-être un objet de la WebView ?

L'autre méthode que j'ai essayée - pas ma préférée - est de définir ceci dans l'activité:

 android:configChanges="keyboardHidden|orientation"

... et puis ne faites pratiquement rien ici:

@Override
public void onConfigurationChanged(Configuration newConfig) {
  super.onConfigurationChanged(newConfig);
  // We do nothing here. We're only handling this to keep orientation
  // or keyboard hiding from causing the WebView activity to restart.
}

CELA fonctionne, mais cela pourrait ne pas être considéré comme une meilleure pratique .

En attendant, j'ai également un seul ImageView que je souhaite mettre à jour automatiquement en fonction de la rotation. Cela s'avère très facile. Dans mon resdossier, j'ai drawable-landet je dois drawable-portcontenir des variations paysage / portrait, puis j'utilise R.drawable.myimagenamepour la source ImageView et Android "fait ce qu'il faut" - yay!

... sauf quand vous regardez les changements de configuration, alors ce n'est pas le cas. :(

Donc je suis en désaccord. Utilisez onRetainNonConfigurationInstance et les ImageView travaux de rotation, mais WebView persistance ne pas ... ou l' utilisation onConfigurationChanged et WebView reste stable, mais le ImageView ne met pas à jour. Que faire?

Une dernière remarque: dans mon cas, forcer l'orientation n'est pas un compromis acceptable. Nous voulons vraiment soutenir gracieusement la rotation. Un peu comme le fait l'application Navigateur Android! ;)

Joe D'Andrea
la source
7
Vous ne devez renvoyer aucune vue depuis onRetainNonConfigurationInstance. Les vues sont attachées au contexte d'activité, donc si vous enregistrez la vue, vous transportez tous ces bagages à chaque fois qu'il y a un changement d'orientation. La vue est "fuyée". (Voir le dernier paragraphe ici: developer.android.com/resources/articles/… ) La meilleure pratique pour onRetainNonConfigurationInstance consiste à enregistrer les données dont la vue a besoin, pas la vue elle-même.
Declan Shanaghy
3

Un compromis est d'éviter la rotation. Ajoutez ceci pour corriger l'activité pour l'orientation Portrait uniquement.

android:screenOrientation="portrait"
Jacques René Mesrine
la source
Cela fonctionne réellement dans de nombreux cas et c'est la solution que j'ai décidé de mettre en œuvre :-)
Abdo
1
hehe, ceci est un changement d'exigence
Max Ch
3

Meilleur moyen de gérer les changements d'orientation et d'empêcher le rechargement de WebView lors de la rotation.

@Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
}

Dans cet esprit, pour éviter que onCreate () ne soit appelé à chaque fois que vous changez d'orientation, vous devrez ajouter android:configChanges="orientation|screenSize" to the AndroidManifest.

ou juste ..

android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"`
Amit raj
la source
3

Écrivez simplement les lignes de code suivantes dans votre fichier Manifest - rien d'autre. Ça marche vraiment:

<activity android:name=".YourActivity"
  android:configChanges="orientation|screenSize"
  android:label="@string/application_name">
Eddie Valmont
la source
3

J'apprécie que ce soit un peu tard, mais c'est la réponse que j'ai utilisée lors du développement de ma solution:

AndroidManifest.xml

    <activity
        android:name=".WebClient"
        android:configChanges="keyboard|keyboardHidden|orientation|screenSize" <--- "screenSize" important
        android:label="@string/title_activity_web_client" >
    </activity>

WebClient.java

public class WebClient extends Activity {

    protected FrameLayout webViewPlaceholder;
    protected WebView webView;

    private String WEBCLIENT_URL;
    private String WEBCLIENT_TITLE;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_web_client);
        initUI();
    }

    @SuppressLint("SetJavaScriptEnabled")
    protected void initUI(){
        // Retrieve UI elements
        webViewPlaceholder = ((FrameLayout)findViewById(R.id.webViewPlaceholder));

        // Initialize the WebView if necessary
        if (webView == null)
        {
            // Create the webview
            webView = new WebView(this);
            webView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
            webView.getSettings().setSupportZoom(true);
            webView.getSettings().setBuiltInZoomControls(true);
            webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
            webView.setScrollbarFadingEnabled(true);
            webView.getSettings().setJavaScriptEnabled(true);
            webView.getSettings().setPluginState(android.webkit.WebSettings.PluginState.ON);
            webView.getSettings().setLoadsImagesAutomatically(true);

            // Load the URLs inside the WebView, not in the external web browser
            webView.setWebViewClient(new SetWebClient());
            webView.setWebChromeClient(new WebChromeClient());

            // Load a page
            webView.loadUrl(WEBCLIENT_URL);
        }

        // Attach the WebView to its placeholder
        webViewPlaceholder.addView(webView);
    }

    private class SetWebClient extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url);
            return true;
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.web_client, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }else if(id == android.R.id.home){
            finish();
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onBackPressed() {
        if (webView.canGoBack()) {
            webView.goBack();
            return;
        }

        // Otherwise defer to system default behavior.
        super.onBackPressed();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig){
        if (webView != null){
            // Remove the WebView from the old placeholder
            webViewPlaceholder.removeView(webView);
        }

        super.onConfigurationChanged(newConfig);

        // Load the layout resource for the new configuration
        setContentView(R.layout.activity_web_client);

        // Reinitialize the UI
        initUI();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState){
        super.onSaveInstanceState(outState);

        // Save the state of the WebView
        webView.saveState(outState);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState){
        super.onRestoreInstanceState(savedInstanceState);

        // Restore the state of the WebView
        webView.restoreState(savedInstanceState);
    }

}
David Passmore
la source
2

Vous pouvez essayer d'utiliser onSaveInstanceState()et onRestoreInstanceState()sur votre activité pour appeler saveState(...)et restoreState(...)sur votre instance WebView.

deltaearl
la source
2

Nous sommes en 2015 et de nombreuses personnes recherchent une solution qui fonctionne toujours sur les téléphones Jellybean, KK et Lollipop. Après beaucoup de difficultés, j'ai trouvé un moyen de préserver la vue Web intacte après avoir changé d'orientation. Ma stratégie consiste essentiellement à stocker la vue Web dans une variable statique distincte dans une autre classe. Ensuite, si une rotation se produit, je détache la vue Web de l'activité, attend la fin de l'orientation et rattache la vue Web à l'activité. Par exemple ... mettez d'abord ceci sur votre MANIFEST (keyboardHidden et keyboard sont facultatifs):

<application
        android:label="@string/app_name"
        android:theme="@style/AppTheme"
        android:name="com.myapp.abc.app">

    <activity
            android:name=".myRotatingActivity"
            android:configChanges="keyboard|keyboardHidden|orientation">
    </activity>

Dans une CLASSE D'APPLICATION SÉPARÉE, mettez:

     public class app extends Application {
            public static WebView webview;
            public static FrameLayout webviewPlaceholder;//will hold the webview

         @Override
               public void onCreate() {
                   super.onCreate();
    //dont forget to put this on the manifest in order for this onCreate method to fire when the app starts: android:name="com.myapp.abc.app"
                   setFirstLaunch("true");
           }

       public static String isFirstLaunch(Context appContext, String s) {
           try {
          SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(appContext);
          return prefs.getString("booting", "false");
          }catch (Exception e) {
             return "false";
          }
        }

    public static void setFirstLaunch(Context aContext,String s) {
       SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(aContext);
            SharedPreferences.Editor editor = prefs.edit();
            editor.putString("booting", s);
            editor.commit();
           }
        }

Dans l'activité mettez:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if(app.isFirstLaunch.equals("true"))) {
            app.setFirstLaunch("false");
            app.webview = new WebView(thisActivity);
            initWebUI("www.mypage.url");
        }
}
@Override
    public  void onRestoreInstanceState(Bundle savedInstanceState) {
        restoreWebview();
    }

public void restoreWebview(){
        app.webviewPlaceholder = (FrameLayout)thisActivity.findViewById(R.id.webviewplaceholder);
        if(app.webviewPlaceholder.getParent()!=null&&((ViewGroup)app.webview.getParent())!=null) {
            ((ViewGroup) app.webview.getParent()).removeView(app.webview);
        }
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.FILL_PARENT);
        app.webview.setLayoutParams(params);
        app.webviewPlaceholder.addView(app.webview);
        app.needToRestoreWebview=false;
    }

protected static void initWebUI(String url){
        if(app.webviewPlaceholder==null);
          app.webviewPlaceholder = (FrameLayout)thisActivity.findViewById(R.id.webviewplaceholder);
        app.webview.getSettings().setJavaScriptEnabled(true);       app.webview.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
        app.webview.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
        app.webview.getSettings().setSupportZoom(false);
        app.webview.getSettings().setBuiltInZoomControls(true);
        app.webview.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
        app.webview.setScrollbarFadingEnabled(true);
        app.webview.getSettings().setLoadsImagesAutomatically(true);
        app.webview.loadUrl(url);
        app.webview.setWebViewClient(new WebViewClient());
        if((app.webview.getParent()!=null)){//&&(app.getBooting(thisActivity).equals("true"))) {
            ((ViewGroup) app.webview.getParent()).removeView(app.webview);
        }
        app.webviewPlaceholder.addView(app.webview);
    }

Enfin, le simple XML:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".myRotatingActivity">
    <FrameLayout
        android:id="@+id/webviewplaceholder"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
</RelativeLayout>

Il y a plusieurs choses qui pourraient être améliorées dans ma solution, mais j'ai déjà passé beaucoup de temps, par exemple: un moyen plus court de valider si l'activité a été lancée pour la toute première fois au lieu d'utiliser le stockage SharedPreferences. Cette approche préserve votre vue Web intacte (afaik), ses zones de texte, ses étiquettes, son interface utilisateur, ses variables javascript et ses états de navigation qui ne sont pas reflétés par l'URL.

Josh
la source
1
Pourquoi même prendre la peine de détacher et de rattacher la WebView alors qu'elle n'est jamais détruite en premier lieu puisque vous gérez manuellement les modifications de configuration?
Fred
@Fred Parce que je veux garder intact l'état exact de la Webview et de son contenu, y compris les cookies, et l'état des boutons et des cases à cocher implémentés avec HTML5 ou JS à l'intérieur de la page Web chargée par la Webview. Le seul ajustement que je fais est le redimensionnement.
Josh
Fred voulait dire que vous n'avez pas besoin de détacher / rattacher la vue Web via une classe statique comme vous avez déclaré configChanges dans le manifeste. De cette façon, vos vues et votre activité ne sont en aucun cas détruites pendant la rotation.
Eugene Kartoyev
2

La seule chose que vous devriez faire est d'ajouter ce code à votre fichier manifeste:

<activity android:name=".YourActivity"
      android:configChanges="orientation|screenSize"
      android:label="@string/application_name">
Les Paul
la source
2

Mise à jour: la stratégie actuelle consiste à déplacer l'instance WebView vers la classe Application au lieu du fragment conservé lorsqu'elle est détachée et de la rattacher au redémarrage comme le fait Josh. Pour empêcher l'application de se fermer, vous devez utiliser le service de premier plan si vous souhaitez conserver l'état lorsque l'utilisateur bascule entre les applications.

Si vous utilisez des fragments, vous pouvez utiliser une instance de conservation de WebView. La vue Web sera conservée comme membre d'instance de la classe. Vous devez cependant attacher la vue Web dans OnCreateView et la détacher avant OnDestroyView pour l'empêcher de se détruire avec le conteneur parent.

class MyFragment extends Fragment{  

    public MyFragment(){  setRetainInstance(true); }

    private WebView webView;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
       View v = ....
       LinearLayout ll = (LinearLayout)v.findViewById(...);
       if (webView == null) {
            webView = new WebView(getActivity().getApplicationContext()); 
       }
       ll.removeAllViews();
       ll.addView(webView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));

       return v;
    }

    @Override
    public void onDestroyView() {
        if (getRetainInstance() && webView.getParent() instanceof ViewGroup) {
           ((ViewGroup) webView.getParent()).removeView(webView);
        }
        super.onDestroyView();
    } 
}

Les crédits PS vont à la réponse de kcoppock

Quant à 'SaveState ()', il ne fonctionne plus selon la documentation officielle :

Veuillez noter que cette méthode ne stocke plus les données d'affichage pour cette WebView. Le comportement précédent pouvait potentiellement fuir des fichiers si restoreState (Bundle) n'était jamais appelé.

Boris Treukhov
la source
Notez que si setRetainInstancele fragment conserve le fragment, la vue (et donc la WebView) est toujours détruite.
Fred
1
@Fred Pouvez-vous fournir une source pour cette déclaration? J'ai testé cela dans une petite application qui a un fragment avec setRetainInstance (true) qui affiche une page youtube et si je change l'orientation, la vue semble être conservée car la vidéo n'est pas interrompue. Il ne semble pas que la vue Web soit détruite.
Pinkerton
@Rai C'était juste une erreur de lecture, j'ai un peu édité la réponse depuis (il faut encore quelques corrections de formulation). Si vous êtes intéressé par l'état actuel des choses, cette méthode fonctionne. Cependant, le problème est qu'android tue l'application lorsqu'il manque de mémoire. J'ai un formulaire de saisie important où l'utilisateur doit lire un message dans une autre application, j'ai donc dû utiliser un service de premier plan avec notification pour éviter que le processus ne soit tué. Pourtant, l'androïde détruit toujours l'activité et donc le cadre conservé. Mais l'instance de la classe Application est préservée - je déplace donc WebView vers Application maintenant.
Boris Treukhov
1
J'utilise également MutableContextWrapper comme base du WebView, je change de contextes en activités sur attacher et détacher. Un autre gros problème que Chromium construit dans les contrôles de zoom enregistre la référence à l'ancienne activité, donc pas de zoom dans la solution avec WebView attaché à l'application au lieu de l'activité. TL, DR; si votre formulaire n'est pas si important et que vous pouvez vivre avec le fait qu'après le changement d'application, votre processus peut être tué et la vue Web non restaurée, utilisez la solution de cadre conservé. PS aussi je pense que les employés de Google sont loin, loin des problèmes de la vie réelle avec tous leurs widgets-gadgets ..
Boris Treukhov
1
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);    
}

@Override
protected void onRestoreInstanceState(Bundle state) {
    super.onRestoreInstanceState(state);    
}

Ces méthodes peuvent être remplacées sur n'importe quelle activité, cela vous permet simplement d'enregistrer et de restaurer des valeurs chaque fois qu'une activité est créée / détruite, lorsque l'orientation de l'écran change, l'activité est détruite et recréée en arrière-plan, vous pouvez donc utiliser ces méthodes aux états de stockage / restauration temporaires pendant la modification.

Vous devriez examiner plus en détail les deux méthodes suivantes et voir si cela correspond à votre solution.

http://developer.android.com/reference/android/app/Activity.html

Chiwai Chan
la source
1

La meilleure solution que j'ai trouvée pour faire cela sans divulguer la Activityréférence précédente et sans définir le configChanges.. est d'utiliser un MutableContextWrapper .

J'ai implémenté ceci ici: https://github.com/slightfoot/android-web-wrapper/blob/48cb3c48c457d889fc16b4e3eba1c9e925f42cfb/WebWrapper/src/com/example/webwrapper/BrowserActivity.java

Simon
la source
cela fonctionne-t-il de manière fiable sans effets secondaires? Je n'ai pas trouvé beaucoup d'informations sur cette méthode.
craigrs84
1

C'est la seule chose qui a fonctionné pour moi (j'ai même utilisé l'état de sauvegarde de l'instance onCreateViewmais ce n'était pas aussi fiable).

public class WebViewFragment extends Fragment
{
    private enum WebViewStateHolder
    {
        INSTANCE;

        private Bundle bundle;

        public void saveWebViewState(WebView webView)
        {
            bundle = new Bundle();
            webView.saveState(bundle);
        }

        public Bundle getBundle()
        {
            return bundle;
        }
    }

    @Override
    public void onPause()
    {
        WebViewStateHolder.INSTANCE.saveWebViewState(myWebView);
        super.onPause();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState)
    {
        View rootView = inflater.inflate(R.layout.fragment_main, container, false); 
        ButterKnife.inject(this, rootView);
        if(WebViewStateHolder.INSTANCE.getBundle() == null)
        {
            StringBuilder stringBuilder = new StringBuilder();
            BufferedReader br = null;
            try
            {
                br = new BufferedReader(new InputStreamReader(getActivity().getAssets().open("start.html")));

                String line = null;
                while((line = br.readLine()) != null)
                {
                    stringBuilder.append(line);
                }
            }
            catch(IOException e)
            {
                Log.d(getClass().getName(), "Failed reading HTML.", e);
            }
            finally
            {
                if(br != null)
                {
                    try
                    {
                        br.close();
                    }
                    catch(IOException e)
                    {
                        Log.d(getClass().getName(), "Kappa", e);
                    }
                }
            }
            myWebView
                .loadDataWithBaseURL("file:///android_asset/", stringBuilder.toString(), "text/html", "utf-8", null);
        }
        else
        {
            myWebView.restoreState(WebViewStateHolder.INSTANCE.getBundle());
        }
        return rootView;
    }
}

J'ai fait un support Singleton pour l'état de la WebView. L'état est conservé tant que le processus de l'application existe.

EDIT: Ce loadDataWithBaseURLn'était pas nécessaire, cela fonctionnait aussi bien avec juste

    //in onCreate() for Activity, or in onCreateView() for Fragment

    if(WebViewStateHolder.INSTANCE.getBundle() == null) {
        webView.loadUrl("file:///android_asset/html/merged.html");
    } else {
        webView.restoreState(WebViewStateHolder.INSTANCE.getBundle());
    }

Bien que j'aie lu, cela ne fonctionne pas nécessairement bien avec les cookies.

EpicPandaForce
la source
L'utilisation de singleton est problématique, car elle suppose que vous n'avez qu'une seule instance à utiliser.
développeur android
@androiddeveloper ah. Eh bien, si ce n'est pas le cas dans votre cas, c'est effectivement un problème. Je n'avais qu'une seule instance WebView, donc cette question ne m'est même pas venue.
EpicPandaForce
1

essaye ça

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class MainActivity extends AppCompatActivity {

    private WebView wv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        wv = (WebView) findViewById(R.id.webView);
        String url = "https://www.google.ps/";

        if (savedInstanceState != null)
            wv.restoreState(savedInstanceState);
        else {
            wv.setWebViewClient(new MyBrowser());
            wv.getSettings().setLoadsImagesAutomatically(true);
            wv.getSettings().setJavaScriptEnabled(true);
            wv.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
            wv.loadUrl(url);
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        wv.saveState(outState);
    }

    @Override
    public void onBackPressed() {
        if (wv.canGoBack())
            wv.goBack();
        else
            super.onBackPressed();
    }

    private class MyBrowser extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url);
            return true;
        }
    }

}
Anwar SE
la source
0

Cette page résout mon problème mais je dois faire un léger changement dans la première:

    protected void onSaveInstanceState(Bundle outState) {
       webView.saveState(outState);
       }

Cette partie a un léger problème pour moi cela. Lors du deuxième changement d'orientation, l'application s'est terminée avec un pointeur nul

en utilisant cela, cela a fonctionné pour moi:

    @Override
protected void onSaveInstanceState(Bundle outState ){
    ((WebView) findViewById(R.id.webview)).saveState(outState);
}
gyanu
la source
semble être exactement le même. Pourquoi le second fonctionnerait-il alors que le précédent ne le ferait pas?
développeur android
0

Vous devriez essayer ceci:

  1. Créez un service, à l'intérieur de ce service, créez votre WebView.
  2. Démarrez le service à partir de votre activité et liez-y.
  3. Dans la onServiceConnectedméthode, récupérez WebView et appelez la setContentViewméthode pour rendre votre WebView.

Je l'ai testé et cela fonctionne mais pas avec d'autres WebViews comme XWalkView ou GeckoView.

Ramdane Oualitsen
la source
Salut! Pouvez-vous l'expliquer davantage? Merci
Jaco
1
Eh bien, obtenez votre instance de vue Web de votre activité et essayez de mettre sa référence à un service qui s'exécute en arrière-plan. La prochaine fois que vous ouvrirez cette activité, essayez d'abord d'obtenir la vue Web enregistrée et de la restituer.
Ramdane Oualitsen
0
@Override
    protected void onSaveInstanceState(Bundle outState )
    {
        super.onSaveInstanceState(outState);
        webView.saveState(outState);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState)
    {
        super.onRestoreInstanceState(savedInstanceState);
        webView.restoreState(savedInstanceState);
    }
RealDEV
la source
Pourriez-vous ajouter quelques lignes d'explication à votre réponse?
yacc
-1

J'aime cette solution http://www.devahead.com/blog/2012/01/preserving-the-state-of-an-android-webview-on-screen-orientation-change/

Selon lui, nous réutilisons la même instance de WebView. Il permet de sauvegarder l'historique de navigation et de faire défiler la position lors du changement de configuration.

Taras Shevchuk
la source
Le lien ci-dessus mène à une page qui ne peut pas être consultée sans installer un «plugin de sécurité»; éviter.
Allan Bazinet