问题在于轮换后的表现。 WebView必须重新加载页面,这可能有点乏味。
每次处理方向更改而不从源重新加载页面的最佳方法是什么?
答案 0 :(得分:85)
如果您不希望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
答案 1 :(得分:69)
修改:此方法不再按照docs
中的说明运作原始答案:
这可以通过覆盖您的活动中的onSaveInstanceState(Bundle outState)
并从网络视图调用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);
}
答案 2 :(得分:17)
最佳答案是遵循找到的here 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();
}
}
答案 3 :(得分:5)
我尝试使用 onRetainNonConfigurationInstance (返回 WebView ),然后在 onCreate getLastNonConfigurationInstance 将其恢复>并重新分配。
似乎还没有奏效。我不禁想到我真的很亲密!到目前为止,我只是得到一个空白/白色背景 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.
}
这有用,但可能不会被视为best practice。
与此同时,我还有一个 ImageView 我希望根据轮换自动更新。事实证明这很容易。在我的res
文件夹下,我有drawable-land
和drawable-port
来保存横向/纵向变体,然后我使用R.drawable.myimagename
作为 ImageView 的来源和Android“做对了” - 耶!
...除非你观察配置更改,否则它不会。 :(
所以我很不相干。使用 onRetainNonConfigurationInstance 并且 ImageView 轮换有效,但 WebView 持久性不会...或使用 onConfigurationChanged 和 WebView 保持稳定,但 ImageView 不会更新。怎么办?
最后一点:在我的情况下,强迫定位不是一个可接受的妥协。我们确实希望优雅地支持旋转。有点像Android浏览器应用程序的功能! ;)
答案 4 :(得分:3)
一个妥协是避免轮换。 添加此项以仅修复纵向方向的活动。
android:screenOrientation="portrait"
答案 5 :(得分:3)
处理方向更改和防止在旋转时重新加载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"`
答案 6 :(得分:3)
我很欣赏这有点晚了,但这是我在开发解决方案时使用的答案:
的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 :(得分:2)
您可以尝试在Activity上使用onSaveInstanceState()
和onRestoreInstanceState()
来调用WebView实例上的saveState(...)
和restoreState(...)
。
答案 8 :(得分:2)
这是2015年,许多人正在寻找仍然适用于Jellybean,KK和Lollipop手机的解决方案。 在很多挣扎之后,我发现了一种在更改方向后保持webview完好无损的方法。 我的策略基本上是将webview存储在另一个类的单独静态变量中。然后,如果发生旋转,我将webview从活动中分离出来,等待方向完成,然后将webview重新连接回活动。 例如......首先将它放在你的MANIFEST上(keyboardHidden和键盘是可选的):
<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>
在我的解决方案中有几件事可以改进,但我已经花了很多时间,例如:验证Activity是否第一次启动而不是使用SharedPreferences存储的更短方法。 这种方法可以保留webview完整(afaik),其文本框,标签,UI,javascript变量以及未被网址反映的导航状态。
答案 9 :(得分:2)
更新:当前的策略是将WebView实例移动到Application类,而不是在分离时将其保留为片段,并像Josh那样重新连接到简历。 要阻止应用程序关闭,如果要在用户在应用程序之间切换时保留状态,则应使用前台服务。
如果使用片段,则可以使用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();
}
}
P.S。积分转至kcoppock answer
对于'SaveState()',它不再符合official documentation:
请注意,此方法不再存储显示数据 这个WebView。如果,以前的行为可能会泄漏文件 从未调用restoreState(Bundle)。
答案 10 :(得分:1)
我发现这样做的最佳解决方案是在不泄漏之前的Activity
引用且未设置configChanges
...的情况下使用MutableContextWrapper。
答案 11 :(得分:1)
这是唯一对我有用的东西(我甚至在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的状态创建了一个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。
答案 12 :(得分:1)
您唯一应该做的就是将此代码添加到清单文件中:
<activity android:name=".YourActivity"
android:configChanges="orientation|screenSize"
android:label="@string/application_name">
答案 13 :(得分:1)
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
}
这些方法可以在任何活动上被覆盖,它基本上允许您在每次创建/销毁活动时保存和恢复值,当屏幕方向改变时活动被销毁并在后台重新创建,因此您可以使用这些方法在更改期间临时存储/恢复状态。
您应该深入了解以下两种方法,看看它是否适合您的解决方案。
http://developer.android.com/reference/android/app/Activity.html
答案 14 :(得分:1)
只需在您的清单文件中编写以下代码行 - 没有别的。它确实有效:
<activity android:name=".YourActivity"
android:configChanges="orientation|screenSize"
android:label="@string/application_name">
答案 15 :(得分:0)
此页面解决了我的问题,但我必须在初始版本中稍作修改:
protected void onSaveInstanceState(Bundle outState) {
webView.saveState(outState);
}
这部分对我来说有点问题。在第二个方向更改时,应用程序以空指针
终止使用它对我有用:
@Override
protected void onSaveInstanceState(Bundle outState ){
((WebView) findViewById(R.id.webview)).saveState(outState);
}
答案 16 :(得分:0)
你应该试试这个:
onServiceConnected
方法中,获取WebView并调用setContentView
方法来呈现WebView。我测试了它并且它可以工作但不适用于其他WebViews,如XWalkView或GeckoView。
答案 17 :(得分: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);
}
答案 18 :(得分:0)
尝试
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;
}
}
}
答案 19 :(得分:-1)
根据它,我们重用了相同的WebView实例。它允许在配置更改时保存导航历史记录和滚动位置。