android - Nested retained FragmentTabHost doesn't attach tabs to new Activity -


i have been playing around fragmenttabhost android v4 support library while , came upon serious issue couldn't resolve. target requirements follows.

1) fragment include tabs built fragments include nested fragments well, hierarchy this:

  • android.support.v4.app.fragment (hostfragment)
    • android.support.v4.app.fragmenttabhost (tabhost)
      • android.support.v4.app.fragment (tab1fragment)
        • android.support.v4.app.fragment (tab1fragment1)
        • android.support.v4.app.fragment (tab1fragment2)
        • android.support.v4.app.fragment (tab1fragment3)
      • android.support.v4.app.fragment (tab2fragment)
        • android.support.v4.app.fragment (tab2fragment1)
        • android.support.v4.app.fragment (tab2fragment2)

hostfragment + tabhost setup done according says in documentation here.

2) upon screen rotation retain status of hostfragment recreation of setup resources consuming operation , besides layout order nothing changes on screen no work needed display screen. return different layout in oncreateview callback of tab1 , tab2 , reattach existing fragments same id's.

to accomplish 1 think setting hostfragment.setretaininstance(true) work and, partially, does. nothing getting recreated, tabs preserved should, worked fine. let's point.

the problem

the big problem came bit later. appears tab1fragment1 (and other tabxfragmenty matter) not attached new activity created upon rotation. attached on first run of activity , nothing being done when user rotates screen.

the consequences

this causes 2 big issues me (might there more):

  1. initial activity leaked because fragments in tabhost attached no reason @ all.
  2. when call getactivity() on of tabxfragmenty old activity , dreadful "cannot perform operation after onsavedinstance" exception.

this leads serious issues when want parent activity. also, doesn't happen when setting hostfragment.setretaininstance(false) get's recreated , works fine.

the question

is issue seeing? couldn't find on topic , have reviewed code hundred times.

i have tried changing com.example.android.supportv4.app.fragmenttabsfragmentsupport v4 examples, did set retain it's instance , added logging information in 1 of nested fragments on every onattach call verify , can see there still same issue. puzzled. help.

recently i've been working on tabhost fragments , found same issue. basically, need control fragments being attached/detached, in case, within ontabchanged() event.

i have tabinfo class store following information each tab

  • the name of fragment (for identification purposes).
  • the fragment attach.
  • the tabhost.tabspec specification, if want remove tab, there's info recreating rest of them tabhost bit tricky removing tabs.
  • the bundle associated (basically, saved instance prior configuration change).

also, need keep track of lasttab , newtab opened tabhost doesn't have native way of knowing tabhost have been closed, declaring variable class-wide. , how handle right now, i'll try add many comments can:

@override public void ontabchanged(final string tag) {   // info tab triggered using tag   final tabinfo newtab = mtabinfo.get(tag);    // if there's been tab change...   if (lasttab != newtab) {     // you'll have make transaction replacing fragment     final fragmenttransaction ft = this.getsupportfragmentmanager().begintransaction();      // if last tab has fragment associated     if ((lasttab != null) && (lasttab.getfragment() != null)) {       // in case i've additional level of complexion, have nested fragment       // inside content fragment. have remove first prior detaching       // parent fragment. not needed if have 1 fragment content.       final fragment loginfrag = (fragment) lasttab.getfragment().getactivity().getsupportfragmentmanager().findfragmentbyid(lasttab.getloginfragid());       ft.remove(loginfrag);        // , trick: calling detach() instead of remove()       // seems versions there's problem makes not apply it,       // calling remove remove fragment       ft.remove(lasttab.getfragment());     }      // you've detached old fragment, have attach new 1     if (newtab != null) {       if (newtab.getfragment() == null) {         // inflate new content if it's first time tab has been fired         final tabfragmentinflater tabinf = new tabfragmentinflater();         newtab.setfragment(fragment.instantiate(this, tabinf.getclass().getname(), newtab.getargs()));         ft.add(r.id.realtabcontent, newtab.getfragment(), newtab.gettag());       }       else {         // if not, attach fragment         ft.attach(newtab.getfragment());       }     }      ft.commit();     this.getsupportfragmentmanager().executependingtransactions();      lasttab = newtab;   } } 

---- edit ----

answering questions:

  • i indeed call newtabspec() on tabhost, it's in separate method didn't include because included ontabchanged() callback. have create tabs normally, ontabchanged() method gets fired when click on tabs. creation, this:

    private void inittabhost(final bundle args) {   final tabhost th = (tabhost) findviewbyid(android.r.id.tabhost);   th.setup();    // have hashmap called fragmap key define tab's name   // , value, have integer unique identifier   // know inflate when call tabfragmentinflater (i   // add code below). can add id or tag also.   (final string tablabel : fragmap.keyset()) {     final tabhost.tabspec tabspec = th.get().newtabspec(tabname).setindicator(tabname);     // here initialize tabinfo object tab, include additional      // handling info: name of tab, tab spec, unique id explained above,     // forth argument irrelevant in example, , args (saved instance)     final tabinfo tabinfo = new tabinfo(tabname, tabspec, fragmap.get(tablabel), r.id.somelayout, args);      // not tabhost's `addtab()` method, i'll call inside     // method (see below)     myclass.addtab(this, th, tabspec, tabinfo);      // have able keep tracking of info     mtabinfo.put(tabinfo.gettag(), tabinfo);   }    // tab creation, force start on first tab   this.ontabchanged(firsttabnametag);   th.setontabchangedlistener(this); } 

the addtab() method simple, inflates object , detaches if needed.

private static void addtab(final myclass activity, final tabhost tabhost, final tabhost.tabspec tabspec, final tabinfo tabinfo) {   tabspec.setcontent(new tabfactory(activity));    // empty view   final string tag = tabspec.gettag();    // here check if there's fragment tab, in    // saved state. if happens, deactivate it, because our   // former state tab "not shown".   tabinfo.setfragment(activity.getsupportfragmentmanager().findfragmentbytag(tag));   if ((tabinfo.getfragment() != null) && (!tabinfo.getfragment().isdetached())) {     final fragmenttransaction ft = activity.getsupportfragmentmanager().begintransaction();     ft.detach(tabinfo.getfragment());     ft.commit();      activity.getsupportfragmentmanager().executependingtransactions();   }    // actually, there's call "official" `addtab()` `tabhost`   tabhost.addtab(tabspec); } 
  • so there's tabfragmentinflater left. it's fragment inflates according layout depending on unique id mentioned above, it's this:

    @override public view oncreateview(final layoutinflater inflater, final viewgroup container, final bundle savedinstancestate) {   fragid = getarguments().getint("fragid");    if (view != null) {     viewgroup parent = (viewgroup) view.getparent();     if (parent != null)       parent.removeview(view);   }    try {     switch (fragid) {       case 1:         // first tab...         final linearlayout fraglayout = (linearlayout) inflater.inflate(r.layout.myfirsttab_fragment_layout, container, false);          ...               return fraglayout;        case 2:           // second tab         fraglayout = (linearlayout) inflater.inflate(r.layout.mysecondtab_fragment_layout, container, false);          ...         return fraglayout;        ...     }   }    catch (final inflateexception e) { return view; }    return null;     } 

Comments

Popular posts from this blog

c# - How to get the current UAC mode -

postgresql - Lazarus + Postgres: incomplete startup packet -

javascript - Ajax jqXHR.status==0 fix error -