Link to home
Create AccountLog in
Avatar of OleSetnes
OleSetnes

asked on

Replacing fragments for ActionBar Tab

I'm creating an App for Android using ActionBarSherlock. The App should have a number of tabs and each tab should have one or two fragments. I wrote the code below but I have a crash when a tab is selected for the second time.

public class MainActivity extends SherlockFragmentActivity implements ActionBar.TabListener {

    private static final String STATE_SELECTED_NAVIGATION_ITEM = "selected_navigation_item";

    private final HashMap<String, TabInfo> mTabs = new HashMap<String, TabInfo>();
    TabInfo mLastTab;

    static final class TabInfo {
        private final Bundle args;
        String  tagLeft;
        boolean bFragmentLeftAdded;
        private Fragment fragmentLeft;
        String  tagRight;
        boolean bFragmentRightAdded;
        private Fragment fragmentRight;

        TabInfo(String _tagLeft, String _tagRight, Fragment _fragmentLeft, Fragment _fragmentRight, Bundle _args) {
            args                = _args;
            tagLeft             = _tagLeft;
            bFragmentLeftAdded  = false;
            fragmentLeft        = _fragmentLeft;
            tagRight            = _tagRight;
            bFragmentRightAdded = false;
            fragmentRight       = _fragmentRight;
        }
    }    


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Set content view
        setContentView(R.layout.activity_main); 

        // Set up the action bar.
        InitializeTabs(savedInstanceState);
    }


    private void InitializeTabs(Bundle args) {
        final ActionBar actionBar = getSupportActionBar();
        actionBar.setDisplayHomeAsUpEnabled(false);
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        actionBar.setDisplayHomeAsUpEnabled(false);

        // For each of the sections in the app, add a tab to the action bar.
        final int MARGIN = 16;
        createTab(
                R.layout.tab_layout_tab1, 
                "Tab1-Left", 
                "Tab1-Right",
                new RoundedColourFragment(getResources().getColor(R.color.android_green), 1f, MARGIN, MARGIN / 2, MARGIN, MARGIN),
                new RoundedColourFragment(getResources().getColor(R.color.honeycombish_blue), 2f, MARGIN / 2, MARGIN, MARGIN, MARGIN),
                args);
        createTab(
                R.layout.tab_layout_tab2, 
                "Tab2",
                "",
                new RoundedColourFragment(getResources().getColor(R.color.soft_grey), 1f, MARGIN, MARGIN, MARGIN, MARGIN),
                null,
                args);    }

    public void createTab(int view, String tagLeft, String tagRight, Fragment _fragmentLeft, Fragment _fragmentRight, Bundle args) {

        TabInfo info = new TabInfo(tagLeft, tagRight, _fragmentLeft, _fragmentRight, args);

        ActionBar.Tab tab = getSupportActionBar().newTab();
        tab.setTag(info);
        tab.setTabListener(this);
        tab.setCustomView(view);
        mTabs.put(tagLeft, info);

        getSupportActionBar().addTab(tab);
    }


    @Override
    public void onRestoreInstanceState(Bundle savedInstanceState) {
        if (savedInstanceState.containsKey(STATE_SELECTED_NAVIGATION_ITEM)) {
            getSupportActionBar().setSelectedNavigationItem(
                    savedInstanceState.getInt(STATE_SELECTED_NAVIGATION_ITEM));
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        outState.putInt(STATE_SELECTED_NAVIGATION_ITEM,
                getSupportActionBar().getSelectedNavigationIndex());
    }

    @Override
    public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
        // When the given tab is selected, show the tab contents in the container
        TabInfo tabInfo = (TabInfo)tab.getTag();

        for (int i = 0; i < mTabs.size(); i++) {
            if (mTabs.get(i) == tabInfo) {
                break;
            }
        }  

        if (mLastTab == tabInfo) 
            return;

        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();

        if (mLastTab != null) {
            if (mLastTab.fragmentLeft != null) {
                ft.detach(mLastTab.fragmentLeft);
            }
            if (mLastTab.fragmentRight != null) {
                ft.detach(mLastTab.fragmentRight);
            }
        }

        mLastTab = tabInfo;

        if (!tabInfo.bFragmentLeftAdded) {
            ft.add(R.id.root, tabInfo.fragmentLeft, tabInfo.tagLeft);
            tabInfo.bFragmentLeftAdded = true;
        }
        else
            ft.attach(tabInfo.fragmentLeft);

        if (tabInfo.fragmentRight != null)
        {
            if (!tabInfo.bFragmentRightAdded) {
                ft.add(R.id.root, tabInfo.fragmentRight, tabInfo.tagRight);
                tabInfo.bFragmentRightAdded = true;
            }
            else
                ft.attach(tabInfo.fragmentRight);
        }

        ft.commit();
    }

    @Override
    public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
    }

    @Override
    public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
    }     
}

Open in new window


The "RoundedColourFragment" class is just a simple fragment that fills out the fragment with a color. It is only for testing the code.

The activity_main.xml looks like this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/root"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    tools:context=".MainActivity"/>

Open in new window


The code crashes the second time I select a tab that had previously been selected - and the code crash when leaving OnTabSelected(). Any ideas what goes wrong?

Thanks, Ole
Avatar of krakatoa
krakatoa
Flag of United Kingdom of Great Britain and Northern Ireland image

Anyone Android-oriented viewing your question will almost certainly want the stacktrace.
Avatar of OleSetnes
OleSetnes

ASKER

If I debug it the debugger (Eclipse) opens a new tab and writes:

Class File Editor
The Jar file C:\...Android-16\android.jar has no source attachment.

// Compiled from ViewGroup.java (version 1.5 : 49.0, super bit)
public abstract class android.view.ViewGroup extends android.view.View implements android.view.ViewParent, android.view.ViewManager {

  // Field descriptor #19 I
  public static final int FOCUS_BEFORE_DESCENDANTS = 131072;
  
  // Field descriptor #19 I
  public static final int FOCUS_AFTER_DESCENDANTS = 262144;
  
  // Field descriptor #19 I
  public static final int FOCUS_BLOCK_DESCENDANTS = 393216;
  
  // Field descriptor #19 I
  public static final int PERSISTENT_NO_CACHE = 0;
  
  // Field descriptor #19 I
  public static final int PERSISTENT_ANIMATION_CACHE = 1;
  
  // Field descriptor #19 I
  public static final int PERSISTENT_SCROLLING_CACHE = 2;
  
  // Field descriptor #19 I
  public static final int PERSISTENT_ALL_CACHES = 3;
  
  // Field descriptor #19 I
  protected static final int CLIP_TO_PADDING_MASK = 34;
  
  // Method descriptor #37 (Landroid/content/Context;)V
  // Stack: 4, Locals: 2
  public ViewGroup(android.content.Context context);
     0  aload_0 [this]
     1  aconst_null
     2  checkcast android.content.Context [1]
     5  aconst_null
     6  checkcast android.util.AttributeSet [2]
     9  iconst_0
    10  invokespecial android.view.View(android.content.Context, android.util.AttributeSet, int) [3]
    13  new java.lang.RuntimeException [4]
    16  dup
    17  ldc <String "Stub!"> [5]
    19  invokespecial java.lang.RuntimeException(java.lang.String) [6]
    22  athrow
      Line numbers:
        [pc: 0, line: 45]
      Local variable table:
        [pc: 0, pc: 23] local: this index: 0 type: android.view.ViewGroup
        [pc: 0, pc: 23] local: context index: 1 type: android.content.Context
  
  // Method descriptor #45 (Landroid/content/Context;Landroid/util/AttributeSet;)V
  // Stack: 4, Locals: 3
  public ViewGroup(android.content.Context context, android.util.AttributeSet attrs);
     0  aload_0 [this]
     1  aconst_null
     2  checkcast android.content.Context [1]
     5  aconst_null
     6  checkcast android.util.AttributeSet [2]
     9  iconst_0
    10  invokespecial android.view.View(android.content.Context, android.util.AttributeSet, int) [3]
    13  new java.lang.RuntimeException [4]
    16  dup
    17  ldc <String "Stub!"> [5]
    19  invokespecial java.lang.RuntimeException(java.lang.String) [6]
    22  athrow
      Line numbers:
        [pc: 0, line: 46]
      Local variable table:
        [pc: 0, pc: 23] local: this index: 0 type: android.view.ViewGroup
        [pc: 0, pc: 23] local: context index: 1 type: android.content.Context
        [pc: 0, pc: 23] local: attrs index: 2 type: android.util.AttributeSet
  
  // Method descriptor #48 (Landroid/content/Context;Landroid/util/AttributeSet;I)V
  // Stack: 4, Locals: 4
  public ViewGroup(android.content.Context context, android.util.AttributeSet attrs, int defStyle);
     0  aload_0 [this]
     1  aconst_null
     2  checkcast android.content.Context [1]
     5  aconst_null
     6  checkcast android.util.AttributeSet [2]
     9  iconst_0
    10  invokespecial android.view.View(android.content.Context, android.util.AttributeSet, int) [3]
    13  new java.lang.RuntimeException [4]
    16  dup
    17  ldc <String "Stub!"> [5]
    19  invokespecial java.lang.RuntimeException(java.lang.String) [6]
    22  athrow
      Line numbers:
        [pc: 0, line: 47]
      Local variable table:
        [pc: 0, pc: 23] local: this index: 0 type: android.view.ViewGroup
        [pc: 0, pc: 23] local: context index: 1 type: android.content.Context
        [pc: 0, pc: 23] local: attrs index: 2 type: android.util.AttributeSet
        [pc: 0, pc: 23] local: defStyle index: 3 type: int
  
  // Method descriptor #51 ()I
  // Stack: 3, Locals: 1
  @android.view.ViewDebug.ExportedProperty(category="focus",
    mapping={@android.view.ViewDebug.IntToString(from=(int) 131072,
            to="FOCUS_BEFORE_DESCENDANTS"),@android.view.ViewDebug.IntToString(from=(int) 262144,
            to="FOCUS_AFTER_DESCENDANTS"),@android.view.ViewDebug.IntToString(from=(int) 393216,
            to="FOCUS_BLOCK_DESCENDANTS")})
  public int getDescendantFocusability();
     0  new java.lang.RuntimeException [4]
     3  dup
     4  ldc <String "Stub!"> [5]
     6  invokespecial java.lang.RuntimeException(java.lang.String) [6]
     9  athrow
      Line numbers:
        [pc: 0, line: 49]
      Local variable table:
        [pc: 0, pc: 10] local: this index: 0 type: android.view.ViewGroup
  
  // Method descriptor #65 (I)V
  // Stack: 3, Locals: 2
  public void setDescendantFocusability(int focusability);
     0  new java.lang.RuntimeException [4]
     3  dup
     4  ldc <String "Stub!"> [5]
     6  invokespecial java.lang.RuntimeException(java.lang.String) [6]
     9  athrow
      Line numbers:
        [pc: 0, line: 50]
      Local variable table:
        [pc: 0, pc: 10] local: this index: 0 type: android.view.ViewGroup
        [pc: 0, pc: 10] local: focusability index: 1 type: int
  
  // Method descriptor #68 (Landroid/view/View;Landroid/view/View;)V
  // Stack: 3, Locals: 3
  public void requestChildFocus(android.view.View child, android.view.View focused);
     0  new java.lang.RuntimeException [4]
     3  dup
     4  ldc <String "Stub!"> [5]
     6  invokespecial java.lang.RuntimeException(java.lang.String) [6]
     9  athrow
      Line numbers:
        [pc: 0, line: 51]
      Local variable table:
        [pc: 0, pc: 10] local: this index: 0 type: android.view.ViewGroup
        [pc: 0, pc: 10] local: child index: 1 type: android.view.View
        [pc: 0, pc: 10] local: focused index: 2 type: android.view.View
  
  // Method descriptor #73 (Landroid/view/View;)V
  // Stack: 3, Locals: 2
  public void focusableViewAvailable(android.view.View v);
     0  new java.lang.RuntimeException [4]
     3  dup
     4  ldc <String "Stub!"> [5]
     6  invokespecial java.lang.RuntimeException(java.lang.String) [6]
     9  athrow
      Line numbers:
        [pc: 0, line: 52]
      Local variable table:
        [pc: 0, pc: 10] local: this index: 0 type: android.view.ViewGroup
        [pc: 0, pc: 10] local: v index: 1 type: android.view.View
  
  // Method descriptor #76 (Landroid/view/View;)Z
  // Stack: 3, Locals: 2
  public boolean showContextMenuForChild(android.view.View originalView);
     0  new java.lang.RuntimeException [4]
     3  dup
     4  ldc <String "Stub!"> [5]
     6  invokespecial java.lang.RuntimeException(java.lang.String) [6]
     9  athrow
      Line numbers:
        [pc: 0, line: 53]
      Local variable table:
        [pc: 0, pc: 10] local: this index: 0 type: android.view.ViewGroup
        [pc: 0, pc: 10] local: originalView index: 1 type: android.view.View
  
  // Method descriptor #81 (Landroid/view/View;Landroid/view/ActionMode$Callback;)Landroid/view/ActionMode;
  // Stack: 3, Locals: 3
  public android.view.ActionMode startActionModeForChild(android.view.View originalView, android.view.ActionMode.Callback callback);
     0  new java.lang.RuntimeException [4]
     3  dup
     4  ldc <String "Stub!"> [5]
     6  invokespecial java.lang.RuntimeException(java.lang.String) [6]
     9  athrow
      Line numbers:
        [pc: 0, line: 54]
      Local variable table:
        [pc: 0, pc: 10] local: this index: 0 type: android.view.ViewGroup
        [pc: 0, pc: 10] local: originalView index: 1 type: android.view.View
        [pc: 0, pc: 10] local: callback index: 2 type: android.view.ActionMode.Callback

Open in new window


And the stack is:

Guest [Android Application]      
      DalvikVM[localhost:8647]      
            Thread [<1> main] (Suspended (exception IllegalStateException))      
                  <VM does not provide monitor information>      
                  NoSaveStateFrameLayout(ViewGroup).addViewInner(View, int, ViewGroup$LayoutParams, boolean) line: 3351      
                  NoSaveStateFrameLayout(ViewGroup).addView(View, int, ViewGroup$LayoutParams) line: 3222      
                  NoSaveStateFrameLayout(ViewGroup).addView(View, int) line: 3179      
                  NoSaveStateFrameLayout(ViewGroup).addView(View) line: 3159      
                  NoSaveStateFrameLayout.wrap(View) line: 40      
                  FragmentManagerImpl.moveToState(Fragment, int, int, int, boolean) line: 875      
                  FragmentManagerImpl.attachFragment(Fragment, int, int) line: 1251      
                  BackStackRecord.run() line: 625      
                  FragmentManagerImpl.execPendingActions() line: 1431      
                  FragmentManagerImpl$1.run() line: 420      
                  FragmentActivity$1(Handler).handleCallback(Message) line: 605      
                  FragmentActivity$1(Handler).dispatchMessage(Message) line: 92      
                  Looper.loop() line: 137      
                  ActivityThread.main(String[]) line: 4517      
                  Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method]      
                  Method.invoke(Object, Object...) line: 511      
                  ZygoteInit$MethodAndArgsCaller.run() line: 993      
                  ZygoteInit.main(String[]) line: 760      
                  NativeStart.main(String[]) line: not available [native method]      
            Thread [<11> Binder Thread #3] (Running)      
            Thread [<10> Binder Thread #2] (Running)      
            Thread [<9> Binder Thread #1] (Running)      
            Daemon Thread [<8> FinalizerWatchdogDaemon] (Running)      
            Daemon Thread [<7> FinalizerDaemon] (Running)      
            Daemon Thread [<6> ReferenceQueueDaemon] (Running)
I'm not doing Android, but it looks like there is an issue for you here :

NoSaveStateFrameLayout(ViewGroup).addViewInner(View, int, ViewGroup$LayoutParams, boolean) line: 3351  
ASKER CERTIFIED SOLUTION
Avatar of Santhana
Santhana
Flag of India image

Link to home
membership
Create an account to see this answer
Signing up is free. No credit card required.
Create Account
Thanks santhana

The RoundedColourFragment class that I took and used from an ActionBarSherlock example is created in memory without a layout XML file. If I use an XML layout resource for the fragment - as in your suggestion - the code works and doesn't crash. I don't understand why it is so, but as I intend to use XML layout files it is not important why the RoundedColourFragment class crashes - perhaps something is released when the fragment is detached.

Br Ole