Android Webview: Handling Oriëntatie Wijzigingen

Het probleem is de uitvoering na rotatie. De Webview moet de pagina opnieuw laden, die een beetje vervelend kan zijn.

Wat is de beste manier om een ​​geaardheidsverandering te verwerken zonder de pagina van de bron telkens opnieuw te laden?


1, Autoriteit 100%

Als u niet wilt dat de WebView opnieuw wordt geladen op oriëntatiewijzigingen, negeert u eenvoudig op ConfigurationChanged in uw activiteitenklasse:

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

en stel de Android in: Configureer attribuut in het manifest:

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

Voor meer info Zie:
http://developer.android.com/guide/topics/resources /runtime-changes.html#HandlingThechange

https://developer.android.com/reference/android/ app / activiteit.html # configurationChanges


2, Autoriteit 77%

EDIT: deze methode werkt niet langer zoals vermeld in de Docs


origineel antwoord:

Dit kan worden afgehandeld door het overschrijven van onSaveInstanceState(Bundle outState)in uw activiteit en bellen saveStatevan de Webview:

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

Herstel dit dan in uw oncreate nadat de Webview natuurlijk is opgeplant:

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

3, Autoriteit 26%

Het beste antwoord hierop volgt Android-documentatie gevonden HIER
In principe verhindert dit Webview van herladen:

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

EDIT (1/4/2020): U hebt deze optionele code niet nodig, het kenmerk Manifest is alles wat u nodig hebt, waardoor hier een optionele code achterblijft om het antwoord compleet te houden.

Eventueel , u kunt anomalieën (indien aanwezig) repareren door door te halen onConfigurationChangedin de activiteit:

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

4, Autoriteit 5%

Ik heb geprobeerd OnretainNonConfigurationInstance (teruggeven aan de Webview ), en dan terug krijgen met GetlastnonconfigurationInstance Tijdens Oncreate en het opnieuw toewijzen.

Het lijkt nog niet te werken. Ik kan het niet helpen, maar denk dat ik er heel dichtbij ben! Tot nu toe krijg ik gewoon een WebViewmet een lege/witte achtergrond. Ik post hier in de hoop dat iemand kan helpen deze over de finish te krijgen.

Misschien moet ik de WebViewniet passeren. Misschien een object uit de WebView?

De andere methode die ik heb geprobeerd – niet mijn favoriet – is om dit in de activiteit in te stellen:

android:configChanges="keyboardHidden|orientation"

… en dan hier vrijwel niets doen:

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

DAT werkt, maar het kan niet worden beschouwd als een best practice.

Ondertussen heb ik ook een enkele ImageViewdie ik automatisch wil bijwerken, afhankelijk van de rotatie. Dit blijkt heel eenvoudig te zijn. In mijn map resheb ik drawable-landen drawable-portom landschaps-/portretvariaties te bewaren, dan gebruik ik R.drawable.myimagenamevoor de bron van ImageViewen Android “doet het juiste” – yay!

… behalve als je let op configuratiewijzigingen, dan gebeurt dat niet. 🙁

Dus ik sta op gespannen voet. Gebruik onRetainNonConfigurationInstanceen de ImageView-rotatie werkt, maar WebView-persistentie werkt niet … of gebruik onConfigurationChangeden de WebViewblijft stabiel, maar de ImageViewwordt niet bijgewerkt. Wat te doen?

Nog een laatste opmerking: in mijn geval is het forceren van oriëntatie geen acceptabel compromis. We willen rotatie echt sierlijk ondersteunen. Een beetje zoals hoe de Android Browser-app doet! 😉


Antwoord 5, autoriteit 4%

De beste manier om met oriëntatiewijzigingen om te gaan en te voorkomen dat WebView opnieuw wordt geladen bij Rotate.

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

Om te voorkomen dat onCreate() wordt aangeroepen telkens wanneer u de oriëntatie wijzigt, moet u daarom android:configChanges="orientation|screenSize" to the AndroidManifest.

of gewoon ..

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

Antwoord 6, autoriteit 4%

Ik stel het op prijs dat dit een beetje laat is, maar dit is het antwoord dat ik heb gebruikt bij het ontwikkelen van mijn oplossing:

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

7, Autoriteit 3%

Eén compromis is om rotatie te voorkomen.
Voeg dit toe om alleen de activiteit voor portretoriëntatie op te lossen.

android:screenOrientation="portrait"

8, Autoriteit 3%

Schrijf gewoon de volgende codelijnen in uw manifest-bestand – niets anders. Het werkt echt:

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

9, Autoriteit 2%

Het is 2015, en veel mensen zijn op zoek naar een oplossing die nog steeds werkt op Jellybean, KK en Lollipop-telefoons.
Na Veel worstelen Ik vond ik een manier om de WebView intact te behouden nadat je de oriëntatie hebt gewijzigd.
Mijn strategie is eigenlijk om de webview op te slaan in een aparte statische variabele in een andere klasse. Als er vervolgens rotatie plaatsvindt, koppel ik de webview los van de activiteit, wacht ik tot de oriëntatie is voltooid en koppel ik de webview weer aan de activiteit.
Bijvoorbeeld… plaats dit eerst op uw MANIFEST (keyboardHidden en keyboard zijn optioneel):

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

Plaats in een APARTE TOEPASSINGSKLASSE:

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

Plaats in de ACTIVITEIT:

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

Tot slot, de eenvoudige 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>

Er zijn verschillende dingen die verbeterd kunnen worden in mijn oplossing, maar ik heb al te veel tijd besteed, bijvoorbeeld: een kortere manier om te valideren of de activiteit voor de allereerste keer is gestart in plaats van het gebruik van SharedPreferences-opslag.
Deze aanpak houdt uw webweergave intact (afaik), de tekstvakken, labels, gebruikersinterface, javascript-variabelen en navigatiestatussen die niet worden weergegeven door de url.


Antwoord 10, autoriteit 2%

Het enige dat u hoeft te doen is deze code aan uw manifestbestand toe te voegen:

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

Antwoord 11, autoriteit 2%

Update: de huidige strategie is om de WebView-instantie naar de Application-klasse te verplaatsen in plaats van het behouden fragment wanneer het wordt losgekoppeld en opnieuw te koppelen bij het hervatten, zoals Josh doet.
Om te voorkomen dat de toepassing wordt gesloten, moet u de voorgrondservice gebruiken als u de status wilt behouden wanneer de gebruiker tussen toepassingen schakelt.


Als u fragmenten gebruikt, kunt u de bewaarinstantie van de WebView gebruiken.
De webweergave blijft behouden als instantielid van de klas. U moet echter de webweergave in OnCreateView bijvoegen en loskoppelen vóór OnDestroyView om te voorkomen dat deze wordt vernietigd met de bovenliggende container.

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 gaan naar kcoppock-antwoord

Voor ‘SaveState()’ werkt het niet meer volgens officiële documentatie:

Houd er rekening mee dat deze methode niet langer de weergavegegevens opslaat voor:
deze WebView. Het vorige gedrag kan mogelijk bestanden lekken als:
restoreState(Bundle) is nooit aangeroepen.


Antwoord 12

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

Deze methoden kunnen voor elke activiteit worden overschreven, het stelt je in feite in staat om waarden op te slaan en te herstellen telkens wanneer een activiteit wordt gemaakt/vernietigd, wanneer de schermoriëntatie verandert, wordt de activiteit vernietigd en opnieuw gemaakt op de achtergrond, dus daarom zou je dat kunnen doen. gebruik deze methoden om statussen tijdelijk op te slaan/te herstellen tijdens de wijziging.

U moet de twee volgende methoden nader bekijken en kijken of deze bij uw oplossing passen.

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


Antwoord 13

De beste oplossing die ik heb gevonden om dit te doen zonder de vorige Activity-referentie te lekken en zonder de configChangesin te stellen.. is het gebruik van een MutableContextWrapper.

Ik heb dit hier geïmplementeerd: https://github.com/slightfoot/android-web-wrapper/blob/48cb3c48c457d889fc16b4e3eba1c9e925f42cfb/WebWrapper/src/com/example/webwrapper/BrowserActivity.java


Antwoord 14

Dit is het enige dat voor mij werkte (ik gebruikte zelfs de status van de opgeslagen instantie in onCreateViewmaar het was niet zo betrouwbaar).

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

Ik heb een Singleton-houder gemaakt voor de status van de WebView. De status blijft behouden zolang het proces van de aanvraag bestaat.

EDIT: De loadDataWithBaseURLwas niet nodig, het werkte net zo goed met slechts

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

Hoewel ik lees dat dit niet per se goed werkt met cookies.


Antwoord 15

probeer dit

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

Antwoord 16

Deze pagina lost mijn probleem op, maar ik moet een kleine wijziging aanbrengen in de eerste:

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

Dit gedeelte heeft voor mij een klein probleem. Bij de tweede wijziging van de oriëntatie eindigde de toepassing met een null-aanwijzer

gebruik hiervan werkte het voor mij:

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

Antwoord 17

Je zou dit moeten proberen:

  1. Maak een service, binnen die service, maak je WebView.
  2. Start de service vanuit uw activiteit en bind eraan.
  3. In de onServiceConnectedmethode haalt u de WebView op en roept u de setContentViewmethode aan om uw WebView weer te geven.

Ik heb het getest en het werkt, maar niet met andere WebViews zoals XWalkView of GeckoView.


Antwoord 18

@Override
    protected void onSaveInstanceState(Bundle outState )
    {
        super.onSaveInstanceState(outState);
        webView.saveState(outState);
    }
    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState)
    {
        super.onRestoreInstanceState(savedInstanceState);
        webView.restoreState(savedInstanceState);
    }

Antwoord 19

Ik vind deze oplossing leuk http://www.devahead.com/blog/2012/01/preserving-the-state-of-an-android-webview-on-screen-orientation-change/

Volgens ons hergebruiken we dezelfde instantie van WebView. Het maakt het mogelijk om navigatiegeschiedenis en scrollpositie op te slaan bij configuratiewijzigingen.

Other episodes