Zoeken…


Invoering

Introductie over Fragmenten en hun communicatiemechanisme

Syntaxis

  • void onActivityCreated (Bundle saveInstanceState) // Wordt aangeroepen wanneer de activiteit van het fragment is gemaakt en de weergavehiërarchie van dit fragment is gestart.

  • void onActivityResult (int requestCode, int resultCode, Intent data) // Ontvang het resultaat van een eerdere aanroep van startActivityForResult (Intent, int).

  • void onAttach (Activiteitsactiviteit) // Deze methode is verouderd op API-niveau 23. Gebruik in plaats daarvan onAttach (Context).

  • void onAttach (Context context) // Wordt aangeroepen wanneer een fragment voor het eerst aan zijn context wordt gekoppeld.

  • void onAttachFragment (Fragment childFragment) // Wordt opgeroepen wanneer een fragment is gekoppeld als een kind van dit fragment.

  • void onConfigurationChanged (Configuration newConfig) // Aangeroepen door het systeem wanneer de apparaatconfiguratie verandert terwijl uw component actief is.

  • void onCreate (Bundle saveInstanceState) // Opgeroepen om de eerste keer een fragment te maken.

  • View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle saveInstanceState) // Opgeroepen om het fragment de weergave van de gebruikersinterface te laten instantiëren.

  • void onDestroy () // Wordt aangeroepen wanneer het fragment niet meer wordt gebruikt.

  • void onDestroyView () // Wordt opgeroepen wanneer de weergave die eerder is gemaakt door onCreateView (LayoutInflater, ViewGroup, Bundle) is losgekoppeld van het fragment.

  • void onDetach () // Wordt aangeroepen wanneer het fragment niet langer aan zijn activiteit is gekoppeld.

  • void onInflate (Activiteitsactiviteit, AttributeSet attrs, Bundle saveInstanceState) // Deze methode is verouderd op API-niveau 23. Gebruik in plaats daarvan onInflate (Context, AttributeSet, Bundle).

  • void onInflate (context, AttributeSet attrs, Bundle saveInstanceState) // Wordt opgeroepen wanneer een fragment wordt gemaakt als onderdeel van een inflatie van de weergave-indeling, meestal door het instellen van de inhoudsweergave van een activiteit.

  • void onPause () // Wordt aangeroepen wanneer het fragment niet meer wordt hervat.

  • void onResume () // Wordt aangeroepen wanneer het fragment zichtbaar is voor de gebruiker en actief wordt uitgevoerd.

  • void onSaveInstanceState (Bundle outState) // Geroepen om het fragment te vragen de huidige dynamische status op te slaan, zodat het later in een nieuw exemplaar van het proces kan worden gereconstrueerd, wordt opnieuw gestart.

  • void onStart () // Wordt aangeroepen wanneer het fragment zichtbaar is voor de gebruiker.

  • void onStop () // Wordt aangeroepen wanneer het fragment niet meer wordt gestart.

  • void onViewStateRestored (Bundle saveInstanceState) // Wordt opgeroepen wanneer alle opgeslagen status is hersteld in de weergavehiërarchie van het fragment.

Opmerkingen

Een fragment vertegenwoordigt een gedrag of een deel van de gebruikersinterface in een activiteit. U kunt meerdere fragmenten in één activiteit combineren om een gebruikersinterface met meerdere vensters te bouwen en een fragment opnieuw te gebruiken in meerdere activiteiten. Je kunt een fragment beschouwen als een modulair gedeelte van een activiteit, die zijn eigen levenscyclus heeft, zijn eigen invoergebeurtenissen ontvangt en die je kunt toevoegen of verwijderen terwijl de activiteit wordt uitgevoerd (een soort 'subactiviteit' die je kunt hergebruik in verschillende activiteiten).

bouwer

Elk fragment moet een lege constructor hebben , dus het kan worden geïnstantieerd bij het herstellen van de status van zijn activiteit. Het wordt sterk aanbevolen dat subklassen geen andere constructors met parameters hebben, omdat deze constructors niet worden aangeroepen wanneer het fragment opnieuw wordt geïnstantieerd; in plaats daarvan kunnen argumenten door de beller worden geleverd met setArguments (bundel) en later worden opgehaald door het fragment met getArguments ().

Het patroon newInstance ()

Hoewel het mogelijk is om een fragmentconstructor met parameters te maken, roept Android intern de nul-argumentconstructor aan bij het opnieuw maken van fragmenten (bijvoorbeeld als deze worden hersteld nadat ze om Android-redenen zijn gedood). Om deze reden is het niet raadzaam om te vertrouwen op een constructor die parameters heeft.

Om ervoor te zorgen dat uw verwachte fragmentargumenten altijd aanwezig zijn, kunt u een statische methode newInstance() gebruiken om het fragment te maken en de gewenste parameters in een bundel opnemen die beschikbaar is wanneer u een nieuw exemplaar maakt.

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);
    }
  }
}

Nu, in de activiteit:

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();

Dit patroon is een best practice om ervoor te zorgen dat alle benodigde argumenten worden doorgegeven aan fragmenten bij het maken. Merk op dat wanneer het systeem het fragment vernietigt en het later opnieuw maakt, het automatisch de status zal herstellen - maar u moet het voorzien van een onSaveInstanceState(Bundle) -implementatie.

Allereerst moeten we ons eerste Fragment aan het begin toevoegen, we moeten het doen in de methode onCreate() van onze activiteit:

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

Vervolgens moeten we onze backstack beheren. De eenvoudigste manier is om een functie toe te voegen die is toegevoegd aan onze activiteit en die wordt gebruikt voor alle 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();
}

Ten slotte moeten we onBackPressed() om de toepassing af te sluiten wanneer we teruggaan van het laatste beschikbare fragment in de 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();
    }
}

Uitvoering in activiteit:

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

Uitvoering buiten activiteit (ervan uitgaande dat MainActivity onze activiteit is):

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

Gegevens doorgeven van activiteit naar fragment met behulp van bundel

Alle fragmenten moeten een lege constructor hebben (dwz een constructormethode zonder invoerargumenten). Daarom moet u de methode setArguments() gebruiken om uw gegevens door te geven aan het fragment dat wordt gemaakt. Deze methode krijgt een bundel waarin u uw gegevens opslaat en slaat de bundel op in de argumenten. Vervolgens kan deze bundel worden opgehaald in onCreate() en onCreateView() callbacks van het fragment.

Werkzaamheid:

 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();

Fragment:

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

Gebeurtenissen terugsturen naar een activiteit met terugbelinterface

Als u gebeurtenissen van fragment naar activiteit moet verzenden, is een van de mogelijke oplossingen om de terugbelinterface te definiëren en te vereisen dat de hostactiviteit deze implementeert.

Voorbeeld

Stuur een terugbelopdracht naar een activiteit wanneer op de knop van het fragment werd geklikt

Definieer eerst de terugbelinterface:
public interface SampleCallback {
    void onButtonClicked();
}

De volgende stap is om deze callback in fragment toe te wijzen:

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;
    }
}

En ten slotte, implementeer callback in activiteit:

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
    }
}

Animeer de overgang tussen fragmenten

Om de overgang tussen fragmenten te animeren, of om het proces van het tonen of verbergen van een fragment te animeren, gebruikt u FragmentManager om een FragmentTransaction te maken.

Voor een enkele FragmentTransaction zijn er twee verschillende manieren om animaties uit te voeren: u kunt een standaardanimatie gebruiken of u kunt uw eigen aangepaste animaties leveren.

Standaardanimaties worden opgegeven door FragmentTransaction.setTransition(int transit) aan te roepen en een van de vooraf gedefinieerde constanten te gebruiken die beschikbaar zijn in de klasse FragmentTransaction . Op het moment van schrijven zijn deze constanten:

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

De volledige transactie kan er ongeveer zo uitzien:

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

Aangepaste animaties worden opgegeven door FragmentTransaction.setCustomAnimations(int enter, int exit) of FragmentTransaction.setCustomAnimations(int enter, int exit, int popEnter, int popExit) .

De enter en exit animaties worden gespeeld voor FragmentTransaction s waarbij geen fragmenten van de backstack worden verwijderd. De animaties popEnter en popExit worden afgespeeld wanneer een fragment van de achterste stapel wordt verwijderd.

De volgende code laat zien hoe u een fragment zou vervangen door het ene fragment eruit te schuiven en het andere op zijn plaats te schuiven.

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

De XML-animatiedefinities gebruiken de tag objectAnimator . Een voorbeeld van slide_in_left.xml kan er ongeveer zo uitzien:

<?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>

Communicatie tussen fragmenten

Alle communicatie tussen fragmenten moet via een activiteit verlopen. Fragmenten KUNNEN NIET met elkaar communiceren zonder een activiteit.

Aanvullende bronnen

In dit voorbeeld hebben we een MainActivity die twee fragmenten host, SenderFragment en ReceiverFragment , voor het verzenden en ontvangen van respectievelijk een message (in dit geval een eenvoudige string).

Een knop in SenderFragment start het verzendproces van het bericht. Een TextView in het ReceiverFragment wordt bijgewerkt wanneer het bericht door het wordt ontvangen.

Hierna volgt het fragment voor de MainActivity met opmerkingen waarin de belangrijke coderegels worden uitgelegd:

// 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);
    }
}
}

Het lay- MainActivity voor de MainActivity host twee fragmenten in een 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>

De SenderFragment onthult een interface SendMessageListener die de MainActivity helpt te weten wanneer op de knop in de SenderFragment is geklikt.

Hierna volgt het codefragment voor de SenderFragment waarin de belangrijke SenderFragment uitgelegd:

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;
}
}

Het lay- SenderFragment voor de 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>

Het ReceiverFragment is eenvoudig en biedt een eenvoudige openbare methode om zijn TextView bij te werken. Wanneer de MainActivity het bericht uit de ontvangt SenderFragment roept deze openbare methode van de ReceiverFragment

Hierna volgt het codefragment voor het ReceiverFragment met opmerkingen waarin de belangrijke coderegels worden uitgelegd:

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);
}
}

Het lay-outbestand voor de 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
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow