Retain-Fragment + ActionBar-Tabs = Absturz!?


#1

Ich habe Tabs in der ActionBar, die ich wie folgt aufbaue:


  public void onCreate(Bundle b){
        setContentView(R.layout.main_activity);
        final Tab tab = getActionBar().newTab();
        tab.setText("Tab 1");
        tab.setTabListener(new TabListener<T>(new Tab1Fragment()));
        getActionBar().addTab(tab);

        tab.setText("Tab 2");
        tab.setTabListener(new TabListener<T>(new Tab2Fragment()));
        getActionBar().addTab(tab);
  }

   private static final class TabListener<T extends Fragment> implements ActionBar.TabListener {
        private final T fragment;
        public TabListener(final T fragment) {this.fragment = fragment;}
        
        public void onTabSelected(final Tab tab, final FragmentTransaction ft) {ft.replace(R.id.tab_target, this.fragment, null);}
        
        public void onTabUnselected(final Tab tab, final FragmentTransaction ft) {}
        public void onTabReselected(final Tab tab, final FragmentTransaction ft) {}
    }
}```Das Funktioniert auch alles wunderbar.

"Problem" scheint zu sein, dass ich in [inline]Tab1Fragment[/inline] ein weiteres Fragment habe, dass [inline]retain = true[/inline] ist:

```public class Tab1Fragment extends Fragment{
  
   public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle b) {
        return inflater.inflate(R.layout.tab_1_fragment, null);
   }

   public void onActivityCreated(Bundle b){
      MyRetainFragment retainFrag = (MyRetainFragment) getFragmentManager().findFragmentByTag("my_frag");
      if (retainFrag == null) {
        retainFrag = new MyRetainFragment(); // retain = true im Konstruktor
      }
      getFragmentManager().beginTransaction().replace(R.id.retain_target, retainFrag, "my_frag").commit(); // HIER KNALLTS ! (Vermutlich)
  }
}```

Die verwendeten Layouts sind:
[B]main_activity.xml[/B]
[xml]<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" >
    <LinearLayout android:id="@+id/tab_target"/>
</RelativeLayout>[/xml]

[b]tab_1_fragment.xml[/b]
[xml]<TableLayout xmlns:android="http://schemas.android.com/apk/res/android">
        <TableRow>
            <LinearLayout android:id="@+id/retain_target"/> <!-- Hier soll das Retain-Fragment rein! -->
    </TableRow>
</TableLayout>[/xml]

Die Fehlermeldung:

11-07 16:33:03.804: E/AndroidRuntime(10895): Caused by: java.lang.IllegalArgumentException: No view found for id 0x7f0a0024 for fragment MyRetainFragment{41fb88a0 #1 id=0x7f0a0024 retain_target}
11-07 16:33:03.804: E/AndroidRuntime(10895): at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:823)
11-07 16:33:03.804: E/AndroidRuntime(10895): at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1035)
11-07 16:33:03.804: E/AndroidRuntime(10895): at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1017)
11-07 16:33:03.804: E/AndroidRuntime(10895): at android.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:1806)
11-07 16:33:03.804: E/AndroidRuntime(10895): at android.app.Activity.performCreate(Activity.java:5166)
11-07 16:33:03.804: E/AndroidRuntime(10895): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1094)
11-07 16:33:03.804: E/AndroidRuntime(10895): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2061)
11-07 16:33:03.804: E/AndroidRuntime(10895): … 12 more

Beim starten geht alles gut. Drehe ich das Tablet in Tab1, verschwindet der gesamt Content. Wechsle ich auf Tab2 und drehe das Tablet, kommt dieser Fehler.

Mir siehts so aus, als würde er die ID [inline]retain_target[/inline] nicht finden. Aber zu dem Zeitpunkt sollte das Tab1Fragment doch schon in der Activity hängen, so dass der FragmentManager die id finden sollte!? Bin verwirrt.

#2

So… nachdem ich mich lange genug mit den Fragments rumgeschlagen habe, bin ich jetzt zu folgender “Lösung” gekommen. Ich habe mich von den Fragments verabschiedet und schalte einfach die entsprechende Views in meiner Activity sichtbar bzw. unsichbar, je nachdem, welches “Tab” ausgewählt ist. Spart ne Menge Stress. :wink:

  public void onCreate(Bundle b){
        setContentView(R.layout.main_activity);
        final Tab tab = getActionBar().newTab();
        tab.setText("Tab 1");
        tab.setTabListener(new TabListener(findViewById(R.id.tab1)));
        getActionBar().addTab(tab);
 
        tab.setText("Tab 2");
        tab.setTabListener(new TabListener(findViewById(R.id.tab2)));
        getActionBar().addTab(tab);
  }
   private static final class TabListener implements ActionBar.TabListener {
        private final View view;
        public TabListener(final View view) {this.view = view;}
        public void onTabSelected(final Tab tab, final FragmentTransaction ft) {view.setVisible(View.VISIBLE);}
        public void onTabUnselected(final Tab tab, final FragmentTransaction ft) {view.setVisible(View.GONE);}
        public void onTabReselected(final Tab tab, final FragmentTransaction ft) {}
    }
}```Damit habe ich zwar keine "echten" Tabs, bin allerdings ne Menge Fragement-Geraffel los. Es funktioniert und ist super einfach. 

Für alle, die "echte" Tabs haben wollen und auf diesen Thread stoßen: Über die Support-Library soll es wohl möglich sein nested Fragments auch vor 4.2 zu nutzen (habe 4.1.2). Habe das  nicht ausprobiert, da mir diese einfache Lösung lieber ist ;)

#3

Hier mal eine Lösung mit der Support Library (ab API 11, für frühere Versionen müsste man noch die Support Library für die ActionBar nutzen):

import android.app.ActionBar.Tab;
import android.app.ActionBar.TabListener;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;

public class MainActivity extends FragmentActivity implements TabListener{

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        final ActionBar actionBar = getActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        
        actionBar.addTab(actionBar.newTab().setText("TAB 1").setTabListener(this).setTag(new Fragment1()));
        actionBar.addTab(actionBar.newTab().setText("TAB 2").setTabListener(this).setTag(new Fragment2()));
    }
    
	public void onTabSelected(Tab tab, android.app.FragmentTransaction ft) {
		getSupportFragmentManager().beginTransaction().replace(R.id.container, (Fragment)tab.getTag()).commit();
	}
	public void onTabReselected(Tab tab, android.app.FragmentTransaction ft) {}
	public void onTabUnselected(Tab tab, android.app.FragmentTransaction ft) {}
}```