I have a tabbed application built with fragments and ActionBarSherlock. I have 7 tabs. Here's what's happening.
When I select any tab, the onCreate method for the associated fragment is called as expected. The problem is that the onCreate method is called for the next adjacent tab as well. For instance:
- App starts in tab1 and onCreate is called as expected
- tab2 onCreate is called as well (should not happen)
- -------
- Click on tab2 and onCreate is called as expected (even though it's already been called)
- tab3 onCreate is called as well (should not happen)
- -------
- Click on tab6 and onCreate is called as expected
- tab7 onCreate is called as well (should not happen)
- -------
- And really weird, click on tab7 (the last tab)
- tab6 (2nd to last tab) onCreate is called as well (should not happen)
I've read a couple of possible problems and checked to make sure it's not happening here:
- Not using unique tag for each tab (they are unique)
- Emulator has bug that calls onCreate twice (I get the same behavior on my ICS device)
So it's not the two previous possibilities and I'll completely out of ideas. The program runs fine but loading two fragments (which are webviews) takes too much time and isn't the behavior I expected.
Here's my code for the main activity onCreate which creates the tab host: EDITED:
public class SynergyWorldwideActivity extends SherlockFragmentActivity
{
//TabHost mTabHost;
ViewPager mViewPager;
TabsAdapter mTabsAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set up the view pager
setContentView(R.layout.fragment_tabs_pager);
mViewPager = (ViewPager)findViewById(R.id.pager);
// Set up action bar
final ActionBar bar = getSupportActionBar();
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
bar.setDisplayShowTitleEnabled(true);
//bar.setDisplayShowHomeEnabled(false);
// Creat tabs with bundled URLs
Bundle tab1Args=new Bundle(), tab2Args=new Bundle(), tab3Args=new Bundle(),
tab4Args=new Bundle(), tab5Args=new Bundle(), tab6Args=new Bundle(), tab7Args=new Bundle();
tab1Args.putString("tabURL", getString(R.string.webtab1_URL));
tab2Args.putString("tabURL", getString(R.string.webtab2_URL));
tab3Args.putString("tabURL", getString(R.string.webtab3_URL));
tab4Args.putString("tabURL", getString(R.string.webtab4_URL));
tab5Args.putString("tabURL", getString(R.string.webtab5_URL));
tab6Args.putString("tabURL", getString(R.string.webtab6_URL));
tab7Args.putString("tabURL", getString(R.string.webtab7_URL));
mTabsAdapter = new TabsAdapter(this, mViewPager);
mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.webtab1_name)),
WebTabFragment.MyWebviewFragment.class, tab1Args);
mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.webtab2_name)),
WebTabFragment.MyWebviewFragment.class, tab2Args);
mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.webtab3_name)),
WebTabFragment.MyWebviewFragment.class, tab3Args);
mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.webtab4_name)),
WebTabFragment.MyWebviewFragment.class, tab4Args);
mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.webtab5_name)),
WebTabFragment.MyWebviewFragment.class, tab5Args);
mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.webtab6_name)),
WebTabFragment.MyWebviewFragment.class, tab6Args);
mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.webtab7_name)),
WebTabFragment.MyWebviewFragment.class, tab7Args);
if (savedInstanceState != null) {
bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("tab", getActionBar().getSelectedNavigationIndex());
}
/**
* This is a helper class that implements the management of tabs and all
* details of connecting a ViewPager with associated TabHost. It relies on a
* trick. Normally a tab host has a simple API for supplying a View or
* Intent that each tab will show. This is not sufficient for switching
* between pages. So instead we make the content part of the tab host
* 0dp high (it is not shown) and the TabsAdapter supplies its own dummy
* view to show as the tab content. It listens to changes in tabs, and takes
* care of switch to the correct paged in the ViewPager whenever the selected
* tab changes.
*/
public static class TabsAdapter extends FragmentPagerAdapter implements ActionBar.TabListener, ViewPager.OnPageChangeListener{
private final Context mContext;
//private final TabHost mTabHost;
private final ActionBar mActionBar;
private final ViewPager mViewPager;
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
static final class TabInfo {
private final Class<?> clss;
private final Bundle args;
TabInfo(Class<?> _class, Bundle _args) {
clss = _class;
args = _args;
}
}
public TabsAdapter(FragmentActivity activity, ViewPager pager) {
super(activity.getSupportFragmentManager());
mContext = activity;
mActionBar = ((SherlockFragmentActivity) activity).getSupportActionBar();
mViewPager = pager;
mViewPager.setAdapter(this);
mViewPager.setOnPageChangeListener(this);
}
public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) {
TabInfo info = new TabInfo(clss, args);
tab.setTag(info);
tab.setTabListener(this);
mTabs.add(info);
mActionBar.addTab(tab);
notifyDataSetChanged();
}
@Override
public int getCount()
{
int iCount = mTabs.size();
return iCount;
}
@Override
public Fragment getItem(int position)
{
TabInfo info = mTabs.get(position);
return Fragment.instantiate(mContext, info.clss.getName(), info.args);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
{
}
@Override
public void onPageSelected(int position)
{
mActionBar.setSelectedNavigationItem(position);
}
@Override
public void onPageScrollStateChanged(int state)
{
}
@Override
public void onTabSelected(Tab tab)
{
Object tag = tab.getTag();
for (int i=0; i<mTabs.size(); i++) {
if (mTabs.get(i) == tag) {
mViewPager.setCurrentItem(i);
}
}
}
@Override
public void onTabUnselected(Tab tab)
{
}
@Override
public void onTabReselected(Tab tab)
{
}
}
}
Here's the code for the tab fragments: EDITED:
public class WebTabFragment extends SherlockFragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState == null)
{
FragmentManager fm = getSupportFragmentManager();
if (fm.findFragmentById(android.R.id.content) == null) {
MyWebviewFragment myWebView = new MyWebviewFragment();
fm.beginTransaction().add(android.R.id.content, myWebView).commit();
}
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//outState.putString("tabNumber", mTabNumber);
}
public static class MyWebviewFragment extends SherlockFragment {
final static private String tag = MyWebviewFragment.class.getSimpleName();
String mTabURL;
private WebView mWebView = null;
static final int REFRESH_ID = Menu.FIRST;
private ProgressDialog spinnerDlg;
@Override
public void onSaveInstanceState(Bundle outState)
{
if(mWebView.saveState(outState) == null)
Log.i(tag,"Saving state FAILED!");
else
Log.i(tag, "Saving state succeeded.");
}
@Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true);
}
@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.add(Menu.NONE, REFRESH_ID, 0, getString(R.string.refresh_string))
.setIcon(R.drawable.ic_action_refresh)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
@Override public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case REFRESH_ID:
if(mWebView != null)
mWebView.reload();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/**
* When creating, retrieve this instance's number from its arguments.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Tell the framework to try to keep this fragment around
// during a configuration change.
setRetainInstance(true);
mTabURL = getArguments() != null ? getArguments().getString("tabURL") : "http://www.google.com";
}
/**
* The Fragment's UI is just a simple text view showing its
* instance number.
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Create view object to return
View v = inflater.inflate(R.layout.webview_layout, container, false);
// Set up webview object
if (mWebView != null) {
mWebView.destroy();
}
mWebView = (WebView)v.findViewById(R.id.webview_fragment);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.setOnKeyListener(new OnKeyListener(){
@Override
public boolean onKey(View v, int keyCode, KeyEvent event)
{
if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
mWebView.goBack();
return true;
}
return false;
}
});
// Check to see if it has been saved and restore it if true
if(savedInstanceState != null)
{
if (savedInstanceState.isEmpty())
Log.i(tag, "Can't restore state because bundle is empty.");
else
{
if (mWebView.restoreState(savedInstanceState) == null)
Log.i(tag, "Restoring state FAILED!");
else
Log.i(tag, "Restoring state succeeded.");
}
}
else
{
// Load web page
mWebView.setWebViewClient(new MyWebViewClient());
mWebView.getSettings().setPluginsEnabled(true);
mWebView.getSettings().setBuiltInZoomControls(false);
mWebView.getSettings().setSupportZoom(false);
mWebView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
mWebView.getSettings().setAllowFileAccess(true);
mWebView.getSettings().setDomStorageEnabled(true);
mWebView.loadUrl(mTabURL);
}
return v;
}
@Override
public void onDestroy()
{
super.onDestroy();
}
@Override
public void onDestroyView()
{
super.onDestroyView();
}
@Override
public void onPause()
{
super.onPause();
}
@Override
public void onResume()
{
super.onResume();
}
@Override
public void onConfigurationChanged(Configuration newConfig)
{
// TODO Auto-generated method stub
super.onConfigurationChanged(newConfig);
}
public class MyWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// YouTube video link
if (url.startsWith("http://youtu.be"))
{
String urlSubString = url.substring("http://youtu.be/".length());
String newURL = String.format("http://www.youtube.com/v/%s", urlSubString);
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(newURL)));
return (true);
}
return (false);
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
if(spinnerDlg == null)
{
spinnerDlg = new ProgressDialog(getActivity());
spinnerDlg.setMessage("Loading....");
spinnerDlg.show();
}
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if(spinnerDlg != null)
{
spinnerDlg.dismiss();
}
spinnerDlg = null;
}
}
}
}