Android WebView: обработка изменений ориентации - PullRequest
138 голосов
/ 16 июня 2009

Проблема в производительности после ротации. WebView должен перезагрузить страницу, что может быть немного утомительно.

Какой лучший способ обработки изменения ориентации без перезагрузки страницы из источника каждый раз?

Ответы [ 20 ]

86 голосов
/ 11 февраля 2010

Если вы не хотите, чтобы WebView перезагружался при изменении ориентации, просто переопределите onConfigurationChanged в вашем классе Activity:

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

И установите атрибут android: configChanges в манифесте:

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

для получения дополнительной информации см .:
http://developer.android.com/guide/topics/resources/runtime-changes.html#HandlingTheChange

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

69 голосов
/ 23 апреля 2010

Редактировать: этот метод больше не работает, как указано в документах


Оригинальный ответ:

Это можно сделать, переписав onSaveInstanceState(Bundle outState) в вашей активности и позвонив по телефону saveState из веб-обзора:

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

Затем восстановите это в своем onCreate после того, как веб-представление было раздуто, конечно:

public void onCreate(final Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.blah);
   if (savedInstanceState != null)
      ((WebView)findViewById(R.id.webview)).restoreState(savedInstanceState);
}
17 голосов
/ 04 июля 2014

Лучший ответ на этот вопрос приведен в документации Android, найденной здесь В основном это предотвратит перезагрузку Webview:

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

В деятельности:

@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();
    }
}
5 голосов
/ 14 января 2010

Я пытался использовать onRetainNonConfigurationInstance (возвращая WebView ), затем возвращая его с getLastNonConfigurationInstance во время onCreate и повторно назначая его.

Кажется, пока не работает. Я не могу помочь, но думаю, что я действительно близко! Пока что я просто получаю пустой / белый фон WebView . Размещать здесь в надежде, что кто-то может помочь протолкнуть это за финишную черту.

Может быть, мне не следует передавать WebView . Возможно, объект из WebView ?

Другой метод, который я пробовал - не мой любимый, - установить это в упражнении:

 android:configChanges="keyboardHidden|orientation"

... а тут ничего не делать:

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

Это работает, но это не может считаться лучшей практикой .

Между тем, у меня также есть один ImageView , который я хочу автоматически обновлять в зависимости от поворота. Это оказывается очень легко. Под моей папкой res у меня есть drawable-land и drawable-port для хранения вариаций ландшафта / портрета, затем я использую R.drawable.myimagename для источника ImageView , а Android "делает все правильно" - яй!

... кроме случаев, когда вы наблюдаете за изменениями конфигурации, тогда это не так. (

Так что я в ссоре. Используйте onRetainNonConfigurationInstance и вращение ImageView работает, но WebView постоянство не ... или используйте onConfigurationChanged и WebView остается стабильным, но ImageView не обновляется. Что делать?

Последнее замечание: в моем случае форсирование ориентации не является приемлемым компромиссом. Мы действительно хотим изящно поддержать ротацию. Вроде как, как работает приложение Android Browser! ;)

3 голосов
/ 11 ноября 2009

Один из компромиссов - избегать вращения. Добавьте это, чтобы исправить действие только для книжной ориентации.

android:screenOrientation="portrait"
3 голосов
/ 05 мая 2017

Лучший способ обрабатывать изменения ориентации и предотвращать перезагрузку WebView при повороте.

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

Имея это в виду, чтобы не вызывать onCreate () каждый раз, когда вы меняете ориентацию, вам нужно добавить android:configChanges="orientation|screenSize" to the AndroidManifest.

или просто ..

android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"`
3 голосов
/ 15 ноября 2014

Я ценю, что это немного поздно, но вот ответ, который я использовал при разработке своего решения:

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 голосов
/ 26 сентября 2015

Обновление: текущая стратегия состоит в том, чтобы переместить экземпляр WebView в класс приложения вместо сохраненного фрагмента, когда он отсоединен, и повторно присоединить его к резюме, как это делает Джош.Чтобы предотвратить закрытие приложения, вы должны использовать службу переднего плана, если вы хотите сохранить состояние, когда пользователь переключается между приложениями.

Если вы используете фрагменты, вы можете использовать сохранить экземпляр WebView.Веб-представление будет сохранено как член экземпляра класса.Однако вы должны прикрепить веб-представление в OnCreateView и отсоединить перед OnDestroyView, чтобы предотвратить его уничтожение родительским контейнером.

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

PS Кредиты переходят на kcoppock answer

Что касается 'SaveState ()', он больше не работает согласно официальной документации :

Обратите внимание, что этот метод больше не сохраняет данные отображения для этого WebView.Предыдущее поведение могло потенциально привести к утечке файлов, если restoreState (Bundle) никогда не вызывался.

2 голосов
/ 29 ноября 2009

Вы можете попробовать использовать onSaveInstanceState() и onRestoreInstanceState() в своей деятельности для вызова saveState(...) и restoreState(...) в вашем экземпляре WebView.

2 голосов
/ 15 сентября 2015

Это 2015 год, и многие люди ищут решение, которое все еще работает на телефонах Jellybean, KK и Lollipop. После значительных попыток я нашел способ сохранить целостность веб-просмотра после изменения ориентации. Моя стратегия заключается в том, чтобы хранить веб-представление в отдельной статической переменной в другом классе. Затем, если происходит ротация, я отсоединяю веб-представление от действия, ожидаю завершения ориентации и снова присоединяю веб-представление к действию. Например ... сначала поместите это в свой манифест (клавиатура скрытая и клавиатура необязательны):

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

В ОТДЕЛЬНОМ КЛАССЕ ПРИМЕНЕНИЯ положите:

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

В ДЕЯТЕЛЬНОСТЬ вкладывается:

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

Наконец, простой 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>

Есть несколько вещей, которые можно улучшить в моем решении, но я уже потратил много времени, например: более короткий способ проверки, был ли Activity запущен в первый раз вместо использования хранилища SharedPreferences. Этот подход сохраняет веб-просмотр без изменений (afaik), его текстовые поля, метки, пользовательский интерфейс, переменные javascript и состояния навигации, которые не отражаются в URL.

...