29

Nell'applicazione che sto sviluppando sto usando un ViewPager con frammenti e ogni frammento costruisce il proprio menu indipendentemente da tutti gli altri frammenti nel ViewPager.Elementi di azione dal frammento iniziale di Viewpager non visualizzati

Il problema è che a volte i frammenti che sono stati inizializzati dal ViewPager per impostazione predefinita (vale a dire nel suo stato iniziale) non hanno i loro elementi popolati nel menu delle azioni. Quel che è peggio è che questo problema si verifica solo a intermittenza. Se faccio scorrere il ViewPager abbastanza in modo che i frammenti siano forzati a inizializzarli di nuovo, quando si scorre indietro, il menu viene popolato correttamente.

Codice attività:

package net.solarnz.apps.fragmentsample; 

import android.app.Activity; 
import android.app.Fragment; 
import android.app.FragmentManager; 
import android.os.Bundle; 
import android.support.v13.app.FragmentStatePagerAdapter; 
import android.support.v4.view.ViewPager; 

public class FragmentSampleActivity extends Activity { 
    private ViewPagerAdapter mViewPagerAdapter; 
    private ViewPager mViewPager; 

    /** Called when the activity is first created. */ 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main); 

     if (mViewPagerAdapter == null) { 
      mViewPagerAdapter = new ViewPagerAdapter(getFragmentManager()); 
     } 

     mViewPager = (ViewPager) findViewById(R.id.log_pager); 
     mViewPager.setAdapter(mViewPagerAdapter); 
     mViewPager.setCurrentItem(0); 
    } 


    private class ViewPagerAdapter extends FragmentStatePagerAdapter { 
     public ViewPagerAdapter(FragmentManager fm) { 
      super(fm); 
     } 

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

     @Override 
     public Fragment getItem(int position) { 
      Fragment f = Fragment1.newInstance(position); 
      // f.setRetainInstance(true); 
      f.setHasOptionsMenu(true); 
      return f; 
     } 
    } 
} 

frammento di codice:

package net.solarnz.apps.fragmentsample; 

import android.app.Fragment; 
import android.os.Bundle; 
import android.view.Menu; 
import android.view.MenuInflater; 

public class Fragment1 extends Fragment { 
    int mNum; 

    static Fragment newInstance(int num) { 
     Fragment1 f = new Fragment1(); 

     // Supply num input as an argument. 
     Bundle args = new Bundle(); 
     args.putInt("num", num); 
     f.setArguments(args); 

     return f; 
    } 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setHasOptionsMenu(true); 
     mNum = getArguments() != null ? getArguments().getInt("num") : 0; 
    } 

    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 
     inflater.inflate(R.menu.menu_list, menu); 
    } 

} 

Disposizione:

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 

    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" > 

      <android.support.v4.view.ViewPager 
       android:id="@+id/log_pager" 
       android:layout_width="match_parent" 
       android:layout_height="match_parent" /> 
</LinearLayout> 

Menu:

<?xml version="1.0" encoding="utf-8"?> 
<menu xmlns:android="http://schemas.android.com/apk/res/android"> 
    <item android:id="@+id/menu_refresh" 
      android:title="Refresh" 
      android:icon="@android:drawable/ic_delete" 
      android:showAsAction="ifRoom|withText" /> 
</menu> 

menu Azione essere popolato: http://i.stack.imgur.com/QFMDd.png

menu Azione non essere popolata: http://i.stack.imgur.com/sH5Pp.png

+0

D'accordo con Solarnz sembra che non ci sia una soluzione ovvia. Ho creato un'applicazione [dimostratore] (https://github.com/snodnipper/page-flipper-race-condition/) che consente di riprodurre il problema in modo affidabile. Lo stesso problema sembra essere [riportato in ActionBarSherlock] (https://github.com/JakeWharton/ActionBarSherlock/issues/351) e ho sollevato questo [problema] (http://code.google.com/p/android/issues/detail? id = 29472) nel tracker dei problemi di Android. – snodnipper

risposta

21

Si consiglia di leggere questo (da xcolw ...)

Attraverso la sperimentazione sembra che la causa principale è invalidateOptionsMenu sempre chiamato più di uno senza una pausa sul thread principale per elaborare i processi in coda. Un'ipotesi: questo sarebbe importante se qualche parte critica della creazione del menu venisse posticipata tramite un post, lasciando la barra delle azioni in uno stato negativo finché non viene eseguita.

ci sono alcuni punti Questo può accadere che non sono evidenti:

  1. chiamata viewPager.setCurrentItem più volte per lo stesso articolo

  2. chiamando viewPager.setCurrentItem nel onCreate dell'attività. setCurrentItem provoca un menù invalidate opzione, che è immediatamente seguito da menu delle opzioni del all'attività invalida

Soluzioni alternative che ho trovato per ogni

  1. Guardia la chiamata a viewPager.setCurrentItem

    if (viewPager.getCurrentItem() != position) 
        viewPager.setCurrentItem(position); 
    
  2. rinviare la chiamata a viewPager.setCurrentItem in onCreate

    public void onCreate(...) { 
        ... 
        view.post(new Runnable() { 
         public void run() { 
          // guarded viewPager.setCurrentItem 
         } 
        } 
    } 
    

Dopo questi menu cambia opzioni all'interno del cercapersone vista sembra funzionare come previsto. Spero che qualcuno possa gettare più luce in questo.

fontehttp://code.google.com/p/android/issues/detail?id=29472

+0

Eccellente. Grazie. –

+0

Pls, fornire snipet completo su come utilizzare questo con viewpager senza schede, non chiaro per me. Grazie. –

+0

Non ha funzionato per me. mViewPager.post (new Runnable() { @Override public void run() { mViewPager.setAdapter (nuova FragPagerAdapter (getSupportFragmentManager(), MainActivity.this));! se (mViewPager.getCurrentItem() = 1) { mViewPager.setCurrentItem (1); } } }); –

8

La risposta più semplice è di non utilizzare i menu all'interno frammenti nel ViewPager. Se è necessario utilizzare i menu all'interno dei frammenti, ciò che suggerisco è il caricamento del menu attraverso il metodo onCreateOptionsMenu nell'attività padre. Ovviamente dovrai essere in grado di determinare quale menu mostrare.

Sono riuscito a ottenere ciò utilizzando la riflessione di classe. Dovrai inoltre utilizzare il metodo invalidateOptionsMenu ogni volta che cambi pagina. Avrai bisogno di un OnPageChangeListener per chiamare questo quando il ViewPager cambia pagina.

+0

Puoi condividere il codice per favore? –

0

Innanzitutto creare un metodo nella classe sub di FragmentPagerAdapter per ottenere il frammento corrente

public SherlockFragment getFragment() { 
    return currentFragment; 
} 

     @Override 
public void onTabSelected(final Tab tab, FragmentTransaction ft) { 

    ((SherlockFragmentActivity) mContext).invalidateOptionsMenu(); 
    Fragment f = ((SherlockFragmentActivity) mContext) 
      .getSupportFragmentManager().findFragmentByTag(
        makeFragmentName(tab.getPosition())); 

    currentFragment=(SherlockFragment) f; 
} 

Ora sovrascrivono sotto i metodi in Main actvity

@Override 
public boolean onCreateOptionsMenu(Menu menu) { 
    if (mTabsAdapter.getPositionOfTabSelected() != 0) { 
     menu.add("Read").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 
     menu.add("Write").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 
     menu.add("Clear").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 
     menu.add("Factory data reset").setShowAsAction(
       MenuItem.SHOW_AS_ACTION_IF_ROOM); 
    } 

    return super.onCreateOptionsMenu(menu); 
} 

Richiamare ora onOptionItemSelected from activity to framment

@Override 
public boolean onOptionsItemSelected(MenuItem item) { 
    mTabsAdapter.getFragment().onOptionsItemSelected(item); 
    return super.onOptionsItemSelected(item); 
} 
3

Ho anche avuto lo stesso problema. Nel mio caso ho un'attività con viewpager che contiene due frammenti, ogni frammento gonfia il proprio menu di azioni ma il menu delle azioni frammento non viene mostrato.

Visualizza codice adattatore cercapersone

public class ScreensAdapter extends FragmentPagerAdapter { 

    public TrackerScreensAdapter(Context context, FragmentManager fm) { 
     super(fm); 
    } 

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

    public Fragment getItem(int position) { 
     Fragment fragment = null; 
     switch (position){ 
      case 0: 
       fragment = new Fragment1(); 
       break; 
      case 1: 
       fragment = new Fragment2(); 
       break; 
     } 
     return fragment; 
    } 

} 

attività su Crea

screensAdapter = new ScreensAdapter(this, getFragmentManager()); 
    viewPager.setAdapter(screensAdapter); 

In questo modo il mio viewPager ha due frammenti, ogni frammento sparano il proprio compito in onActivityCreated, ottenere dati e disegnare il layout basata su dati ottenuti. Inoltre ogni frammento ha onCreateOptionsMenu

@Override 
public void onActivityCreated(Bundle savedInstanceState) { 
    super.onActivityCreated(savedInstanceState); 
    setHasOptionsMenu(true); 
    MyTask task = new MyTask(); 
    task.setTaskListener(this); 
    task.execute(); 


} 

public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 
    inflater.inflate(R.menu.fragment_menu, menu); 
} 

trascorso molte volte per risolvere questo problema e capire non menù per questo frammento di spettacoli. Tutto ciò che avevo bisogno è

screenAdapter = new ScreenAdapter(this, getFragmentManager()); 
    viewPager.post(new Runnable() { 
     public void run() { 
      viewPager.setAdapter(screenAdapter); 
     } 
    }); 
+0

Grazie! impostazione dell'adattatore all'interno della eseguibile risolto per me –

0

ho risolto un problema molto simile in cui l'azione della barra icone assegnati dal frammento all'interno di un ViewPager stavano scomparendo onPause(). Riapparvero quando il frammento tornava in vista e l'utente si spostava a destra oa sinistra, ma non immediatamente. La soluzione stava chiamando notifyDataSetChanged() sul PagerAdapter nel metodo onResume() del frammento.

@Override 
public void onResume() { 
    mPagerAdapter.notifyDataSetChanged(); 
} 
0

ho avuto questo problema con gli elementi di azione bar mentre stavo usando HorizontalScrollView per mostrare le schede, ma ho cambiato per PagerTitleStrip e il problema è stato risolto.

Forse questa informazione può aiutare qualcun altro.

0

Nel mio caso ho rintracciato la causa principale del problema a quello che ritengo sia un bug in FragmentStatePagerAdapter che sta chiamando Fragment#setMenuVisibility in falso e non riuscendo a impostarlo correttamente su true quando ripristina il suo stato.

Soluzioni alternative:

  1. Uso FragmentPagerAdapter invece di FragmentStatePagerAdapter

  2. Se è necessario utilizzare FragmentStatePagerAdapter, l'adattatore sottoclasse di override setPrimaryItem in questo modo:

    @Override 
    public void setPrimaryItem(ViewGroup container, int position, Object object) { 
        super.setPrimaryItem(container, position, object); 
    
        //This is a workaround for a bug in FragmentStatePagerAdapter 
        Fragment currentItem = getItem(position); 
        if (currentItem != null) { 
         currentItem.setMenuVisibility(true); 
         currentItem.setUserVisibleHint(true); 
        } 
    } 
    
+0

Nessuno di questi approcci funziona. – Proverbio

Problemi correlati