2015-06-11 10 views
13

Ho una domanda sulla "programmazione corretta" in Android.Fondamenti di Android Fragments: perché? Questo è concettualmente sbagliato?

Attualmente sto sviluppando un'applicazione utilizzando frammenti. Include i frammenti dinamicamente aggiunti a Attività, i frammenti gonfiati da XML, i frammenti nidificati da XML o aggiunti dinamicamente. Diciamo solo un po 'di tutto.

Il concetto su cui si concentra questa domanda è il processo di comunicazione coinvolto con i frammenti. Quindi, ho letto i documenti e questa non è la prima volta che provo a usare i frammenti.

Il buon senso (e i documenti) dicono che se un frammento vuole parlare o comunicare con la sua attività, dovremmo usare un'interfaccia.

Esempio:

TestFragment

public class TestFragment extends Fragment { 

    private TestFragmentInterface listener; 

    public interface TestFragmentInterface { 

     void actionMethod(); 

    } 


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

     if (getActivity() instanceof TestFragmentInterface) { 
      listener = (TestFragmentInterface) getActivity(); 
     } 

     // sending the event 
     if (listener != null) listener.actionMethod(); 
    } 

} 

TestActivity

public class Test implements TestFragmentInterface { 

    @Override 
    public void actionMethod() { 
    .. 
    } 
} 

Tutto bene qui.

Ciò migliora la riutilizzabilità, poiché il mio TestFragment in questo modo può interagire con qualsiasi tipo di attività, data l'attività implementa l'interfaccia che dichiaro.

Al contrario, l'attività può interagire con il frammento tenendo un riferimento e chiamando i suoi metodi pubblici. Questo è anche il modo suggerito per la comunicazione frammento-frammento, usando l'attività come un ponte.

Questo è bello, ma a volte è come se l'uso di un'interfaccia fosse un po '"troppo".

Domanda A

Nello scenario frammenti ho collegare avere un ruolo abbastanza concentrata, nel senso che sono fatto per quella particolare attività e non sarebbero stati utilizzati in caso contrario, è concettualmente sbagliato ignorare l'implementazione dell'interfaccia e basta fare qualcosa come

((TestActivity) getActivity().myCustomMethod(); 

?

Questo vale anche per lo scenario in cui (non è il mio caso, ma lo considero come "al massimo") la mia attività ha a che fare con un'ampia varietà di questi DIVERSI frammenti, il che significa che dovrebbe implementare un metodo per ogni frammento che dovrebbe gestire. Ciò porta il codice a un gran casino di "linee potenzialmente non necessarie".

Andando avanti: ancora con l'uso di frammenti "mirati", mirati a lavorare solo in un certo modo, che cosa è con l'uso di frammenti annidati?

li aggiunse come

public class TestFragment extends Fragment { 


    private void myTestMethod() { 

    NestedFragment nested = new NestedFragment(); 

    getChildFragmentManager() 
     .beginTransaction() 
     .add(R.id.container, nested) 
     .commit(); 
    } 

} 

questo si lega NestedFragment a TestFragment. Lo ripeto, NestedFragment, proprio come TestFragment, deve essere usato solo in questo modo, non ha alcun significato per funzionare diversamente.

Torna alla domanda, come dovrei comportarmi in questa situazione?

Domanda B

1) dovrei fornire un'interfaccia in NestedFragment, e fare in modo che TestFragments implementa NestedFragmentInterface? In questo caso vorrei agire come segue

NestedFragment

public class NestedFragment extends Fragment { 

    private NestedFragmentInterface listener; 

    public interface NestedFragmentInterface { 

     void actionMethodNested(); 

    } 


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

     if (getParentFragment() instanceof NestedFragmentInterface) { 
      listener = (NestedFragmentInterface) getParentFragment(); 
     } 

     // sending the event 
     if (listener != null) listener.actionMethodNested(); 
    } 

} 

2) dovrebbe (o potrebbe) ignoro l'interfaccia, e basta chiamare

getParentFragment().publicParentMethod(); 

?

3) dovrei creare l'interfaccia in NestedFragment, ma lasciare che il attività lo implementa, in modo che l'attività di chiamerà TestFragment?

Domanda C

Per quanto riguarda l'idea di utilizzare l'attività come un ponte tra i frammenti, credo che sia fatto in modo per la gestione corretta del ciclo di vita di tutti questi oggetti. È ancora possibile fare un frammento diretto al frammento (usando l'interfaccia o chiamare direttamente metodi pubblici) mentre si cerca di gestire manualmente l'eccezione che il sistema potrebbe lanciare?

+0

Vedere se questo aiuta: https://corner.squareup.com/2014/10/advocating-against-android-fragments.html –

+0

Ho letto il collegamento che hai postato, e mentre era davvero un articolo interessante con molte informazioni, temo che non risponda alle mie domande. Grazie comunque – FrancescoC

risposta

4

Proverò a chiarire un po 'tutto.

Prima di tutto, considera l'approccio di listener di impostazione per il frammento.Non è bene impostare il listener nel metodo onViewCreated, perché esegue il leeds al listener di resettazione in eccesso ogni frammento creato. È sufficiente configurarlo nel metodo onAttach.

Ho parlato delle righe di codice. Prendimi nota, è bene che BaseFragment abbia implementato un comportamento comune nella tua app come impostazione di FragmentListener che crea la vista dalla risorsa.

E oltre a ridurre le righe di codice e ottenere parte del riutilizzo del codice, è possibile utilizzare generico in BaseFragment. Così guardare il codice seguente frammento di codice:

public abstract BaseFragment<T extends BaseFragmentListener> extends Fragment { 

    T mListener; 

    public void onAttach(Activity activity) { 
    super.onAttach(activity); 
    if (Activity instanceof T) 
     mListener = (T) activity; 
    } 

    abstract int getLayoutResourceId(); 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
     View layout = inflater.inflate(getLayoutResourceId(), null); 
     // you can use some view injected tools here, mb ButterKnife 
     return layout; 
    } 
} 

risposta A (per la domanda A):

Se avete frammento solo esattamente un'attività è necessario decidere: "Hai davvero bisogno di usare Frammento qui?" . Ma mb è bello avere il frammento esattamente per una attività per estrarre alcune logiche di visualizzazione dall'attività e dalla logica di base di compensazione. Ma per eliminare la logica dell'architettura di base per la tua app usa gli ascoltatori. Ciò renderà la vita più semplice per altri sviluppatori

Risposta B: Per i frammenti nidificati è necessario risolvere, ciò di cui hanno bisogno per utilizzare l'attività esatta o solo i frammenti e utilizzarlo come un ponte per un altro sistema. Se sai che il frammento annidato sarà annidato per tutto il tempo, devi dichiarare il frammento genitore come ascoltatore altrimenti devi usare un altro approccio.

Avviso: Come approccio di base per comunicare tra la parte diff di App è possibile utilizzare gli eventi, provare anche a guardare il bus di eventi, ad esempio. Ti dà un approccio comune per la comunicazione e puoi estrarre la logica di chiamare metodi personalizzati di ascoltatori e altro ancora, tutta la logica sarà situata nella gestione degli eventi e avrai un unico sistema di mediazione per la cooperazione.

Risposta C: In parte spieghi uno degli approcci alla cooperazione tra i frammenti. L'utilizzo di un dispatcher di eventi evita di avere molti ascoltatori per tutte le diverse comunicazioni. Alcune volte è molto redditizio.

O penso che sia più utilizzabile Attività o altre attività di classe in Attività, per un mediatore per la cooperazione di Fragments perché c'è una situazione in cui Frammenti cambiano durante il ciclo di vita sia di gestione che di sistema. E focalizza tutta questa logica in un unico punto e rende il tuo codice più chiaro.

Spero che le mie considerazioni ti siano d'aiuto.

+0

Grazie, questa è stata una grande risposta, dettagliata nella logica e utile, mi ha dato alcune buone idee:) – FrancescoC

10

Ill fare del mio meglio per rispondere alla parete del testo qui :)

Domanda A:

frammenti sono stati progettati per essere moduli riutilizzabili che possono essere la spina e giocato con qualsiasi attività. Per questo motivo, l'unico modo corretto per interfacciarsi con l'attività è di ereditare l'attività da un'interfaccia che il frammento comprende.

public class MapFragment extends Fragment { 

    private MapFragmentInterface listener; 

    public interface MapFragmentInterface { 

     //All methods to interface with an activity 

    } 


    @Override 
    public void onViewCreated(View view, Bundle savedInstanceState) { 
     // sending the event 
     if (listener != null) listener.anyMethodInTheAboveInterface(); 
    } 

} 

Poi hanno l'attività implementare l'interfaccia

public class MainActivity extends Activity implement MapFragmentInterface{ 

//All methods need to be implemented here 
} 

Questo permette al frammento da utilizzare con qualsiasi attività fintanto che l'attività implementa questa interfaccia. Il motivo per cui hai bisogno di questa interfaccia è perché il frammento può essere utilizzato con qualsiasi attività. Chiamare un metodo come

((TestActivity) getActivity().myCustomMethod(); 

si basa sul fatto che il frammento solo può funzionare all'interno di un'attività di test e, pertanto, "rompe" le regole di frammenti.

Domanda B e C:

Supponendo che si stanno seguendo le linee guida corrette per frammenti e che essi sono moduli indipendenti. Quindi non dovresti mai avere una situazione in cui i frammenti abbiano bisogno di conoscersi l'un l'altro. Il 99% delle volte in cui le persone pensano di aver bisogno di frammenti per comunicare direttamente, possono ridimensionare il proprio problema alla situazione che ho descritto utilizzando un patten MVC o qualcosa di simile. Fare in modo che l'attività agisca come il controller e comunichi i frammenti quando è necessario aggiornarli e quindi creare un archivio dati separato.

+0

Quindi, da quello che scrivi, ho capito che quello che sto facendo è possibile ma non suggerito in quanto l'utilizzo è al di fuori dell'ambito originale. Non è qualcosa che è dannoso, ma semplicemente non usato come originariamente previsto. Quanto mi sbaglio? :) – FrancescoC

+0

Il tuo 100% corretto. Come con tutta la programmazione ci sono molti modi per fare le cose. Un sacco di volte lo farò esattamente come hai descritto perché so che il mio frammento sarà usato solo in questa unica attività. Tuttavia, se stai cercando di essere "corretto", dovresti farlo attraverso un'interfaccia per rendere il tuo codice disaccoppiato il più possibile. Se hai tempo e denaro dovresti sempre costruire la tua app in modo corretto :) – nbroeking

+0

Bounty assegnato:) – FrancescoC