Preserving the state of an Android WebView on screen orientation change

Source code

If you’ve tried to use a WebView inside your app, you know that the standard behavior on screen orientation change is not satisfactory in most cases because the full state of the WebView is not preserved. Here I’m going to show you a possible implementation to keep the full state of theWebView every time you rotate the screen. This is the same implementation I used in my own app (FWebLauncher) for the internal web browser.

Standard implementation (the state is not completely preserved)

Let’s start by taking a look at what a standard implementation would look like. This is how you would usually implement the state saving for a WebView according to the Android documentation:

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class StandardImplActivity extends Activity
{
   protected WebView webView;
 
   @Override
   public void onCreate(Bundle savedInstanceState)
   {
     super .onCreate(savedInstanceState);
     setContentView(R.layout.standard_impl);
 
     // Retrieve UI elements
     webView = ((WebView)findViewById(R.id.webView));
     
     // Initialize the WebView
     webView.getSettings().setSupportZoom( true );
     webView.getSettings().setBuiltInZoomControls( true );
     webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
     webView.setScrollbarFadingEnabled( true );
     webView.getSettings().setLoadsImagesAutomatically( true );
 
     // Load the URLs inside the WebView, not in the external web browser
     webView.setWebViewClient( new WebViewClient());
 
     if (savedInstanceState == null )
     {
       // Load a page
       webView.loadUrl( "http://www.google.com" );
     }
   }
   
   @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);
   }
}

So we call the saveState and the restoreState methods in the onSaveInstanceState and theonRestoreInstanceState methods of the Activity.

The WebView is declared directly in the layout file for the activity:

?
01
02
03
04
05
06
07
08
09
10
<? xml version = "1.0" encoding = "utf-8" ?>
< LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
   android:layout_width = "fill_parent"
   android:layout_height = "fill_parent" >
   
   < WebView android:id = "@+id/webView"
     android:layout_width = "fill_parent"
     android:layout_height = "fill_parent" />
   
</ LinearLayout >

The main problem with this implementation is that, whenever you rotate the screen, the WebViewis created again because the activity is destroyed and its saveState method doesn’t save the full state, but only a part of it like the URL of the page that was loaded and the browsing history. So it happens that for example the zoom and the scroll position are not preserved after the screen orientation change and sometimes the page is reloaded from the web.

State preserving implementation (a possible solution)

I tried many different solutions to preserve the full state of the WebView on screen rotation and the following one proved to be a reliable one that solves our problem. The main point is that the activity must not be destroyed and we must handle the screen orientation change by ourselves. Let’s start by taking a look at the layout file:

?
01
02
03
04
05
06
07
08
09
10
<? xml version = "1.0" encoding = "utf-8" ?>
< LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
   android:layout_width = "fill_parent"
   android:layout_height = "fill_parent" >
   
   < FrameLayout android:id = "@+id/webViewPlaceholder"
     android:layout_width = "fill_parent"
     android:layout_height = "fill_parent" />
   
</ LinearLayout >

You immediately notice the difference with the standard implementation. Here we don’t declare theWebView inside the layout file, but we declare a placeholder instead. It is the position where ourWebView will be placed inside the activity.

Also the code inside the activity will be different of course and here it is:

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
public class StatePreservingImplActivity extends Activity
{
   protected FrameLayout webViewPlaceholder;
   protected WebView webView;
 
   @Override
   public void onCreate(Bundle savedInstanceState)
   {
     super .onCreate(savedInstanceState);
     setContentView(R.layout.state_preserving_impl);
     
     // Initialize the UI
     initUI();
   }
   
   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(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
       webView.getSettings().setSupportZoom( true );
       webView.getSettings().setBuiltInZoomControls( true );
       webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
       webView.setScrollbarFadingEnabled( true );
       webView.getSettings().setLoadsImagesAutomatically( true );
 
       // Load the URLs inside the WebView, not in the external web browser
       webView.setWebViewClient( new WebViewClient());
 
       // Load a page
       webView.loadUrl( "http://www.google.com" );
     }
 
     // Attach the WebView to its placeholder
     webViewPlaceholder.addView(webView);
   }
 
   @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.state_preserving_impl);
 
     // 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);
   }
}

We need to change also the AndroidManifest.xml file to intercept the configuration changes inside the activity:

?
1
2
3
4
< activity
   android:name = ".StatePreservingImplActivity"
   android:configChanges = "keyboard|keyboardHidden|orientation"
   android:label = "State preserving implementation" />

In the android:configChanges attribute we are telling the system that we don’t want the activity to be restarted when the declared configuration changes happen, but we want to handle the configuration changes by ourselves through the onConfigurationChanged method that will be called every time there is a change.

When the activity is created we initialize its content with the current layout and we initialize the user interface with the initUI method. That’s where the WebView is created, stored in the webView class field and then attached to the webViewPlaceholder element filling its space completely. Every time the screen is rotated, the onConfigurationChanged method is called so we remove the WebViewfrom its current placeholder first, then we load the new activity layout (we can define a different layout for the portrait and the landscape configuration) and reinitialize the UI. After a configuration change, the activity instance is still the same because it has not been destroyed. The initUI method retrieves the new placeholder instance while the WebView instance is still the same because thewebView class field is not changed, so the WebView is not created again and all we have to do is attach the old WebView to the new webViewPlaceholder instance.

This implementation is very useful in case you defined a different layout depending on the screen orientation. For example, you could have a my_activity_layout.xml file in the res/layout folder of your Android project for the portrait orientation and another my_activity_layout.xml file in the res/layout-land folder with a different content for the landscape orientation. When onConfigurationChanged is called, the system already has the appropriate resources for the new configuration and when you call the setContentView method passing it a layout resource id, the system uses the correct layout file for the current screen orientation, you just have to make sure that the ids of the UI elements are the same in both files and that the webViewPlaceholder element is present in both the layout configurations. This is what I have also in the internal web browser of my FWebLauncher app and that allows me to have a different position and layout of the buttons bar depending on the screen orientation while keeping the same WebView instance.

I’m sure you noticed that the onSaveInstanceState and onRestoreInstanceState methods are still there, but why since the activity is not destroyed anymore? Well, they are there in case the activity is destroyed for a reason that is not a configuration change (e.g. the system needs to destroy the activity because it is not in foreground and there’s a need to free some memory). In that case we can still restore the original state of the WebView, just without preserving the complete state because we’re going to lose the zoom and the scroll position for example, anyway it’s still better than totally losing the WebView state.

If you want to read more about the configuration change handling in Android, you can check thespecific page of the Android documentation. You can also download an example application through the link on top of this post to take a look at the source code that shows what I described in the post.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值