Android WebView:处理方向更改

问题在于轮换后的performance。 WebView必须重新加载页面,这可能有点乏味。

每次处理方向更改而不从源重新加载页面的最佳方式是什么?

如果您不希望WebView在方向更改上重新加载,则只需覆盖Activity类中的onConfigurationChanged即可:

@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

编辑:此方法不再像文档中所述的那样工作


原始答案:

这可以通过在您的活动中覆盖onSaveInstanceState(Bundle outState)并从webview调用saveState来处理:

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

然后在webview重新膨胀之后,在你的onCreate中恢复它:

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

最好的答案是在这里find的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(); } } 

我已经尝试使用onRetainNonConfigurationInstance (返回WebView ),然后在onCreategetLastNonConfigurationInstance重新分配它。

似乎还没有工作。 我不禁觉得我真的很接近! 到目前为止,我只是得到一个空白/背景的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-landdrawable-port来保存横向/纵向的变化,然后我使用R.drawable.myimagename作为ImageView的源代码,Android“做正确的事情” – 好的!

…除了当你看到configuration更改,然后它不。 🙁

所以我不同意 使用onRetainNonConfigurationInstanceImageView旋转工作,但WebView持久性不…或使用onConfigurationChangedWebView保持稳定,但ImageView不更新。 该怎么办?

最后一个注意事项:就我而言,强制定向不是一个可以接受的妥协。 我们确实想要优雅地支持轮换。 有点像Android浏览器的应用程序! ;)

一个妥协是避免轮换。 添加此仅修复肖像方向的活动。

 android:screenOrientation="portrait" 

您可以尝试在Activity上使用onSaveInstanceState()onRestoreInstanceState()来调用WebView实例上的saveState(...)restoreState(...)

我感谢这有点晚,但是这是我在开发我的解决scheme时使用的答案:

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

改编自这里

希望这可以帮助 :)

现在是2015年,许多人正在寻找一种仍然适用于Jellybean,KK和棒棒糖手机的解决scheme。 经过很多努力之后我发现了一种方法,可以在改变方向后保持webview的完整性。 我的策略基本上是将webview存储在另一个类中的单独静态variables中。 然后,如果发生旋转,我从活动中分离webview,等待方向完成,然后将webview重新附加到活动。 例如…首先把它放在你的MANIFEST(键盘隐藏和键盘是可选的):

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

在ACTIVITY中说:

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

在我的解决scheme中有几件事情可以改进,但我已经花费了很多时间,例如:validation活动是否第一次启动而不是使用SharedPreferences存储的简短方法。 这种方法保留了webview完整(afaik),其文本框,标签,UI,JavaScriptvariables和导航状态,这些并不反映在url中。

更新:当前的策略是将WebView实例移动到Application类,而不是保留片段,当它被分离时,像Josh那样重新执行。 要阻止应用程序closures,如果要在应用程序之间切换时保留状态,则应使用前台服务。

如果您正在使用片段,则可以使用WebView的retain实例。 Web视图将保留为类的实例成员。 但是,您应该在OnCreateView中附加Web视图并在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的答案

至于“SaveState()”,根据官方文档不再有效:

请注意,此方法不再存储此WebView的显示数据。 如果restoreState(Bundle)从未被调用过,以前的行为可能会泄漏文件。

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

这些方法可以在任何活动上被覆盖,它基本上允许你在每次创build/销毁活动时保存和恢复值,当屏幕方向改变时,活动被破坏并在后台重新创build,所以你可以使用这些方法临时存储/恢复状态。

您应该深入了解以下两种方法,看看它是否适合您的解决scheme。

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

我发现这样做的最好的解决scheme,而不泄漏以前的Activity引用,并没有设置configChanges ..是使用MutableContextWrapper 。

我在这里实现了这个: https : //github.com/slightfoot/android-web-wrapper/blob/48cb3c48c457d889fc16b4e3eba1c9e925f42cfb/WebWrapper/src/com/example/webwrapper/BrowserActivity.java

你应该做的唯一事情就是把这个代码添加到你的清单文件中:

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

这是我唯一的工作(我甚至使用保存实例状态在onCreateView但它不是可靠的)。

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

我为WebView的状态创build了一个Singleton持有者。 只要应用程序存在,状态就会被保留。

编辑: loadDataWithBaseURL是没有必要的,它刚刚工作

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

虽然我读了这不一定适用于cookies。

处理方向更改并防止在Web上重新加载Rotate的最佳方法。

 @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"` 

这个页面解决了我的问题,但我必须在最初的一个稍作改动:

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

这部分对我来说有一个小问题。 在第二个方向改变应用程序终止与空指针

使用它为我工作:

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

我喜欢这个解决schemehttp://www.devahead.com/blog/2012/01/preserving-the-state-of-an-android-webview-on-screen-orientation-change/

根据它,我们重用相同的WebView实例。 它允许保存导航历史和configuration更改的滚动位置。

你应该试试这个:

  1. 在该服务中创build一个服务,创build您的WebView。
  2. 从你的活动开始服务并绑定到它。
  3. onServiceConnected方法中,获取WebView并调用setContentView方法来呈现您的WebView。

我testing了它,它的工作原理,但不是像XWalkView或GeckoView的其他WebViews。