Ricerca…


introduzione

Introduzione sui frammenti e il loro meccanismo di intercomunicazione

Sintassi

  • void onActivityCreated (Bundle savedInstanceState) // Chiamato quando l'attività del frammento è stata creata e la gerarchia della vista di questo frammento è stata istanziata.

  • void onActivityResult (int requestCode, int resultCode, Intent data) // Ricevi il risultato da una chiamata precedente a startActivityForResult (Intent, int).

  • void onAttach (Activity activity) // Questo metodo è stato deprecato nel livello API 23. Utilizzare invece onAttach (Context).

  • void onAttach (Contesto contesto) // Chiamato quando un frammento viene prima collegato al suo contesto.

  • void onAttachFragment (Fragment childFragment) // Chiamato quando un frammento è collegato come figlio di questo frammento.

  • void onConfigurationChanged (Configuration newConfig) // Chiamato dal sistema quando la configurazione del dispositivo cambia mentre il componente è in esecuzione.

  • void onCreate (Bundle savedInstanceState) // Chiamato per fare la creazione iniziale di un frammento.

  • Visualizza onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) // Chiamato per fare in modo che il frammento istanzia la sua vista dell'interfaccia utente.

  • void onDestroy () // Chiamato quando il frammento non è più in uso.

  • void onDestroyView () // Chiamato quando la vista precedentemente creata da onCreateView (LayoutInflater, ViewGroup, Bundle) è stata scollegata dal frammento.

  • void onDetach () // Chiamato quando il frammento non è più associato alla sua attività.

  • void onInflate (Activity activity, AttributeSet attrs, Bundle savedInstanceState) // Questo metodo è stato deprecato nel livello API 23. Utilizza invece onInflate (Context, AttributeSet, Bundle).

  • void onInflate (Contesto contesto, AttributeSet attrs, Bundle savedInstanceState) // Chiamato quando un frammento viene creato come parte di un'inflazione del layout della vista, in genere dall'impostazione della visualizzazione del contenuto di un'attività.

  • void onPause () // Chiamato quando il frammento non viene più ripreso.

  • void onResume () // Chiamato quando il frammento è visibile all'utente e in esecuzione attiva.

  • void onSaveInstanceState (Bundle outState) // Chiamato per chiedere al frammento di salvare il suo stato dinamico corrente, in modo che possa essere ricostruito in un secondo momento in modo che il nuovo processo venga riavviato.

  • void onStart () // Chiamato quando Fragment è visibile all'utente.

  • void onStop () // Chiamato quando Fragment non è più avviato.

  • void onViewStateRestored (Bundle savedInstanceState) // Chiamato quando tutti gli stati salvati sono stati ripristinati nella gerarchia della vista del frammento.

Osservazioni

Un frammento rappresenta un comportamento o una parte dell'interfaccia utente in un'attività. È possibile combinare più frammenti in un'unica attività per creare un'interfaccia utente a più riquadri e riutilizzare un frammento in più attività. Puoi pensare a un frammento come a una sezione modulare di un'attività, che ha il suo ciclo di vita, riceve i propri eventi di input e che puoi aggiungere o rimuovere mentre l'attività è in esecuzione (una specie di "attività secondaria" che puoi riutilizzo in diverse attività).

Costruttore

Ogni frammento deve avere un costruttore vuoto , quindi può essere istanziato quando si ripristina lo stato della propria attività. Si raccomanda vivamente che le sottoclassi non abbiano altri costruttori con parametri, poiché questi costruttori non saranno chiamati quando il frammento viene riattivato; invece, gli argomenti possono essere forniti dal chiamante con setArguments (Bundle) e successivamente recuperati da Fragment con getArguments ().

Il modello newInstance ()

Sebbene sia possibile creare un costruttore di frammenti con parametri, Android richiama internamente il costruttore di argomenti zero durante la ricreazione di frammenti (ad esempio, se vengono ripristinati dopo essere stati uccisi per motivi personali di Android). Per questo motivo, non è consigliabile fare affidamento su un costruttore che ha parametri.

Per garantire che gli argomenti dei frammenti previsti siano sempre presenti, è possibile utilizzare un metodo newInstance() statico per creare il frammento e inserire i parametri desiderati in un pacchetto che sarà disponibile quando si crea una nuova istanza.

import android.os.Bundle;
import android.support.v4.app.Fragment;

public class MyFragment extends Fragment
{
  // Our identifier for obtaining the name from arguments
  private static final String NAME_ARG = "name";

  private String mName;

  // Required
  public MyFragment(){}

  // The static constructor.  This is the only way that you should instantiate
  // the fragment yourself
  public static MyFragment newInstance(final String name) {
    final MyFragment myFragment = new MyFragment();
    // The 1 below is an optimization, being the number of arguments that will
    // be added to this bundle.  If you know the number of arguments you will add
    // to the bundle it stops additional allocations of the backing map.  If
    // unsure, you can construct Bundle without any arguments
    final Bundle args = new Bundle(1);

    // This stores the argument as an argument in the bundle.  Note that even if
    // the 'name' parameter is NULL then this will work, so you should consider
    // at this point if the parameter is mandatory and if so check for NULL and
    // throw an appropriate error if so
    args.putString(NAME_ARG, name);

    myFragment.setArguments(args);
    return myFragment;
  }

  @Override
  public void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    final Bundle arguments = getArguments();
    if (arguments == null || !arguments.containsKey(NAME_ARG)) {
      // Set a default or error as you see fit
    } else {
      mName = arguments.getString(NAME_ARG);
    }
  }
}

Ora, nell'attività:

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
MyFragment mFragment = MyFragment.newInstance("my name");
ft.replace(R.id.placeholder, mFragment);
//R.id.placeholder is where we want to load our fragment
ft.commit();

Questo modello è una best practice per garantire che tutti gli argomenti necessari vengano passati ai frammenti della creazione. Si noti che quando il sistema distrugge il frammento e lo ricrea in seguito, ripristinerà automaticamente il suo stato, ma è necessario fornirlo con onSaveInstanceState(Bundle) .

Prima di tutto, dobbiamo aggiungere il nostro primo Fragment all'inizio, dovremmo farlo nel metodo onCreate() della nostra attività:

if (null == savedInstanceState) {
    getSupportFragmentManager().beginTransaction()
      .addToBackStack("fragmentA")
      .replace(R.id.container, FragmentA.newInstance(), "fragmentA")
      .commit();
}

Successivamente, dobbiamo gestire il nostro backstack. Il modo più semplice è utilizzare una funzione aggiunta nella nostra attività che viene utilizzata per tutte le FragmentTransactions.

public void replaceFragment(Fragment fragment, String tag) {
    //Get current fragment placed in container
    Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.container);

    //Prevent adding same fragment on top
    if (currentFragment.getClass() == fragment.getClass()) {
        return;
    }

    //If fragment is already on stack, we can pop back stack to prevent stack infinite growth
    if (getSupportFragmentManager().findFragmentByTag(tag) != null) {
        getSupportFragmentManager().popBackStack(tag, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    }

    //Otherwise, just replace fragment
    getSupportFragmentManager()
            .beginTransaction()
            .addToBackStack(tag)
            .replace(R.id.container, fragment, tag)
            .commit();
}

Infine, dovremmo sovrascrivere onBackPressed() per uscire dall'applicazione quando torno dall'ultimo frammento disponibile nel backstack.

@Override
public void onBackPressed() {
    int fragmentsInStack = getSupportFragmentManager().getBackStackEntryCount();
    if (fragmentsInStack > 1) { // If we have more than one fragment, pop back stack
        getSupportFragmentManager().popBackStack();
    } else if (fragmentsInStack == 1) { // Finish activity, if only one fragment left, to prevent leaving empty screen
        finish();
    } else {
        super.onBackPressed();
    }
}

Esecuzione in attività:

replaceFragment(FragmentB.newInstance(), "fragmentB");

Esecuzione all'esterno dell'attività (presupponendo che MainActivity sia la nostra attività):

((MainActivity) getActivity()).replaceFragment(FragmentB.newInstance(), "fragmentB");

Passa i dati da Attività a frammento usando Bundle

Tutti i frammenti dovrebbero avere un costruttore vuoto (cioè un metodo di costruzione che non ha argomenti di input). Pertanto, per passare i tuoi dati al Frammento in fase di creazione, devi utilizzare il metodo setArguments() . Questo metodo ottiene un pacchetto in cui vengono archiviati i dati e archivia il pacchetto negli argomenti. Successivamente, questo pacchetto può quindi essere richiamato nelle onCreate() e onCreateView() di Fragment.

Attività:

 Bundle bundle = new Bundle();
 String myMessage = "Stack Overflow is cool!";
 bundle.putString("message", myMessage );
 FragmentClass fragInfo = new FragmentClass();
 fragInfo.setArguments(bundle);
 transaction.replace(R.id.fragment_single, fragInfo);
 transaction.commit();

Frammento:

 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    String myValue = this.getArguments().getString("message");
    ...
 }

Invio di eventi a un'attività con interfaccia di callback

Se è necessario inviare eventi dal frammento all'attività, una delle possibili soluzioni è definire l'interfaccia di callback e richiedere che l'attività dell'host lo implementa.

Esempio

Invia callback a un'attività, quando si fa clic sul pulsante di frammento

Prima di tutto, definisci l'interfaccia di callback:
public interface SampleCallback {
    void onButtonClicked();
}

Il prossimo passo è assegnare questo callback in frammento:

public final class SampleFragment extends Fragment {

    private SampleCallback callback;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof SampleCallback) {
            callback = (SampleCallback) context;
        } else {
            throw new RuntimeException(context.toString()
                    + " must implement SampleCallback");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        callback = null;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.sample, container, false);
        // Add button's click listener
        view.findViewById(R.id.actionButton).setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                callback.onButtonClicked(); // Invoke callback here
            }
        });
        return view;
    }
}

Infine, implementa la funzione di callback nell'attività:

public final class SampleActivity extends Activity implements SampleCallback {

    // ... Skipped code with settings content view and presenting the fragment

    @Override
    public void onButtonClicked() {
        // Invoked when fragment's button has been clicked
    }
}

Animare la transizione tra i frammenti

Per animare la transizione tra i frammenti, o per animare il processo di mostrare o nascondere un frammento, si usa FragmentManager per creare un FragmentTransaction .

Per una singola FragmentTransaction , ci sono due modi diversi per eseguire le animazioni: puoi usare un'animazione standard o puoi fornire le tue animazioni personalizzate.

Le animazioni standard vengono specificate chiamando FragmentTransaction.setTransition(int transit) e utilizzando una delle costanti predefinite disponibili nella classe FragmentTransaction . Al momento della scrittura, queste costanti sono:

FragmentTransaction.TRANSIT_NONE
FragmentTransaction.TRANSIT_FRAGMENT_OPEN
FragmentTransaction.TRANSIT_FRAGMENT_CLOSE
FragmentTransaction.TRANSIT_FRAGMENT_FADE

La transazione completa potrebbe essere simile a questa:

getSupportFragmentManager()
    .beginTransaction()
    .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
    .replace(R.id.contents, new MyFragment(), "MyFragmentTag")
    .commit();

Le animazioni personalizzate vengono specificate chiamando FragmentTransaction.setCustomAnimations(int enter, int exit) o FragmentTransaction.setCustomAnimations(int enter, int exit, int popEnter, int popExit) .

Le animazioni di enter e exit verranno riprodotte per FragmentTransaction s che non implicano frammenti fuori dallo stack posteriore. Le animazioni popEnter e popExit verranno riprodotte quando si inserisce un frammento fuori dallo stack posteriore.

Il codice seguente mostra come sostituire un frammento facendo scorrere un frammento e facendo scorrere l'altro al suo posto.

getSupportFragmentManager()
    .beginTransaction()
    .setCustomAnimations(R.anim.slide_in_left, R.anim.slide_out_right)
    .replace(R.id.contents, new MyFragment(), "MyFragmentTag")
    .commit();

Le definizioni dell'animazione XML utilizzano il tag objectAnimator . Un esempio di slide_in_left.xml potrebbe essere qualcosa del genere:

<?xml version="1.0" encoding="utf-8"?>
<set>
  <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:propertyName="x" 
    android:valueType="floatType"
    android:valueFrom="-1280"
    android:valueTo="0" 
    android:duration="500"/>
</set>

Comunicazione tra frammenti

Tutte le comunicazioni tra i frammenti devono passare per un'attività. I frammenti NON possono comunicare tra loro senza un'attività.

Risorse addizionali

In questo esempio, abbiamo un MainActivity che ospita due frammenti, SenderFragment e ReceiverFragment , per l'invio e la ricezione di un message (una stringa semplice in questo caso) rispettivamente.

Un pulsante in SenderFragment avvia il processo di invio del messaggio. Una TextView in ReceiverFragment viene aggiornata quando il messaggio viene ricevuto da esso.

Di seguito è riportato lo snippet per MainActivity con i commenti che spiegano le importanti righe di codice:

// Our MainActivity implements the interface defined by the SenderFragment. This enables
// communication from the fragment to the activity
public class MainActivity extends AppCompatActivity implements SenderFragment.SendMessageListener {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

/**
 * This method is called when we click on the button in the SenderFragment
 * @param message The message sent by the SenderFragment
 */
@Override
public void onSendMessage(String message) {
    // Find our ReceiverFragment using the SupportFragmentManager and the fragment's id
    ReceiverFragment receiverFragment = (ReceiverFragment)
            getSupportFragmentManager().findFragmentById(R.id.fragment_receiver);

    // Make sure that such a fragment exists
    if (receiverFragment != null) {
        // Send this message to the ReceiverFragment by calling its public method
        receiverFragment.showMessage(message);
    }
}
}

Il file di layout per MainActivity ospita due frammenti all'interno di un LinearLayout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.naru.fragmentcommunication.MainActivity">

<fragment
    android:id="@+id/fragment_sender"
    android:name="com.naru.fragmentcommunication.SenderFragment"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    tools:layout="@layout/fragment_sender" />

<fragment
    android:id="@+id/fragment_receiver"
    android:name="com.naru.fragmentcommunication.ReceiverFragment"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    tools:layout="@layout/fragment_receiver" />
</LinearLayout>

SenderFragment espone un'interfaccia SendMessageListener che aiuta il MainActivity sapere quando è stato fatto clic su Button in SenderFragment .

Di seguito è riportato lo snippet di codice per SenderFragment spiega le importanti righe di codice:

public class SenderFragment extends Fragment {

private SendMessageListener commander;

/**
 * This interface is created to communicate between the activity and the fragment. Any activity
 * which implements this interface will be able to receive the message that is sent by this
 * fragment.
 */
public interface SendMessageListener {
    void onSendMessage(String message);
}

/**
 * API LEVEL >= 23
 * <p>
 * This method is called when the fragment is attached to the activity. This method here will
 * help us to initialize our reference variable, 'commander' , for our interface
 * 'SendMessageListener'
 *
 * @param context
 */
@Override
public void onAttach(Context context) {
    super.onAttach(context);
    // Try to cast the context to our interface SendMessageListener i.e. check whether the
    // activity implements the SendMessageListener. If not a ClassCastException is thrown.
    try {
        commander = (SendMessageListener) context;
    } catch (ClassCastException e) {
        throw new ClassCastException(context.toString()
                + "must implement the SendMessageListener interface");
    }
}

/**
 * API LEVEL < 23
 * <p>
 * This method is called when the fragment is attached to the activity. This method here will
 * help us to initialize our reference variable, 'commander' , for our interface
 * 'SendMessageListener'
 *
 * @param activity
 */
@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    // Try to cast the context to our interface SendMessageListener i.e. check whether the
    // activity implements the SendMessageListener. If not a ClassCastException is thrown.
    try {
        commander = (SendMessageListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString()
                + "must implement the SendMessageListener interface");
    }
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                         @Nullable Bundle savedInstanceState) {
    // Inflate view for the sender fragment.
    View view = inflater.inflate(R.layout.fragment_receiver, container, false);

    // Initialize button and a click listener on it
    Button send = (Button) view.findViewById(R.id.bSend);
    send.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            // Sanity check whether we were able to properly initialize our interface reference
            if (commander != null) {

                // Call our interface method. This enables us to call the implemented method
                // in the activity, from where we can send the message to the ReceiverFragment.
                commander.onSendMessage("HELLO FROM SENDER FRAGMENT!");
            }
        }
    });

    return view;
}
}

Il file di layout per SenderFragment :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

<Button
    android:id="@+id/bSend"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="SEND"
    android:layout_gravity="center_horizontal" />
</LinearLayout>

ReceiverFragment è semplice ed espone un semplice metodo pubblico per l'aggiornamento del TextView. Quando MainActivity riceve il messaggio da SenderFragment , chiama questo metodo pubblico di ReceiverFragment

Di seguito è riportato lo snippet di codice per ReceiverFragment con i commenti che spiegano le linee importanti di codice:

public class ReceiverFragment extends Fragment {
TextView tvMessage;

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                         @Nullable Bundle savedInstanceState) {
    // Inflate view for the sender fragment.
    View view = inflater.inflate(R.layout.fragment_receiver, container, false);

    // Initialize the TextView
    tvMessage = (TextView) view.findViewById(R.id.tvReceivedMessage);

    return view;
}


/**
 * Method that is called by the MainActivity when it receives a message from the SenderFragment.
 * This method helps update the text in the TextView to the message sent by the SenderFragment.
 * @param message Message sent by the SenderFragment via the MainActivity.
 */
public void showMessage(String message) {
    tvMessage.setText(message);
}
}

Il file di layout per ReceiverFragment :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">
<TextView
    android:id="@+id/tvReceivedMessage"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Waiting for message!" />
</LinearLayout>


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow