Comment implémenter OnFragmentInteractionListener

151

J'ai une application générée par un assistant avec un tiroir de navigation dans Android Studio 0.8.2

J'ai créé un fragment et l'ai ajouté avec newInstance () et j'obtiens cette erreur:

com.domain.myapp E / AndroidRuntime ﹕ FATAL EXCEPTION: main java.lang.ClassCastException: com.domain.myapp.MainActivity@422fb8f0 doit implémenter OnFragmentInteractionListener

Je ne trouve nulle part comment implémenter ce OnFragmentInteractionListener ?? Il ne peut pas être trouvé même dans la documentation android sdk!

MainActivity.java

import android.app.Activity;

import android.app.ActionBar;
import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.support.v4.widget.DrawerLayout;


public class MainActivity extends Activity
    implements NavigationDrawerFragment.NavigationDrawerCallbacks {

/**
 * Fragment managing the behaviors, interactions and presentation of the navigation drawer.
 */
private NavigationDrawerFragment mNavigationDrawerFragment;

/**
 * Used to store the last screen title. For use in {@link #restoreActionBar()}.
 */
private CharSequence mTitle;

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

    mNavigationDrawerFragment = (NavigationDrawerFragment)
            getFragmentManager().findFragmentById(R.id.navigation_drawer);
    mTitle = getTitle();

    // Set up the drawer.
    mNavigationDrawerFragment.setUp(
            R.id.navigation_drawer,
            (DrawerLayout) findViewById(R.id.drawer_layout));
}

@Override
public void onNavigationDrawerItemSelected(int position) {
    // update the main content by replacing fragments
    FragmentManager fragmentManager = getFragmentManager();

    switch (position) {
        case 0: fragmentManager.beginTransaction()
                .replace(R.id.container, PlaceholderFragment.newInstance(position + 1))
                .commit(); break; 
        case 1: fragmentManager.beginTransaction() 
                .replace(R.id.container, AboutFragment.newInstance("test1", "test2"))
                .commit(); break; // this crashes the app
        case 2: fragmentManager.beginTransaction()
                .replace(R.id.container, BrowseQuotesFragment.newInstance("test1", "test2"))
                .commit(); break; // this crashes the app
    }
}


public void onSectionAttached(int number) {
    switch (number) {
        case 1:
            mTitle = getString(R.string.title_section1);
            break;
        case 2:
            mTitle = getString(R.string.title_section2);
            break;
        case 3:
            mTitle = getString(R.string.title_section3);
            break;
    }
}

public void restoreActionBar() {
    ActionBar actionBar = getActionBar();
    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
    actionBar.setDisplayShowTitleEnabled(true);
    actionBar.setTitle(mTitle);
}


@Override
public boolean onCreateOptionsMenu(Menu menu) {
    if (!mNavigationDrawerFragment.isDrawerOpen()) {
        // Only show items in the action bar relevant to this screen
        // if the drawer is not showing. Otherwise, let the drawer
        // decide what to show in the action bar.
        getMenuInflater().inflate(R.menu.main, menu);
        restoreActionBar();
        return true;
    }
    return super.onCreateOptionsMenu(menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();
    if (id == R.id.action_settings) {
        return true;
    }
    return super.onOptionsItemSelected(item);
}

/**
 * A placeholder fragment containing a simple view.
 */
public static class PlaceholderFragment extends Fragment {
    /**
     * The fragment argument representing the section number for this
     * fragment.
     */
    private static final String ARG_SECTION_NUMBER = "section_number";

    /**
     * Returns a new instance of this fragment for the given section
     * number.
     */
    public static PlaceholderFragment newInstance(int sectionNumber) {
        PlaceholderFragment fragment = new PlaceholderFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_SECTION_NUMBER, sectionNumber);
        fragment.setArguments(args);
        return fragment;
    }

    public PlaceholderFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_main, container, false);
        return rootView;
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        ((MainActivity) activity).onSectionAttached(
                getArguments().getInt(ARG_SECTION_NUMBER));
    }
}

}

Mario M
la source

Réponses:

120

Les réponses publiées ici n'ont pas aidé, mais le lien suivant l'a fait:

http://developer.android.com/training/basics/fragments/communicating.html

Définir une interface

public class HeadlinesFragment extends ListFragment {
    OnHeadlineSelectedListener mCallback;

    // Container Activity must implement this interface
    public interface OnHeadlineSelectedListener {
        public void onArticleSelected(int position);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        // This makes sure that the container activity has implemented
        // the callback interface. If not, it throws an exception
        try {
            mCallback = (OnHeadlineSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                    + " must implement OnHeadlineSelectedListener");
        }
    }

    ...
}

Par exemple, la méthode suivante dans le fragment est appelée lorsque l'utilisateur clique sur un élément de liste. Le fragment utilise l'interface de rappel pour fournir l'événement à l'activité parent.

@Override
public void onListItemClick(ListView l, View v, int position, long id) {
    // Send the event to the host activity
    mCallback.onArticleSelected(position);
}

Mettre en œuvre l'interface

Par exemple, l'activité suivante implémente l'interface de l'exemple ci-dessus.

public static class MainActivity extends Activity
        implements HeadlinesFragment.OnHeadlineSelectedListener{
    ...

    public void onArticleSelected(int position) {
        // The user selected the headline of an article from the HeadlinesFragment
        // Do something here to display that article
    }
}

Mise à jour pour l'API 23: 31/08/2015

La méthode remplacée onAttach(Activity activity)est désormais obsolète dans android.app.Fragment, le code doit être mis à niveau versonAttach(Context context)

@Override
public void onAttach(Context context) {
    super.onAttach(context);
}


@Override
public void onStart() {
    super.onStart();
    try {
        mListener = (OnFragmentInteractionListener) getActivity();
    } catch (ClassCastException e) {
        throw new ClassCastException(getActivity().toString()
                + " must implement OnFragmentInteractionListener");
    }
}
meda
la source
7
veuillez noter qu'il onAttach(Activity activity);est obsolète et remplacé paronAttach(Context context)
EpicPandaForce
1
@EpicPandaForce En effet, vous avez raison, j'ai mis à jour mon message pour refléter cela
meda
1
Y a-t-il une raison pour laquelle il passe de onAttach à onStart? puis-je toujours le mettre dans onAttach en utilisant simplement le contexte au lieu de l'activité?
Louis Tsai
Je pense que le simple fait d'utiliser onAttach(context)fonctionnerait bien
EpicPandaForce
212

Pour ceux d'entre vous qui ne comprennent toujours pas après avoir lu la réponse @meda, voici mon explication concise et complète de ce problème:

Disons que vous avez 2 fragments, Fragment_Aet Fragment_Bqui sont générés automatiquement à partir de l'application. Sur la partie inférieure de vos fragments générés, vous allez trouver ce code:

public class Fragment_A extends Fragment {

    //rest of the code is omitted

    public interface OnFragmentInteractionListener {
        // TODO: Update argument type and name
        public void onFragmentInteraction(Uri uri);
    }
}

public class Fragment_B extends Fragment {

    //rest of the code is omitted

    public interface OnFragmentInteractionListener {
        // TODO: Update argument type and name
        public void onFragmentInteraction(Uri uri);
    }
}

Pour surmonter le problème, vous devez ajouter une onFragmentInteractionméthode à votre activité, qui dans mon cas est nommée MainActivity2. Après cela, vous devez implementstous les fragments de la manière MainActivitysuivante:

public class MainActivity2 extends ActionBarActivity
        implements Fragment_A.OnFragmentInteractionListener, 
                   Fragment_B.OnFragmentInteractionListener, 
                   NavigationDrawerFragment.NavigationDrawerCallbacks {
    //rest code is omitted

    @Override
    public void onFragmentInteraction(Uri uri){
        //you can leave it empty
    }
}

PS: En bref, cette méthode pourrait être utilisée pour communiquer entre fragments. Pour ceux d'entre vous qui souhaitent en savoir plus sur cette méthode, veuillez consulter ce lien .

Bla ...
la source
10
Avec la version actuelle du SDK dans Android studio, il faut vous mettre en œuvre la onFragmentIntereactior(Uri)méthode, qui est mentionné dans aucun des autres réponses. +1
2
Merci beaucoup!
ofir_aghai
Merci d'avoir mentionné cela. M'a beaucoup aidé.
hablema
2
A besoin? Vous pouvez simplement effacer le code lié aux écouteurs ... Si vous avez des fragments qui n'ont pas besoin d'interagir avec d'autres fragments, ces écouteurs sont inutiles.
Trace du
4
Beaucoup plus facile à suivre et à comprendre que la réponse acceptée.
jerrythebum
44

Consultez votre Fragmentcréation automatique créée par Android Studio. Lorsque vous avez créé le nouveau Fragment, Studio a écrasé un tas de code pour vous. Au bas du modèle généré automatiquement, il y a une définition d'interface interne appelée OnFragmentInteractionListener. Vos Activitybesoins pour implémenter cette interface. Il s'agit du modèle recommandé pour que vous Fragmentvous informiez Activitydes événements afin qu'il puisse ensuite prendre les mesures appropriées, comme en charger un autre Fragment. Consultez cette page pour plus de détails, recherchez la section "Création de rappels d'événements pour l'activité": http://developer.android.com/guide/components/fragments.html

Larry Schiefer
la source
Dans la documentation montre comment implémenter l'écouteur dans le fragment (qui est déjà généré par l'assistant), mais pas dans l'activité principale qui provoque le blocage de l'application.
Mario M
3
Pas exactement. Le doc (et le code généré) définissent l'interface et la vérifient lorsque le Activityest attaché au Fragment. Le crash que vous voyez est dû au fait que vous n'avez Activitypas implémenté l'interface. Vous devez aller dans votre Activityet ajouter un implements YourFragment.OnFragmentInteractionListenerpuis ajouter une implémentation de la méthode définie dans l'interface.
Larry Schiefer
ok, mais je ne sais pas comment ajouter cette implémentation car elle n'est nulle part dans la documentation android sdk
Mario M
1
Cela ne fait pas partie du SDK, mais une meilleure pratique. Le code de modèle produit par l'assistant ne fait que jeter les bases pour vous. L'interface onFragmentInteraction(Uri uri)est juste un stub. Vous pouvez faire de cette méthode tout ce que vous voulez et vos Activitybesoins pour la mettre en œuvre. Voyez si cela aide.
Larry Schiefer
3
Cet indice a sauvé de nombreuses heures. Lors de la création de fragments UN-check "Inclure les méthodes de fabrique de fragments" et "Inclure les rappels d'interface". Et vous n'avez pas à implémenter OnFragmentInteractionListener. J'utilise Android Studio 1.3.2 avec Java sdk 8. Android 6.0 (API 23) et sdk-platform 23 est. Merci Larry Schiefer.
apprenant
28

Pour ceux d'entre vous qui visitent cette page à la recherche de précisions sur cette erreur, dans mon cas, l'activité appelant le fragment nécessitait d'avoir 2 outils dans ce cas, comme ceci:

public class MyActivity extends Activity implements 
    MyFragment.OnFragmentInteractionListener, 
    NavigationDrawerFragment.NaviationDrawerCallbacks {
    ...// rest of the code
}
Bwvolleyball
la source
9

Vous devriez essayer de supprimer le code suivant de vos fragments

    try {
        mListener = (OnFragmentInteractionListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString()
                + " must implement OnFragmentInteractionListener");
    }

L'interface / écouteur est une valeur par défaut créée pour que votre activité et vos fragments puissent communiquer plus facilement

Joe Plante
la source
2
C'est un très bon point car cet auditeur n'est pas nécessaire dans la plupart des applications pour débutants.
Code-Apprentice
5

En plus de la réponse de @ user26409021, si vous avez ajouté un ItemFragment, le message dans ItemFragment est;

Activities containing this fragment MUST implement the {@link OnListFragmentInteractionListener} interface.

Et vous devez ajouter votre activité;

public class MainActivity extends AppCompatActivity
    implements NavigationView.OnNavigationItemSelectedListener, ItemFragment.OnListFragmentInteractionListener {

//the code is omitted

 public void onListFragmentInteraction(DummyContent.DummyItem uri){
    //you can leave it empty
}

Ici, l'élément factice est ce que vous avez sur le bas de votre ItemFragment

oneNiceFriend
la source
5

Avec moi, cela a fonctionné, supprimez ce code:

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

Se terminant comme ça:

@Override
public void onAttach(Context context) {
    super.onAttach(context);
}
Vinicius Petrachin
la source
4

OnFragmentInteractionListenerest l'implémentation par défaut pour la gestion de la communication fragment à activité. Cela peut être mis en œuvre en fonction de vos besoins. Supposons que si vous avez besoin qu'une fonction de votre activité soit exécutée pendant une action particulière dans votre fragment, vous pouvez utiliser cette méthode de rappel. Si vous n'avez pas besoin d'avoir cette interaction entre votre hébergement activityet fragment, vous pouvez supprimer cette implémentation.

En bref, vous devriez implementl'auditeur dans votre activité d'hébergement de fragment si vous avez besoin de l'interaction fragment-activité comme celle-ci

public class MainActivity extends Activity implements 
YourFragment.OnFragmentInteractionListener {..}

et votre fragment devrait l'avoir défini comme ceci

public interface OnFragmentInteractionListener {
    // TODO: Update argument type and name
    void onFragmentInteraction(Uri uri);
}

fournissent également une définition pour void onFragmentInteraction(Uri uri); votre activité

ou bien supprimez simplement l' listenerinitialisation de votre fragment onAttachsi vous n'avez aucune interaction fragment-activité

Navneet Krishna
la source
3

Au lieu d'Activité, utilisez le contexte. Cela fonctionne pour moi.

@Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            mListener = (OnFragmentInteractionListener) context;
        } catch (ClassCastException e) {
            throw new ClassCastException(context.toString()
                    + " must implement OnFragmentInteractionListener");
        }
}
KCN
la source
3

Juste un addendum:

OnFragmentInteractionListener gère la communication entre Activity et Fragment à l'aide d'une interface (OnFragmentInteractionListener) et est créé par défaut par Android Studio, mais si vous n'avez pas besoin de communiquer avec votre activité, vous pouvez simplement en profiter.

L'objectif est que vous puissiez attacher votre fragment à plusieurs activités tout en réutilisant la même approche de communication (chaque activité peut avoir son propre OnFragmentInteractionListener pour chaque fragment).

Mais et si je suis sûr que mon fragment sera attaché à un seul type d'activité et que je souhaite communiquer avec cette activité?

Ensuite, si vous ne souhaitez pas utiliser OnFragmentInteractionListener en raison de sa verbosité, vous pouvez accéder à vos méthodes d'activité en utilisant:

((MyActivityClass) getActivity()).someMethod()
s'affaisse
la source
Bien que cela fonctionne dans la plupart des cas, getActivity () peut parfois retourner null si les fragments se sont détachés de l'activité pendant son appel, par exemple dans la méthode postExecute d'une asynctask, si vous appelez get activity mais que vous avez déjà quitté le fragment avant la fin de la tâche asyncTask, vous obtiendrez une exception de pointeur nul. Pour cette raison, les documents Android indiquent spécifiquement d'utiliser une interface d'écoute d'interaction de fragment
MichaelStoddart
2

Accédez simplement à votre activité de fragment et supprimez toutes les méthodes ..... à la place de la méthode createview.

votre fragment n'a que sur la méthode oncreateview c'est tout.

// seule cette méthode implémente une autre méthode delete

 @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_main, container, false);
    return rootView;
}

et assurez-vous que votre mise en page est une démonstration pour vous.

Kalpesh A. Nikam
la source
Merci ... Si besoin Quelque chose [email protected] envoyer un mail
Kalpesh A. Nikam
1

Je voudrais ajouter la destruction de l'auditeur lorsque le fragment est détaché de l'activité ou détruit.

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

et lors de l'utilisation de la nouvelle méthode onStart () avec Context

@Override
public void onDestroy() {
    super.onDestroy();
    mListener = null;
}
rexxar
la source