2015-06-20 13 views
7

L'app per Android su cui sto lavorando ha un'unica MainActivity e ogni schermata dell'app è implementata come frammento. Ogni frammento viene istanziata simili nel MainActivity come una variabile di classe privato:Aggiornamento di un frammento in risposta all'interazione di Android Navigation Drawer

public class MainActivity extends Activity implements MainStateListener { 

    private FragmentManager fm = getFragmentManager(); 
    private BrowseFragment browseFragment = BrowseFragment.newInstance(); 

... 

V'è un singolo 'telaio frammento' che carica ogni frammento schermo. Quando si passa schermi nell'applicazione questo codice è chiamato a caricare un frammento:

FragmentTransaction ft = fm.beginTransaction(); 
ft.replace(R.id.frag_frame, incoming); 
ft.addToBackStack(null); 
ft.commit(); 
fm.executePendingTransactions(); 

Ogni frammento schermo ha un ascoltatore che consente il frammento di chiamare vari metodi nel MainActivity:

public void onAttach(Activity activity) { 
    super.onAttach(activity); 
    try { 
     mainStateListener = (MainStateListener) activity; 
    } catch (ClassCastException e) { 
     throw new ClassCastException(activity.toString() 
       + " must implement MainStateListener"); 
    } 
} 

L'edizione che sto avendo sta aggiornando un aspetto di un frammento da un cassetto di navigazione che esce nel MainActivity. Il cassetto di navigazione deve aggiornare il frammento, e utilizza questo codice per farlo:

 navigationDrawer.setOnItemClickListener(new AdapterView.OnItemClickListener() { 
      @Override 
      public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { 
       browseFragment.doSomethingOnBrowserFragment(); 
      } 
     }); 

le cose funzionano bene quando fino a quando si modifica l'orientamento. Quindi il frammento della schermata corrente viene caricato correttamente (browseFragment). Ma poi quando si fa clic sul cassetto di navigazione che causa il metodo doSomethingOnBrowserFragment() per l'esecuzione, ottengo un'eccezione di puntatore nullo a causa del fatto che l'oggetto mainStateListener stesso (collegato a browseFragment) è nullo. Da quanto so del ciclo di vita di Fragment questa variabile non dovrebbe essere nulla perché il metodo onAttach() viene eseguito prima di tutto e imposta la variabile mainStateListener. Inoltre, se ho un pulsante su tale browserFragment che utilizza l'oggetto mainStateListener (a seguito di una modifica dell'orientamento), facendo clic sul pulsante non si ha mai questo problema con il puntatore nullo.

traccia stack:

08-04 16:23:28.937 14770-14770/co.openplanit.totago E/AndroidRuntime﹕ FATAL EXCEPTION: main 
    Process: co.openplanit.totago, PID: 14770 
    java.lang.NullPointerException 
      at co.openplanit.totago.MapFragment.enableOfflineMode(MapFragment.java:489) 
      at co.openplanit.totago.MainActivity.setMapMode(MainActivity.java:663) 
      at co.openplanit.totago.MainActivity.itineraryMapDrawerSelectItem(MainActivity.java:610) 
      at co.openplanit.totago.MainActivity.access$200(MainActivity.java:52) 
      at co.openplanit.totago.MainActivity$5.onItemClick(MainActivity.java:420) 
      at android.widget.AdapterView.performItemClick(AdapterView.java:299) 
      at android.widget.AbsListView.performItemClick(AbsListView.java:1158) 
      at android.widget.AbsListView$PerformClick.run(AbsListView.java:2957) 
      at android.widget.AbsListView$3.run(AbsListView.java:3850) 
      at android.os.Handler.handleCallback(Handler.java:733) 
      at android.os.Handler.dispatchMessage(Handler.java:95) 
      at android.os.Looper.loop(Looper.java:136) 
      at android.app.ActivityThread.main(ActivityThread.java:5103) 
      at java.lang.reflect.Method.invokeNative(Native Method) 
      at java.lang.reflect.Method.invoke(Method.java:515) 
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790) 
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:606) 
      at dalvik.system.NativeStart.main(Native Method) 

Mi sembra il problema potrebbe essere che l'utilizzo del cassetto di navigazione è in realtà interagendo con il ciclo di vita browseFragment e inducendolo a staccare o qualcosa del genere.

Qualsiasi suggerimento su come risolvere questo sarebbe molto apprezzato.

+0

Si prega di inviare la traccia di errore cat di registro. – avinash

+0

Ho aggiunto la traccia di errore al post. –

+0

In base alla traccia dello stack, sembra che nulla in onItemClick stia causando NullPointerException. Succede in MapFragment sulla linea 489. Che cosa c'è su quella linea? Probabilmente qualcosa non viene ripristinato in MapFragment sul cambio di orientamento ed è nullo. –

risposta

1

Quello che penso possa accadere è che il browseFragment della tua attività può essere diverso da BrowseFragment che viene mostrato dal gestore dei frammenti (che sembra funzionare bene come hai detto facendo clic su un pulsante in quel frammento).

a rotazione, l'attività creerà una nuova istanza BrowseFragment per la variabile browseFragment (che non è attaccato alla attività) - privato BrowseFragment browseFragment = BrowseFragment.newInstance() eseguito ogni volta che viene creata l'attività, ma il il gestore dei frammenti riutilizzerà l'istanza BrowseFragment ESISTENTE a cui la variabile NON fa riferimento. BrowseFragment riutilizzato verrà collegato ed eseguirà quel codice per aggiornare mainStateListener, il nuovo browseFragment non utilizzato non sarà collegato all'attività a meno che non si esegua un frammentTransaction che lo aggiunge, quindi il mainStateListener in esso sarà null (non inizializzato).

Invece di creare il frammento e archiviarlo in una variabile e quindi tentare di accedere a tale variabile dopo una rotazione, è preferibile utilizzare un tag di frammento e ottenere il frammento in base al tag dal gestore di frammenti.

, ad es.

private static final String BROWSE_TAG = "browseFrag"; 

ft.replace(R.id.frag_frame, browseFragment, BROWSE_TAG); 

navigationDrawer.setOnItemClickListener(new AdapterView.OnItemClickListener() { 
    @Override 
    public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { 
     Fragment browseFragment = fm.findFragmentByTag(BROWSE_TAG); 
     if (browseFragment != null) { 
      browseFragment.doSomethingOnBrowserFragment(); 
     } 
    } 
}); 
+0

Sfortunatamente questo approccio porta allo stesso problema con il puntatore nullo. –

+0

Forse invece di parlare direttamente con l'attività principale, dovresti inviare una trasmissione e mettere un ricevitore broadcast nell'attività principale - quindi non devi preoccuparti di mantenere i riferimenti e aggiornarli attraverso le rotazioni. –

3

Mantenere un riferimento a un frammento potrebbe lasciare fuori sincrono con un riferimento a un vecchio frammento che si stacca nei casi in cui il frammento Responsabile ricrea il frammento per voi.

La soluzione è quello di trovare il frammento attualmente in frag_frame, in pseudo-codice:

Fragment fragment = fm.findFragmentById(R.id.frag_frame); 
if(fragment instanceof BrowseFragment) { 
    // do your stuff 
} 
+1

Credo che la tua spiegazione sia un po 'spenta. I frammenti non vengono ricreati a rotazione: lo stesso frammento esiste ancora e anche tutti i dati (onCreate non viene eseguito di nuovo) - OnCreateView viene eseguito nuovamente, quindi la GUI viene ricreata.Il riferimento viene reinizializzato quando viene ricreata l'attività - _private BrowseFragment browseFragment = BrowseFragment.newInstance(); _ eseguirà ogni rotazione. Detto questo, la tua soluzione suggerita dovrebbe funzionare - perché sta ottenendo il frammento dal gestore dei frammenti invece di provare a utilizzare il browseFragment appena creato. –

+0

@ jt-gilkeson Potrebbe essere che tu abbia ragione riguardo al fatto che i Frammenti non vengano ricreati sul cambio di orientamento. Mantenere un riferimento a un frammento non sopravvivrà a tutti i tipi di situazioni, mi sono ricordato che questo era il caso anche per Rotation ma la mia memoria potrebbe essere sbagliata. – Raanan

2

basta sostituire il codice ascoltatore onclick con questo in mainactivity.

errore è che si verificano a causa di NullPointerException,

causa: puntatore nullo passando sostituire seconda colonna.

Soluzione: avviare classe frammento (nuovo frammento())

case R.id.home: 
    hfragment = new homefragment(); 
    FragmentTransaction hfragmentTransaction= getSupportFragmentManager().beginTransaction(); 
    hfragmentTransaction.replace(R.id.frame, hfragment); 
    hfragmentTransaction.commit(); 
    //do ur task here or in fragment class 
    return true; 


case R.id.notification: 
    return true; 

default: 
    Toast.makeText(getApplicationContext(),"Somethings Wrong",Toast.LENGTH_SHORT).show(); 
    return true; 
2

Ho notato che sei fondamentalmente caching frammenti a causa di codice:

public class MainActivity extends Activity implements MainStateListener { 
    private FragmentManager fm = getFragmentManager(); 
    private BrowseFragment browseFragment = BrowseFragment.newInstance(); 

Ma l'attuazione del presente può essere difficile . O si crea codice/classe che gestisce questi frammenti come l'utilizzo di Array di frammenti o si utilizza classe come FragmentPagerAdapter.

Se posso suggerire, non memorizzare nella cache i frammenti poiché è necessario comprenderne il ciclo di vita, il caching è una buona idea solo se il layout del frammento è complicato. Basta semplicemente crearne una nuova nel codice public void onItemClick() come su suggerimento di Google @Creating a Navigation Drawer, nel caso in cui non l'hai letto. Snippet di codice nella pagina web:

private class DrawerItemClickListener implements ListView.OnItemClickListener { 
    @Override 
    public void onItemClick(AdapterView parent, View view, int position, long id) { 
     selectItem(position); 
    } 
} 

private void selectItem(int position) { 
    // Create a new fragment and specify the planet to show based on position 
    Fragment fragment = new PlanetFragment(); 
    Bundle args = new Bundle(); 
    args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position); 
    fragment.setArguments(args); 

    // Insert the fragment by replacing any existing fragment 
    FragmentManager fragmentManager = getFragmentManager(); 
    fragmentManager.beginTransaction() 
        .replace(R.id.content_frame, fragment) 
        .commit(); 

Nota: Una nuova istanza di frammento è fatto con new PlanetFragment().

+0

Ciò è utile tuttavia sorprendentemente non ha ancora risolto il problema quando ho provato questa soluzione. Ho ottenuto la stessa eccezione del puntatore nullo. –

+0

@AdrianLaurenzi. Mi sembra che il tuo vero problema sia in MapFragment, elencato nel logcat. Se pubblichi un'altra domanda, pubblica il codice correlato in MapFragment. E poi dimmi in questo thread se lo pubblichi. In bocca al lupo.... –

Problemi correlati