Android WebView: obsługa zmian orientacji


147

Problemem jest wydajność po rotacji. WebView musi ponownie załadować stronę, co może być trochę uciążliwe.

Jaki jest najlepszy sposób obsługi zmiany orientacji bez ponownego ładowania strony ze źródła za każdym razem?


3
Przez „ponowne ładowanie ze źródła” masz na myśli ponowne pobranie strony, czy tylko ponowne jej renderowanie?
Jeremy Logan

Rozwiązanie, do którego odwołuje się Blackhex, rozwiązało mój problem.
Italo Borssatto

6
Zastanawiam się, co do cholery jest takiego specjalnego w mojej aplikacji, że żadna z tych odpowiedzi nie działa ...
RobinJ

Odpowiedzi:


91

Jeśli nie chcesz, aby WebView ponownie ładował się po zmianach orientacji, po prostu zastąp onConfigurationChanged w swojej klasie Activity:

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

I ustaw atrybut android: configChanges w manifeście:

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

aby uzyskać więcej informacji, zobacz:
http://developer.android.com/guide/topics/resources/runtime-changes.html#HandlingTheChange

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


4
Testowałem, ale stwierdziłem, że nie działa, ustaw atrybut configChanges, jak poniżej, będzie działać. android: configChanges = "keyboardHidden | Orientacja"
virsir

18
robienie tego jest de facto odradzane, jak już wielokrotnie wspominano
Matthias

3
Matthias: To nieprawda - zobacz Javadoc dla WebView.
krtek

Dwie rzeczy, na które należy zwrócić uwagę na temat tego rozwiązania: 1) Jeśli masz abstrakcyjne działanie z WebView, configChangesatrybut zostanie dodany do podklasy Działanie 2) Jeśli twoja aplikacja zależy od wielu projektów, configChangesatrybut zostanie dodany do manifestu tego w wierzchołek drzewa zależności (który może nie być projektem zawierającym klasę Activity).
Amanda S,

4
Takie onConfigurationChangednadpisanie metody jest bezużyteczne.
Miha_x64

69

Edycja: ta metoda nie działa już zgodnie z opisem w dokumentacji


Oryginalna odpowiedź:

Można to zrobić, nadpisując onSaveInstanceState(Bundle outState)swoją aktywność i dzwoniąc saveStatez widoku internetowego:

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

Następnie odzyskaj to w swoim onCreate po ponownym zawyżeniu widoku internetowego:

public void onCreate(final Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.blah);
   if (savedInstanceState != null)
      ((WebView)findViewById(R.id.webview)).restoreState(savedInstanceState);
}

26
Nie działa to przy zmianie orientacji - wyświetlany jest pusty ekran. metody saveState \ restoreState są wywoływane, ale to nie pomaga.
Oleksii Malovanyi

1
Oto fragment kodu onCreate: setContentView (R.layout.webview); mWebView = (WebView) findViewById (R.id.webview); if (saveInstanceState == null) {mWebView.getSettings (). setJavaScriptEnabled (true); mWebView.setWebViewClient (nowy SimpleWebViewClient ()); mWebView.loadUrl (Consts.URL_AUTHORIZATION_OAUTH); } else {mWebView.restoreState (saveInstanceState); }
Oleksii Malovanyi

12
Z dokumentacji saveState: Zwróć uwagę, że ta metoda nie przechowuje już danych wyświetlania dla tego WebView. Poprzednie zachowanie mogło potencjalnie spowodować wyciek plików ...
brillenheini,

5
„Należy pamiętać, że ta metoda nie przechowuje już danych wyświetlania dla tego WebView” - developer.android.com/reference/android/webkit/WebView.html
Steven Mark Ford

Czy ktoś ma jakieś sugestie, co teraz zrobić, że „ta metoda nie przechowuje już danych wyświetlania dla tego WebView”, aby zapobiec przeładowaniu formularza WebView?
Lucas P.

24

Najlepszą odpowiedzią na to jest poniższa dokumentacja Androida, którą znajdziesz tutaj. Zasadniczo zapobiegnie to ponownemu załadowaniu Webview:

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

Opcjonalnie możesz naprawić anomalie (jeśli występują), nadpisując onConfigurationChangeddziałanie:

@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();
    }
}

6
Nie potrzebujesz metody onConfigurationChanged (), jestem pewien, tylko dodatkowy atrybut w manifeście. Domyślam się, że natywne wsparcie dla tego zostało zapewnione przez WebView i dlatego ta odpowiedź jest teraz najlepsza (ponieważ to nie zadziałało, gdy pierwotnie zadano pytanie)
Booger

1
Akceptuję @Booger, nie musisz nadpisywać metody onConfigurationChanged (). Wszystko, czego potrzebujesz, to dodać android: configChanges = "orientacja | screenSize" w pliku manifest.xml.
ozanurkan

5

Próbowałem użyć onRetainNonConfigurationInstance (zwracając WebView ), a następnie odzyskać go za pomocą getLastNonConfigurationInstance podczas onCreate i ponownie przypisać.

Wydaje się, że jeszcze nie działa. Nie mogę pomóc, ale myślę, że jestem naprawdę blisko! Jak dotąd, po prostu otrzymuję WebView z pustym / białym tłem zamiast tego . Publikowanie tutaj w nadziei, że ktoś może pomóc przepchnąć to poza linię mety.

Może nie powinienem przekazywać WebView . Być może obiekt z WebView ?

Inną metodą, którą wypróbowałem - nie moją ulubioną - jest ustawienie tego w ćwiczeniu:

 android:configChanges="keyboardHidden|orientation"

... a potem prawie nic tutaj nie rób:

@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.
}

TO działa, ale może nie być uważane za najlepszą praktykę .

W międzyczasie mam też pojedynczy ImageView, który chcę automagicznie aktualizować w zależności od rotacji. Okazuje się, że jest to bardzo łatwe. W moim resfolderze mam drawable-landi drawable-portprzechowuję odmiany w poziomie / pionie, a następnie używam R.drawable.myimagenamedo ImageView źródła , a Android „robi to, co należy” - yay!

... z wyjątkiem sytuacji, gdy obserwujesz zmiany konfiguracji, wtedy tak się nie dzieje. :(

Więc jestem w sprzeczności. Użyj onRetainNonConfigurationInstance i rotacji ImageView działa, ale trwałość WebView nie ... lub użyj onConfigurationChanged, a WebView pozostaje stabilny, ale ImageView nie jest aktualizowany. Co robić?

Ostatnia uwaga: w moim przypadku wymuszanie orientacji nie jest akceptowalnym kompromisem. Naprawdę chcemy z wdziękiem wspierać rotację. Trochę jak aplikacja Android Browser! ;)


7
Nie należy zwracać żadnych widoków z onRetainNonConfigurationInstance. Widoki są dołączone do kontekstu działania, więc jeśli zapiszesz widok, będziesz przenosić cały ten bagaż za każdym razem, gdy nastąpi zmiana orientacji. Widok jest „wyciekły”. (Zobacz ostatni akapit tutaj: developer.android.com/resources/articles/ ... ) Najlepszą praktyką w przypadku onRetainNonConfigurationInstance jest zapisanie danych, których potrzebuje widok, a nie sam widok.
Declan Shanaghy

3

Jednym z kompromisów jest unikanie rotacji. Dodaj to, aby naprawić działanie tylko dla orientacji pionowej.

android:screenOrientation="portrait"

To faktycznie działa w wielu przypadkach i jest rozwiązaniem, które zdecydowałem się wdrożyć :-)
Abdo

1
hehe, to jest zmiana wymagań
Max Ch

3

Najlepszy sposób na obsługę zmian orientacji i zapobieganie ponownemu ładowaniu WebView podczas obracania.

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

Mając to na uwadze, aby zapobiec wywoływaniu onCreate () za każdym razem, gdy zmieniasz orientację, musisz dodać android:configChanges="orientation|screenSize" to the AndroidManifest.

Lub tylko ..

android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"`

3

Po prostu napisz następujące linie kodu w swoim pliku manifestu - nic więcej. To naprawdę działa:

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

3

Doceniam, że jest trochę za późno, ale tak brzmi odpowiedź, której użyłem przy tworzeniu mojego rozwiązania:

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);
    }

}

2

Możesz spróbować użyć onSaveInstanceState()i onRestoreInstanceState()na swojej Aktywności, aby zadzwonić saveState(...)i restoreState(...)na swojej instancji WebView.


2

Jest rok 2015 i wiele osób szuka rozwiązania, które nadal działa na telefonach Jellybean, KK i Lollipop. Po wielu zmaganiach znalazłem sposób na zachowanie widoku internetowego w nienaruszonym stanie po zmianie orientacji. Moja strategia polega w zasadzie na przechowywaniu widoku internetowego w oddzielnej zmiennej statycznej w innej klasie. Następnie, jeśli nastąpi obrót, odłączam widok internetowy od działania, czekam na zakończenie orientacji i ponownie przyłączam widok internetowy do działania. Na przykład ... najpierw umieść to w swoim MANIFESTIE (klawiaturaHidden i klawiatura są opcjonalne):

<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>

W ODDZIELNEJ KLASIE ZASTOSOWAŃ umieść:

     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();
           }
        }

W ACTIVITY wpisz:

@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);
    }

Wreszcie prosty 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>

Jest kilka rzeczy, które można by ulepszyć w moim rozwiązaniu, ale spędziłem już za dużo czasu, na przykład: krótszy sposób sprawdzenia, czy działanie zostało uruchomione po raz pierwszy, zamiast korzystania z magazynu SharedPreferences. Takie podejście zachowuje widok sieci Web w stanie nienaruszonym (afaik), jego pola tekstowe, etykiety, interfejs użytkownika, zmienne javascript i stany nawigacji, które nie są odzwierciedlane w adresie URL.


1
Po co nawet zawracać sobie głowę odłączaniem i ponownym podłączaniem WebView, skoro nigdy nie zostanie on zniszczony, skoro ręcznie obsługujesz zmiany konfiguracji?
Fred

@Fred Ponieważ chcę zachować dokładny stan Webview i jego zawartość nienaruszoną, w tym pliki cookie, a także stan przycisków i pól wyboru zaimplementowanych za pomocą HTML5 lub JS na stronie ładowanej przez przeglądarkę. Jedyne dostosowanie, które robię, to zmiana rozmiaru.
Josh

Fred miał na myśli, że nie musisz odłączać / ponownie dołączać widoku internetowego za pośrednictwem statycznej klasy, ponieważ zadeklarowałeś configChanges w manifeście. W ten sposób Twoje widoki i aktywność w żadnym wypadku nie zostaną zniszczone podczas rotacji.
Eugene Kartoyev

2

Jedyne, co powinieneś zrobić, to dodać ten kod do pliku manifestu:

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

2

Aktualizacja: obecna strategia polega na przeniesieniu instancji WebView do klasy Application zamiast zachowywanego fragmentu po odłączeniu i ponownym dołączeniu przy wznowieniu, tak jak robi to Josh. Aby zapobiec zamykaniu aplikacji, należy używać usługi pierwszego planu, jeśli chcesz zachować stan, gdy użytkownik przełącza się między aplikacjami.

Jeśli używasz fragmentów, możesz użyć zachowywania wystąpienia WebView. Widok sieciowy zostanie zachowany jako element członkowski instancji klasy. Należy jednak dołączyć widok sieci Web w OnCreateView i odłączyć przed OnDestroyView, aby zapobiec jego zniszczeniu za pomocą kontenera nadrzędnego.

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();
    } 
}

Kredyty PS przejdź do odpowiedzi kcoppock

Jeśli chodzi o 'SaveState ()', nie działa już zgodnie z oficjalną dokumentacją :

Należy pamiętać, że ta metoda nie przechowuje już danych wyświetlania dla tego WebView. Poprzednie zachowanie mogło potencjalnie spowodować wyciek plików, jeśli funkcja restoreState (pakiet) nigdy nie została wywołana.


Zwróć uwagę, że while setRetainInstancezachowuje fragment, widok (a tym samym WebView) nadal jest niszczony.
Fred

1
@Fred Czy możesz podać źródło tego oświadczenia? Przetestowałem to w małej aplikacji, która ma fragment z setRetainInstance (true), który wyświetla stronę youtube i jeśli zmienię orientację, widok wydaje się być zachowany, ponieważ wideo nie jest przerywane. Wygląda na to, że widok nie jest niszczony.
Pinkerton

@Rai To był tylko błąd w odczytaniu, od tamtej pory trochę poprawiłem odpowiedź (nadal wymaga poprawek sformułowań). Jeśli interesuje Cię aktualny stan rzeczy - ta metoda działa. Problem polega jednak na tym, że Android zabija aplikację, gdy brakuje pamięci. Mam ważny formularz wejściowy, w którym użytkownik musi przeczytać wiadomość w innej aplikacji, więc musiałem użyć usługi pierwszego planu z powiadomieniem, aby zapobiec zabiciu procesu. Jednak android nadal niszczy aktywność, a tym samym zachowaną klatkę. Ale instancja klasy Application jest zachowywana - więc teraz przenoszę WebView do Application.
Boris Treukhov

1
Używam również MutableContextWrapper jako podstawy WebView, przełączam konteksty na działania przy podłączaniu i odłączaniu. Kolejny duży problem polegający na tym, że Chromium tworzy elementy sterujące powiększeniem, zapisuje odniesienie do starej aktywności, więc nie ma rozwiązania powiększającego z WebView dołączonym do aplikacji zamiast działania. TL; DR; jeśli twój formularz nie jest tak ważny i możesz żyć z faktem, że po przełączeniu aplikacji twój proces może zostać zabity, a przeglądarka internetowa nie zostanie przywrócona, skorzystaj z rozwiązania z zachowaną ramką. PS też myślę, że pracownicy Google są daleko, daleko od problemów z prawdziwego życia z wszystkimi ich
widżetami

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

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

Metody te można nadpisać w dowolnym działaniu, w zasadzie pozwala to na zapisywanie i przywracanie wartości za każdym razem, gdy działanie jest tworzone / niszczone, gdy zmienia się orientacja ekranu, działanie zostaje zniszczone i odtworzone w tle, więc możesz użyć tych metod do tymczasowego przechowywania / przywracania stanów podczas zmiany.

Powinieneś dokładniej przyjrzeć się dwóm następującym metodom i sprawdzić, czy pasuje do Twojego rozwiązania.

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


1

Najlepszym rozwiązaniem, jakie znalazłem, aby to zrobić bez wycieku poprzedniego Activityodniesienia i bez ustawiania configChanges.., jest użycie MutableContextWrapper .

Zaimplementowałem to tutaj: https://github.com/slightfoot/android-web-wrapper/blob/48cb3c48c457d889fc16b4e3eba1c9e925f42cfb/WebWrapper/src/com/example/webwrapper/BrowserActivity.java


czy to działa niezawodnie bez skutków ubocznych? Nie znalazłem zbyt wielu informacji na temat tej metody.
craigrs84

1

To jedyna rzecz, która działała dla mnie (użyłem nawet stanu zapisywania instancji w, onCreateViewale nie był on tak niezawodny).

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;
    }
}

Zrobiłem uchwyt Singleton dla stanu WebView. Stan jest zachowywany tak długo, jak trwa proces aplikacji.

EDYCJA: Nie loadDataWithBaseURLbyło to konieczne, działało równie dobrze z just

    //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());
    }

Chociaż czytałem, to niekoniecznie działa dobrze z plikami cookie.


Korzystanie z singletona jest problematyczne, ponieważ zakłada, że ​​masz tylko jedną instancję, której musisz użyć.
programista Androida

@androiddeveloper ah. Cóż, jeśli nie jest to prawdą w twoim przypadku, to rzeczywiście jest problem. Miałem tylko jedną instancję WebView, więc to pytanie nawet mi się nie pojawiło.
EpicPandaForce

1

Spróbuj tego

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;
        }
    }

}

0

Ta strona rozwiązuje mój problem, ale muszę wprowadzić niewielką zmianę w pierwszej:

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

Ta porcja ma dla mnie mały problem z tym. W drugiej orientacji zmień aplikację zakończoną wskaźnikiem zerowym

używając tego, zadziałało dla mnie:

    @Override
protected void onSaveInstanceState(Bundle outState ){
    ((WebView) findViewById(R.id.webview)).saveState(outState);
}

wydaje się być dokładnie tym samym. Dlaczego drugi miałby działać, podczas gdy poprzedni nie?
programista Androida

0

Powinieneś spróbować tego:

  1. Utwórz usługę, w ramach tej usługi utwórz WebView.
  2. Uruchom usługę na podstawie swojej aktywności i powiąż ją.
  3. W onServiceConnectedmetodzie pobierz WebView i wywołaj setContentViewmetodę, aby renderować swój WebView.

Przetestowałem to i działa, ale nie z innymi WebView, takimi jak XWalkView czy GeckoView.


Cześć! Czy mógłbyś to bardziej wyjaśnić? Dziękuję
Jaco

1
Cóż, pobierz swoją instancję Webview z Aktywności i spróbuj umieścić jej odniesienie do usługi działającej w tle. Następnym razem, gdy otworzysz to działanie, spróbuj najpierw pobrać zapisany widok sieciowy i wyrenderować go.
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);
    }

Czy możesz dodać kilka linijek wyjaśnienia do swojej odpowiedzi?
yacc

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.