2014-12-23 11 views
6

Sto implementando un ViewPager con schede simili all'implementazione fornita da Google here.ViewPager Android con scheda che non mantiene lo stato dopo la rotazione dello schermo

La mia app ha il seguente comportamento. My ViewPager ha 3 "pagine" (fragA, fragB, fragC) e sto implementando 3 schede in cima al ViewPager. La differenza tra l'implementazione di Google e la mia è che le schede non sono utilizzate per navigare tra i frammenti. Ad esempio, premendo una delle mie schede, carica un diverso set di dati sul frammento visibile. Posso avere fragA con tab3 selezionato, fragB con tab2 selezionato e fragC con tab1 selezionato.

Il problema è quando ruoto lo schermo. Se sono su FragmentA con la terza scheda selezionata, quando ruoto lo schermo, rimango nello stesso frammento ma ora, la prima scheda è selezionata. Voglio mantenere la stessa scheda selezionata quando si ruota lo schermo.

Questa è la mia classe per la gestione delle schede:

public class ManagerTabs extends Fragment { 

    private TabHost.TabContentFactory mFactory = new TabHost.TabContentFactory() { 

     @Override 
     public View createTabContent(String tag) { 
      View v = new View(getActivity()); 
      v.setMinimumWidth(0); 
      v.setMinimumHeight(0); 
      return v; 
     } 
    }; 

    public static ManagerTabs newManager() { 
     return new ManagerTabs(); 
    } 


    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
    Bundle savedInstanceState) { 
     view = inflater.inflate(R.layout.datatabs, container, false); 
     createTabs(); 
     createPager(); 
     return view; 
    } 

    @Override 
    public void onViewCreated(View view, Bundle savedInstanceState) { 
     super.onViewCreated(view, savedInstanceState); 

     if (savedInstanceState != null) { 
      currentPage = savedInstanceState.getInt("currentPage", currentPage); 
      currentTab = savedInstanceState.getInt("currentTab", currentTab); 
      mTabHost.setCurrentTab(currentTab); 
     } 
     mPager.setCurrentItem(currentPage); 
    } 

    private void createPager() { 
     mPager = (ViewPager) view.findViewById(R.id.datapager); 
     pagerAdapter = new PagerAdapter(getChildFragmentManager()); 
     mPager.setAdapter(pagerAdapter); 

     IconPageIndicator indicator = (IconPageIndicator) rootView.findViewById(R.id.indicator); 
     indicator.setViewPager(pager); 
     indicator.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { 
      @Override 
      public void onPageSelected(int position) { 
       currentTab = mTabHost.getCurrentTab(); 

       currentPage = mPager.getCurrentItem(); 

       switch (currentPage) { 
       case 0: 
        ((FragmentA) pagerAdapter.getCurrentFragment(currentPage)).getData(currentTab); 
        break; 
       case 1: 
        ((FragmentB) pagerAdapter.getCurrentFragment(currentPage)).getData(currentTab); 
        break; 
       case 2: 
        ((FragmentC) pagerAdapter.getCurrentFragment(currentPage)).getData(currentTab); 
        break; 
       } 
      } 
     }); 
    } 

    private void createTabs() { 

     mTabHost = (TabHost) view.findViewById(android.R.id.tabhost); 
     mTabHost.setup(); 

     mTabHost.addTab(mTabHost.newTabSpec(getString(R.string.day))setIndicator(getString(R.string.day)).setContent(mFactory)); 
     mTabHost.addTab(mTabHost.newTabSpec(getString(R.string.month)).setIndicator(getString(R.string.month)).setContent(mFactory)); 
     mTabHost.addTab(mTabHost.newTabSpec(getString(R.string.year)).setIndicator(getString(R.string.year)).setContent(mFactory)); 

     mTabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() { 
      public void onTabChanged(String tag) { 
       currentTab = mTabHost.getCurrentTab(); 
       currentPage = mPager.getCurrentItem(); 
       switch (currentPage) { 
       case 0: 
        ((FragmentA) pagerAdapter.getCurrentFragment(currentPage)).getData(currentTab); 
        break; 
       case 1: 
        ((FragmentB) pagerAdapter.getCurrentFragment(currentPage)).getData(currentTab); 
        break; 
       case 2: 
        ((FragmentC) pagerAdapter.getCurrentFragment(currentPage)).getData(currentTab); 
        break; 
       } 
      } 
     }); 
    } 

    @Override 
    public void onSaveInstanceState(Bundle outState) { 
     super.onSaveInstanceState(outState); 
     outState.putInt("currentPage", mPager.getCurrentItem()); 
     outState.putInt("currentTab", mTabHost.getCurrentTab()); 
    } 
} 

E questo è l'adattatore che crea i frammenti:

public class PagerAdapter extends FragmentPagerAdapter implements IconPagerAdapter{ 

    private Fragment[] currentFragment = new Fragment[3]; 

    public PagerAdapter(FragmentManager fm) { 
     super(fm); 
    } 

    @Override 
    public Fragment getItem(int index) { 
     Fragment fragment; 
     switch (index) { 
     case 0: 
      fragment = FragmentA.newInstance(); 
      break; 
     case 1: 
      fragment = FragmentB.newInstance(); 
      break; 
     case 2: 
      fragment = FragmentC.newInstance(); 
      break; 
     default: 
      fragment = null; 
      break; 
     } 

     currentFragment[index] = fragment; 

     return fragment; 
    } 

    @Override 
    public int getIconResId(int index) { 

     switch (index) { 
     case 0: 
      return R.drawable.fraga; 
     case 1: 
      return R.drawable.fragb; 
     case 2: 
      return R.drawable.fragc; 
     default: 
      return -1; 
     } 
    } 

    @Override 
    public int getCount() { 
     return 3; 
    } 

    public Fragment getCurrentFragment(int currentFrag) { 
     return currentFragment[currentFrag]; 
    } 

    public Fragment[] getCurrentFragment() { 
     return currentFragment; 
    } 
} 

E, infine, un frammento (fragmenta, FragmentB e FragmentC) sono in modo quasi identico:

public class FragmentA extends Fragment { 

    public static FragmentA newInstance() { 
     return new FragmentA(); 
    } 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
     rootView = inflater.inflate(R.layout.datafragment, container, false); 

     mTabHost = (TabHost) ((View) container.getParent().getParent()).findViewById(android.R.id.tabhost); 

     if (savedInstanceState != null) { 
      currentTab = savedInstanceState.getInt("currentTab"); 
     } 
     populateLayout(currentTab); 

     return rootView; 
    } 

    @Override 
    public void onSaveInstanceState(Bundle outState) { 
     super.onSaveInstanceState(outState); 
     outState.putInt("currentTab", currentTab); 
    } 
} 

Il problema è che tutta la mia onCreateView vengono sempre chiamati due volte e la seconda volta che vengono chiamati, l'informazione viene persa. Ho il debug e ho visto questo comportamento,

Quando ho ruotare lo schermo, ad esempio in fragmenta e con la terza scheda selezionata, questo accade:

01 ManagerTabs -> onSaveInstanceState 
02 FragmentA -> onSaveInstanceState 
03 ManagerTabs -> onCreateView 
04 ManagerTabs -> onViewCreated 

Nei 04 chiamate (ManagerTabs -> onViewCreated) , mTabHost.setCurrentTab(currentTab); viene chiamato ma all'interno di onTabChanged, ottengo uno java.lang.NullPointerException perché tutti i frammenti all'interno dell'adattatore sono tutti nulli.

Cosa posso fare in modo che se ruoto lo schermo, posso mantenere la scheda selezionata in quel momento?

+0

override di 'onSaveInstanceState' nell'attività e impostare i dati dall'attività –

+0

Ciao. Nessuna fortuna con questo – Favolas

+0

Stai rilasciando tutto collegato all'attività in 'Fragment.onDestroyView()'? – Takhion

risposta

13

Dopo la rotazione dello schermo viene creata una nuova istanza di FragmentPagerAdapter. Ma lo ViewPager ripristina il suo stato e lo stato di tutti i frammenti che contiene. ViewPager non chiama il metodo degli adattatori getView(). Pertanto, Fragment[] currentFragment contiene solo valori null.

È possibile ottenere frammenti correnti in altri modi.

Se si utilizza FragmentPagerAdapter:

FragmentPagerAdapter set tag su frammenti si passa a FragmentManager. Questo ID dipende dall'indice della pagina. Come viene generato questo ID, puoi vedere in source of the latest support lib.

Per ottenere questo basta aggiungere seguente metodo al vostro ManagerTabs frammento:

public Fragment getCurrentPagerFragment(int position) { 
    String fragmentTag = "android:switcher:" + mPager.getId() + ":" + position; 
    return getChildFragmentManager().findFragmentByTag(fragmentTag); 
} 

Se si utilizza FragmentStatePagerAdapter:

Non imposta tag su frammenti. Con FragmentStatePagerAdapter sembra che possiamo cavarcela, usando la sua chiamata instantiateItem(ViewGroup container, int position). Restituisce il riferimento al frammento nella posizione position. Se FragmentStatePagerAdapter detiene già il riferimento al frammento in questione, instantiateItem restituisce semplicemente un riferimento a quel frammento e non chiama getItem() per istanziarlo nuovamente. In questo caso il nostro metodo sarà:

public Fragment getCurrentPagerFragment(int position) { 
    FragmentStatePagerAdapter a = (FragmentStatePagerAdapter) mPager.getAdapter(); 
    return (Fragment) a.instantiateItem(pager, position); 
} 

Infine:

Ora usano sopra metodo per ottenere frammento corrente. Nel tuo caso:

mTabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() { 
    public void onTabChanged(String tag) { 
     currentTab = mTabHost.getCurrentTab(); 
     currentPage = mPager.getCurrentItem(); 
     Fragment currentFragment = getCurrentPagerFragment(currentPage); 
     switch (currentPage) { 
      case 0: 
       ((FragmentA) currentFragment).getData(currentTab); 
       break; 
      case 1: 
       ((FragmentB) currentFragment).getData(currentTab); 
       break; 
      case 2: 
       ((FragmentC) currentFragment).getData(currentTab); 
       break; 
     } 
    } 
}); 
+0

Grazie. Sto avendo 'java.lang.NullPointerException' perché' getCurrentPagerFragment' sta restituendo NULL. Se eseguo il debug posso vedere che 'getFragmentManager()' campo 'mActive' ->' [0] '->' mChildFragmentManager' -> 'mActive' come i frammenti di cui ho bisogno ma non riesco a capire come ottenerli. Puoi fornire ulteriori dettagli sulla stringa 'fragmentTag'? – Favolas

+1

Oh, mi dispiace. C'era un piccolo bug nel mio codice. Dovremmo usare 'getChildFragmentManager()' invece di 'getFragmentManager()'. Ho aggiunto alcune spiegazioni e aggiornato la mia risposta. – erakitin

+0

Ci scusiamo per il feedback tardivo. Grazie molto. Ho una domanda simile ma con un'implementazione diversa [qui] (http://stackoverflow.com/questions/27723968/android-oncreateview-called-twice). Se potessi dare un'occhiata – Favolas

Problemi correlati