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
<uses-permission android:name="android.permission.INTERNET"/>
- replace the file res/layout/tab3.xml
<?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" > <WebView android: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
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.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; private Bundle webViewBundle; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); webViewBundle = new Bundle(); } @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()); if (webViewBundle == null || webViewBundle.isEmpty()) { webView.loadUrl("http://www.lucazanini.eu"); } else { webView.restoreState(webViewBundle); webViewBundle.clear(); } } return rootView; } @Override public 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
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
Leave a Reply