it-swarm.com.de

Android WebView: Handhabung von Orientierungsänderungen

Das Problem ist die Leistung nach der Rotation. Das WebView muss die Seite neu laden, was ein wenig langweilig sein kann.

Wie gehe ich am besten mit Orientierungsänderungen um, ohne die Seite jedes Mal neu aus dem Quellcode zu laden?

134
glennanthonyb

Wenn Sie nicht möchten, dass das WebView bei Orientierungsänderungen erneut geladen wird, überschreiben Sie einfach onConfigurationChanged in Ihrer Activity-Klasse: 

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

Und legen Sie das Attribut Android: configChanges im Manifest fest:

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

für weitere Informationen siehe:
http://developer.Android.com/guide/topics/resources/runtime-changes.html#HandlingTheChange

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

83
Totach

Bearbeiten: Diese Methode funktioniert nicht mehr wie in docs angegeben.


Ursprüngliche Antwort:

Dies kann durch Überschreiben von onSaveInstanceState(Bundle outState) in Ihrer Aktivität und durch Aufruf von saveState in der Webansicht behandelt werden:

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

Stellen Sie diese in Ihrem onCreate wieder her, nachdem der Webview natürlich wieder aufgeblasen wurde:

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

Die beste Antwort darauf ist die folgende Android-Dokumentation, die here .__ gefunden wurde. Grundsätzlich verhindert dies, dass Webview erneut geladen wird:

<activity Android:name=".MyActivity"
      Android:configChanges="orientation|screenSize"
      Android:label="@string/app_name">

In der Tätigkeit:

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

Ich habe es mit onRetainNonConfigurationInstance versucht (das WebView zurückgegeben), es dann mit getLastNonConfigurationInstance während onCreate zurückbekommen und es erneut zuweisen.

Scheint noch nicht zu klappen. Ich kann nicht anders als zu denken, dass ich wirklich nahe bin! Bis jetzt bekomme ich nur einen leeren/weißen Hintergrund WebView. Posten Sie hier in der Hoffnung, dass jemand helfen kann. Schieben Sie dieses Ziel über die Ziellinie.

Vielleicht sollte ich nicht das WebView übergeben. Vielleicht ein Objekt aus dem WebView?

Die andere Methode, die ich versucht habe - nicht meine Lieblingsmethode - besteht darin, dies in der Aktivität festzulegen:

 Android:configChanges="keyboardHidden|orientation"

... und dann so ziemlich nichts hier tun:

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

DAS funktioniert, kann aber nicht als Best Practice betrachtet werden.

Inzwischen habe ich auch ein einzelnes ImageView, das ich je nach Rotation automatisch aktualisieren möchte. Dies stellt sich als sehr einfach heraus. In meinem res-Ordner habe ich drawable-land und drawable-port, um Querformat-/Hochformatvariationen zu halten, dann verwende ich R.drawable.myimagename für die ImageView-Quelle und Android "tut das Richtige" - yay!

... außer wenn Sie auf Konfigurationsänderungen achten, ist dies nicht der Fall. :(

Also bin ich uneins. Verwenden Sie onRetainNonConfigurationInstance und die ImageView -Drehung funktioniert, aber WebView-Persistenz ist nicht ... oder onConfigurationChanged und WebView bleibt stabil, aber das ImageView wird nicht aktualisiert. Was ist zu tun?

Noch eine letzte Anmerkung: In meinem Fall ist das Erzwingen der Orientierung kein akzeptabler Kompromiss. Wir möchten die Rotation wirklich unterstützen. Irgendwie mag es die Android Browser App! ;)

5
Joe D'Andrea

Ein Kompromiss ist das Vermeiden von Rotation. Fügen Sie dies hinzu, um die Aktivität nur für die Ausrichtung Hochformat festzulegen.

Android:screenOrientation="portrait"

Ich weiß, dass dies etwas zu spät ist, aber dies ist die Antwort, die ich bei der Entwicklung meiner Lösung verwendet habe:

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

}
3
David Passmore

Sie können versuchen, onSaveInstanceState() und onRestoreInstanceState() in Ihrer Activity zu verwenden, um saveState(...) und restoreState(...) in Ihrer WebView-Instanz aufzurufen. 

2
deltaearl

Es ist 2015, und viele Leute suchen nach einer Lösung, die immer noch an Jellybean-, KK- und Lollipop-Telefonen arbeitet .. Nach viel Kämpfen fand ich einen Weg, den Webview intakt zu halten, nachdem Sie die Orientierung geändert haben . Meine Strategie besteht im Wesentlichen darin, den Webview in einer separaten statischen Variablen in einer anderen Klasse zu speichern. Wenn dann eine Rotation auftritt, entferne ich die Webansicht von der Aktivität, warte, bis die Ausrichtung beendet ist, und stelle die Webansicht wieder zur Aktivität . Zum Beispiel ... setzen Sie dies zuerst auf Ihr MANIFEST (keyboardHidden und keyboard sind optional) ):

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

Geben Sie in einer separaten Anwendungsklasse Folgendes ein:

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

In der AKTIVITÄT setzen Sie:

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

Zum Schluss noch das einfache 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>

Es gibt mehrere Dinge, die in meiner Lösung verbessert werden könnten, aber ich habe bereits zu viel Zeit aufgewendet, z. B. eine kürzere Methode, um zu überprüfen, ob die Aktivität zum ersten Mal gestartet wurde, anstatt SharedPreferences-Speicher zu verwenden. Dieser Ansatz behält Ihre Webansicht (afaik), ihre Textfelder, Beschriftungen, Benutzeroberflächen, Javascript-Variablen und Navigationszustände bei, die von der URL nicht berücksichtigt werden.

2
Josh

Update: Die aktuelle Strategie besteht darin, die WebView-Instanz in die Application-Klasse zu verschieben, anstatt ein beibehaltenes Fragment zu erhalten, wenn sie getrennt wird, und bei der Wiederaufnahme wie bei Josh erneut anzuhängen .. Um das Schließen von Application zu verhindern, sollten Sie den Vordergrunddienst verwenden, wenn Sie den Status beim Benutzer beibehalten möchten wechselt zwischen Anwendungen.

Wenn Sie Fragmente verwenden, können Sie die Retain-Instanz von WebView verwenden. .__ Die Webansicht wird als Instanzmitglied der Klasse beibehalten. Sie sollten jedoch die Webansicht in OnCreateView anhängen und vor OnDestroyView trennen, um eine Zerstörung durch den übergeordneten Container zu verhindern. 

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

P.S. Credits gehen an kcoppock answer </ S>

Wie für "SaveState ()" funktioniert es nicht mehr gemäß offizieller Dokumentation :

Bitte beachten Sie, dass diese Methode die Anzeigedaten für .__ nicht mehr speichert. diese WebView. Das vorherige Verhalten kann zu Dateien führen, wenn restoreState (Bundle) wurde nie aufgerufen.

2
Boris Treukhov

Dies ist das einzige, was für mich funktioniert hat (ich habe sogar den Zustand der Sicherungsinstanz in onCreateView verwendet, war aber nicht so zuverlässig).

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

Ich habe einen Singleton-Halter für den Status des WebView erstellt. Der Zustand bleibt erhalten, solange der Prozess der Anwendung vorhanden ist.

EDIT: Die loadDataWithBaseURL war nicht notwendig, es hat genauso gut funktioniert

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

Obwohl ich das gelesen habe, funktioniert es nicht unbedingt gut mit Cookies.

1
EpicPandaForce

Schreiben Sie einfach die folgenden Codezeilen in Ihre Manifest-Datei - sonst nichts. Es funktioniert wirklich:

<activity Android:name=".YourActivity"
  Android:configChanges="orientation|screenSize"
  Android:label="@string/application_name">
1
Eddie Valmont
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);    
}

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

Diese Methoden können bei jeder Aktivität überschrieben werden. Sie können also grundsätzlich jedes Mal, wenn eine Aktivität erstellt/gelöscht wird, Werte speichern und wiederherstellen. Wenn sich die Bildschirmausrichtung ändert, wird die Aktivität zerstört und im Hintergrund neu erstellt zum vorübergehenden Speichern/Wiederherstellen von Zuständen während der Änderung.

Sie sollten sich die beiden folgenden Methoden genauer ansehen und prüfen, ob sie zu Ihrer Lösung passt.

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

1
Chiwai Chan

Sie sollten nur diesen Code zu Ihrer Manifestdatei hinzufügen:

<activity Android:name=".YourActivity"
      Android:configChanges="orientation|screenSize"
      Android:label="@string/application_name">
1
Les Paul

Am besten behandeln Sie Orientierungsänderungen und verhindern, dass WebView bei Rotate erneut geladen wird.

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

Damit onCreate () nicht jedes Mal aufgerufen wird, wenn Sie die Ausrichtung ändern, müssen Sie Android:configChanges="orientation|screenSize" to the AndroidManifest. hinzufügen.

oder nur ..

Android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"`
1
Amit raj

Sie sollten dies versuchen:

  1. Erstellen Sie einen Dienst innerhalb dieses Dienstes, erstellen Sie Ihr WebView.
  2. Starten Sie den Dienst aus Ihrer Aktivität und binden Sie ihn an.
  3. Rufen Sie in der Methode onServiceConnected das WebView ab und rufen Sie die Methode setContentView auf, um das WebView zu rendern.

Ich habe es getestet und es funktioniert aber nicht mit anderen WebViews wie XWalkView oder GeckoView.

0

versuche dies

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
Anwar SE
@Override
    protected void onSaveInstanceState(Bundle outState )
    {
        super.onSaveInstanceState(outState);
        webView.saveState(outState);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState)
    {
        super.onRestoreInstanceState(savedInstanceState);
        webView.restoreState(savedInstanceState);
    }
0
RealDEV

Diese Seite löst mein Problem, aber in der ersten muss ich etwas ändern:

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

Dieser Teil hat für mich ein kleines Problem. Bei der zweiten Orientierungsänderung wurde die Anwendung mit einem Nullzeiger beendet

mit diesem funktionierte es für mich:

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