In the post Tabs and swipe views Szymon asks how to implement a WebView in order to preserve its state moving from one tab to another in a similar way as explained in How to save the state of a WebView inside a Fragment of an Action Bar.
Starting from the code in Tabs and swipe views
- add a permission to access to internet in AndroidManifest.xml
1<uses-permission android:name="android.permission.INTERNET"/> - replace the file res/layout/tab3.xml
1234567891011121314<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/tab3"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:orientation="vertical" ><WebViewandroid:id="@+id/webView1"android:layout_width="match_parent"android:layout_height="match_parent" /></LinearLayout>
where I add a WebView in the third tab;
as you’ll verify, this WebView still retains its state switching from an adjacent tab (from the second tab in this example) without the need of the code to restore the state of the WebView, instead the WebView is reset switching from a not adjacent tab (from the first tab in this example); - replace the file eu/lucazanini/viewpager/MainActivity.java
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232package eu.lucazanini.swipeviews;/*** Copyright 2012 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/import android.app.ActionBar;import android.app.FragmentTransaction;import android.os.Bundle;import android.support.v4.app.Fragment;import android.support.v4.app.FragmentActivity;import android.support.v4.app.FragmentManager;import android.support.v4.app.FragmentPagerAdapter;import android.support.v4.view.ViewPager;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.webkit.WebView;import android.webkit.WebViewClient;public class MainActivity extends FragmentActivity implementsActionBar.TabListener {/*** The {@link android.support.v4.view.PagerAdapter} that will provide* fragments for each of the three primary sections of the app. We use a* {@link android.support.v4.app.FragmentPagerAdapter} derivative, which* will keep every loaded fragment in memory. If this becomes too memory* intensive, it may be best to switch to a* {@link android.support.v4.app.FragmentStatePagerAdapter}.*/CollectionPagerAdapter mCollectionPagerAdapter;/*** The {@link android.support.v4.view.ViewPager} that will display the* object collection.*/ViewPager mViewPager;public void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);// Create an adapter that when requested, will return a fragment// representing an object in// the collection.//// ViewPager and its adapters use support library fragments, so we must// use// getSupportFragmentManager.mCollectionPagerAdapter = new CollectionPagerAdapter(getSupportFragmentManager());// Set up action bar.final ActionBar actionBar = getActionBar();// Specify that the Home/Up button should not be enabled, since there is// no hierarchical// parent.actionBar.setHomeButtonEnabled(false);// Specify that we will be displaying tabs in the action bar.actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);// Set up the ViewPager, attaching the adapter and setting up a listener// for when the// user swipes between sections.mViewPager = (ViewPager) findViewById(R.id.pager);mViewPager.setAdapter(mCollectionPagerAdapter);mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {@Overridepublic void onPageSelected(int position) {// When swiping between different app sections, select// the corresponding tab.// We can also use ActionBar.Tab#select() to do this if// we have a reference to the// Tab.actionBar.setSelectedNavigationItem(position);}});// For each of the sections in the app, add a tab to the action bar.for (int i = 0; i < mCollectionPagerAdapter.getCount(); i++) {// Create a tab with text corresponding to the page title defined by// the adapter.// Also specify this Activity object, which implements the// TabListener interface, as the// listener for when this tab is selected.actionBar.addTab(actionBar.newTab().setText(mCollectionPagerAdapter.getPageTitle(i)).setTabListener(this));}}public void onTabUnselected(ActionBar.Tab tab,FragmentTransaction fragmentTransaction) {}public void onTabSelected(ActionBar.Tab tab,FragmentTransaction fragmentTransaction) {// When the given tab is selected, switch to the corresponding page in// the ViewPager.mViewPager.setCurrentItem(tab.getPosition());}public void onTabReselected(ActionBar.Tab tab,FragmentTransaction fragmentTransaction) {}/*** A {@link FragmentPagerAdapter} that returns a fragment corresponding to* one of the primary sections of the app.*/public class CollectionPagerAdapter extends FragmentPagerAdapter {final int NUM_ITEMS = 3; // number of tabspublic CollectionPagerAdapter(FragmentManager fm) {super(fm);}@Overridepublic Fragment getItem(int i) {Fragment fragment = new TabFragment();Bundle args = new Bundle();args.putInt(TabFragment.ARG_OBJECT, i);fragment.setArguments(args);return fragment;}@Overridepublic int getCount() {return NUM_ITEMS;}@Overridepublic CharSequence getPageTitle(int position) {String tabLabel = null;switch (position) {case 0:tabLabel = getString(R.string.label1);break;case 1:tabLabel = getString(R.string.label2);break;case 2:tabLabel = getString(R.string.label3);break;}return tabLabel;}}/*** A fragment that launches other parts of the demo application.*/public static class TabFragment extends Fragment {public static final String ARG_OBJECT = "object";private WebView webView;private Bundle webViewBundle;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);webViewBundle = new Bundle();}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {Bundle args = getArguments();int position = args.getInt(ARG_OBJECT);int tabLayout = 0;switch (position) {case 0:tabLayout = R.layout.tab1;break;case 1:tabLayout = R.layout.tab2;break;case 2:tabLayout = R.layout.tab3;break;}View rootView = inflater.inflate(tabLayout, container, false);webView = (WebView) rootView.findViewById(R.id.webView1);if (webView != null) {webView.setWebViewClient(new WebViewClient());if (webViewBundle == null || webViewBundle.isEmpty()) {webView.loadUrl("http://www.lucazanini.eu");} else {webView.restoreState(webViewBundle);webViewBundle.clear();}}return rootView;}@Overridepublic void onPause() {super.onPause();if (webView != null && webViewBundle.isEmpty()) {webView.saveState(webViewBundle);}}}}
in the internal static class TabFragment:- in the onCreate method I initialize the Bundle webViewBundle where I save the state of the WebView
- in the onCreateView method starting from line 207 I intialize WebView and if webViewBundle is not null I restore the WebView
- in the onPause method I save the state of the WebView in the Bundle webViewBundle
if you comment the lines 211 and 213-216 you can verify what I wrote before and i.e. the fragment is not destroyed switching from a tab to another adjacent tab and then also the state of the WebView is preserved:
this is the default behavior of the ViewPager that retains the selected tab and the two adjacent;
the code in the lines 211-216 and in onPause method is used to restore the state of the WebView if you select the third tab from the first tab.
Switching to the third tab from the first one, the state of the WebView is restored but the WebView is also reloaded unlike what happens switching from the second tab to the third tab when the entire fragment is stored in memory (you can also see this from the different loading times of the third tab depending on whether you are coming from the first or second tab).
You can change the number of the stored tabs from ViewPager using the setOffscreenPageLimit method whose argument is the number of pages (tabs) before and after the current page to keep in memory.
Consider the following code for eu/lucazanini/viewpager/MainActivity.java
1 2 3 4 5 6 7 8 9 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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
package eu.lucazanini.swipeviews; /** * Copyright 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import android.app.ActionBar; import android.app.FragmentTransaction; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.webkit.WebView; import android.webkit.WebViewClient; public class MainActivity extends FragmentActivity implements ActionBar.TabListener { /** * The {@link android.support.v4.view.PagerAdapter} that will provide * fragments for each of the three primary sections of the app. We use a * {@link android.support.v4.app.FragmentPagerAdapter} derivative, which * will keep every loaded fragment in memory. If this becomes too memory * intensive, it may be best to switch to a * {@link android.support.v4.app.FragmentStatePagerAdapter}. */ CollectionPagerAdapter mCollectionPagerAdapter; /** * The {@link android.support.v4.view.ViewPager} that will display the * object collection. */ ViewPager mViewPager; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // Create an adapter that when requested, will return a fragment // representing an object in // the collection. // // ViewPager and its adapters use support library fragments, so we must // use // getSupportFragmentManager. mCollectionPagerAdapter = new CollectionPagerAdapter( getSupportFragmentManager()); // Set up action bar. final ActionBar actionBar = getActionBar(); // Specify that the Home/Up button should not be enabled, since there is // no hierarchical // parent. actionBar.setHomeButtonEnabled(false); // Specify that we will be displaying tabs in the action bar. actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); // Set up the ViewPager, attaching the adapter and setting up a listener // for when the // user swipes between sections. mViewPager = (ViewPager) findViewById(R.id.pager); mViewPager.setOffscreenPageLimit(2); mViewPager.setAdapter(mCollectionPagerAdapter); mViewPager .setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageSelected(int position) { // When swiping between different app sections, select // the corresponding tab. // We can also use ActionBar.Tab#select() to do this if // we have a reference to the // Tab. actionBar.setSelectedNavigationItem(position); } }); // For each of the sections in the app, add a tab to the action bar. for (int i = 0; i < mCollectionPagerAdapter.getCount(); i++) { // Create a tab with text corresponding to the page title defined by // the adapter. // Also specify this Activity object, which implements the // TabListener interface, as the // listener for when this tab is selected. actionBar.addTab(actionBar.newTab() .setText(mCollectionPagerAdapter.getPageTitle(i)) .setTabListener(this)); } } public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { } public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { // When the given tab is selected, switch to the corresponding page in // the ViewPager. mViewPager.setCurrentItem(tab.getPosition()); } public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { } /** * A {@link FragmentPagerAdapter} that returns a fragment corresponding to * one of the primary sections of the app. */ public class CollectionPagerAdapter extends FragmentPagerAdapter { final int NUM_ITEMS = 3; // number of tabs public CollectionPagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int i) { Fragment fragment = new TabFragment(); Bundle args = new Bundle(); args.putInt(TabFragment.ARG_OBJECT, i); fragment.setArguments(args); return fragment; } @Override public int getCount() { return NUM_ITEMS; } @Override public CharSequence getPageTitle(int position) { String tabLabel = null; switch (position) { case 0: tabLabel = getString(R.string.label1); break; case 1: tabLabel = getString(R.string.label2); break; case 2: tabLabel = getString(R.string.label3); break; } return tabLabel; } } /** * A fragment that launches other parts of the demo application. */ public static class TabFragment extends Fragment { public static final String ARG_OBJECT = "object"; private WebView webView; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Bundle args = getArguments(); int position = args.getInt(ARG_OBJECT); int tabLayout = 0; switch (position) { case 0: tabLayout = R.layout.tab1; break; case 1: tabLayout = R.layout.tab2; break; case 2: tabLayout = R.layout.tab3; break; } View rootView = inflater.inflate(tabLayout, container, false); webView = (WebView) rootView.findViewById(R.id.webView1); if (webView != null) { webView.setWebViewClient(new WebViewClient()); webView.loadUrl("http://www.lucazanini.eu"); } return rootView; } } } |
The key point is the line 81 that sets to 2 the number of pages to keep in memory before and after the current one, and in the case of three tabs as in this example it means that all the pages are in memory.
Now the state of WebView is preserved without the need to use a Bundle to save and restore the state because all fragments are stored in memory.
References:
ViewPager
Hi Luca,
One question: Is this compatible with android versions older than 3.0 ?
Following your tutorials it’s working great on android 4.1 and 4.2 but when i tried it on android 2.3.6 i’m getting:
java.lang.ClassNotFoundException in dalvik.system.PathClassLoader.findClass
that exception is thrown at the moment I execute the app.
I tried installing latest compatibility package because I tought actionbar was not supported. I changed my mainclass from FragmentActivity to ActionBarActivity and set theme to compattheme and it’s working on 4.1 and 4.2 but in android 2.3.6 i’m having the same problem again.
Any suggestions or workarounds?
Thanks !
No, sorry, it doesn’t work for versions older than 3.0.
For example getActionBar() at line 67 requires API level 11 or Android 3.0
Hi, first i want to say thanks, this is working GREAT.
The comments in code make it completely clear.
But i have one issue and maybe you can help me :
I managed to put 3 webviews in 3 fragments but whenever i rotate the screen , the webviews gets reloaded.
This is usually solved by overriding on configurationchange and saving the state of the webview but I cannot do that with 3 webviews at the same time, or at least i dont know how. Do you have any idea on how to do that?
Thanks for your time
If there aren’t changes in layout on in other configurations when you rotate the screen you can try this line in AndroidManifest.xml:
android:configChanges=”orientation|screenSize”
See here for more info.
Oohh, i read that article before and tried out but it didn’t work, now i realize I made a a mistake when I wrote the manifest.
It’s working great, can’t believe it was so easy haha.
Thanks a lot from Argentina! keep this site up, it’s great !
Hello Sir , i would like to ad a webview on each tab , and load 3 different url’s for each tab
You can modify onCreateView in MainActivity.java to handle 3 webviews
Same question here?
all tabs loads the same webpage.. how to change this code so each can have a diferent webpage
/**
* A fragment that launches other parts of the demo application.
*/
public static class TabFragment extends Fragment {
public static final String ARG_OBJECT = “object”;
private WebView webView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Bundle args = getArguments();
int position = args.getInt(ARG_OBJECT);
int tabLayout = 0;
switch (position) {
case 0:
tabLayout = R.layout.auto;
break;
case 1:
tabLayout = R.layout.fragment_main;
break;
case 2:
tabLayout = R.layout.fragment_main;
break;
}
View rootView = inflater.inflate(tabLayout, container, false);
webView = (WebView) rootView.findViewById(R.id.browser);
if (webView != null) {
webView.setWebViewClient(new WebViewClient());
webView.loadUrl(“http://www.headgone.net”);
}
return rootView;
}
}
figured it out…
Hello, thank you for the help but I have a question
When I open a webpage and click on some link it will open the link but when I want to go back to the first page by clicking the back button, it directly exits the program.
Need help fast!!!
Can you write a new article on going back to the primary fragment. Thanks.
You can add this code to MainActivity:
and replace onTabUnselected and onTabSelected methods:
Sir, Is it a much to ask if I asked you to make a post on displaying three different web pages on three different tabs. I tired the thing you said it works but when I try to replace tab1 and tab2 with tab3 then it doesn’t work. the app exits after I press back button before going to the parent fragment.
Let me put it clear.
I want 5 tabs with different web pages. and in every web pages after i click a link I should be able to go back to the previous fragment.
I would be so much thankful if you could help.
What should the back button do?
Is it like the back button in the browser, i.e. open the previous page in the web view?
If so the code in my last comment does this, but it works only for the third tab because of the code in onTabSelected, but you can extend to the other tabs.
But maybe you want the back button going to a specific fragment or tab.
If so you can try:
Yes, I want it like back button in the browser. but with this code now when ever I do back it goes to tab 1. I mean when I am in tab 1 and open a link than when i do back it should go to previous link after that may be exit the software.
The code in my first comment does what you ask for the third tab, write the code for the other webviews in others tabs.
Hello, first of all thank you for the fantastic tutorials you have on the web.
I would like to ask you a question, how can I make BACK button works alike in all WebView? If I explain the code as above only works with WebView1 the rest exits the application.
Thank you very much.
Спасибо.
Hi! Thank you so much! This is just what I needed! If you have time can you do a scrollable tabs + swipe version of this?
Thank you!
Thank you very much for long and helpful post. Working great.