Jak zaimplementować OnFragmentInteractionListener

151

Mam aplikację wygenerowaną przez kreatora z szufladą nawigacji w Android Studio 0.8.2

Utworzyłem fragment i dodałem go za pomocą newInstance () i otrzymuję ten błąd:

com.domain.myapp E / AndroidRuntime ﹕ KRYTYCZNY WYJĄTEK: główny java.lang.ClassCastException: com.domain.myapp.MainActivity@422fb8f0 musi zaimplementować OnFragmentInteractionListener

Nie mogę nigdzie znaleźć, jak zaimplementować ten OnFragmentInteractionListener? Nie można go znaleźć nawet w dokumentacji SDK systemu Android!

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
źródło

Odpowiedzi:

120

Opublikowane tutaj odpowiedzi nie pomogły, ale poniższe łącze pomogło:

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

Zdefiniuj interfejs

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

    ...
}

Na przykład następująca metoda we fragmencie jest wywoływana, gdy użytkownik kliknie element listy. Fragment używa interfejsu wywołań zwrotnych, aby dostarczyć zdarzenie do działania nadrzędnego.

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

Zaimplementuj interfejs

Na przykład poniższe działanie implementuje interfejs z powyższego przykładu.

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

Aktualizacja dla API 23: 31.08.2015

Zastąpiona metoda onAttach(Activity activity)jest teraz przestarzała w programie android.app.Fragment, kod powinien zostać zaktualizowany doonAttach(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
źródło
7
należy pamiętać, że onAttach(Activity activity);jest on przestarzały i zastąpiony przezonAttach(Context context)
EpicPandaForce
1
@EpicPandaForce Rzeczywiście masz rację, zaktualizowałem mój post, aby to odzwierciedlić
meda
1
Czy jest jakiś powód, dla którego przechodzi on z onAttach do onStart? czy nadal mogę go umieścić w onAttach używając tylko kontekstu zamiast aktywności?
Louis Tsai
Myślę, że samo użycie onAttach(context)będzie działało dobrze
EpicPandaForce
212

Dla tych z Was, którzy nadal nie rozumieją po przeczytaniu odpowiedzi @meda, oto moje zwięzłe i pełne wyjaśnienie tego problemu:

Powiedzmy, że masz 2 fragmenty, Fragment_Aa Fragment_Bktóre są generowane automatycznie z aplikacji. W dolnej części wygenerowanych fragmentów znajdziesz następujący kod:

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

Aby rozwiązać ten problem, musisz dodać onFragmentInteractiondo swojej aktywności metodę, która w moim przypadku nosi nazwę MainActivity2. Następnie musisz implementswszystkie fragmenty w MainActivityten sposób:

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: Krótko mówiąc, ta metoda może być używana do komunikacji między fragmentami. Ci z Was, którzy chcą dowiedzieć się więcej o tej metodzie, mogą skorzystać z tego linku .

Bla ...
źródło
10
W obecnej wersji SDK w Android Studio wymaga implementacji onFragmentIntereactior(Uri)metody, o której nie ma mowy w żadnej z pozostałych odpowiedzi. +1
2
Dziękuję Ci bardzo!
ofir_aghai
Dzięki za wspomnienie o tym. Bardzo mi pomogło.
hablema
2
Wymaga? Możesz po prostu skasować kod związany ze słuchaczami ... Jeśli zdarzy się, że masz fragmenty, które nie muszą wchodzić w interakcje z innymi fragmentami, ci słuchacze są bezużyteczni.
Trace
4
O wiele łatwiejsze do zrozumienia i zrozumienia niż zaakceptowana odpowiedź.
jerrythebum
44

Zobacz swoje wygenerowane automatycznie Fragmentprzez Android Studio. Po utworzeniu nowej wersji FragmentStudio zgarnęło za Ciebie fragment kodu. U dołu automatycznie generowanego szablonu znajduje się definicja interfejsu wewnętrznego o nazwie OnFragmentInteractionListener. Twoje Activitypotrzeby, aby zaimplementować ten interfejs. Jest to zalecany wzorzec Fragmentpowiadamiania Cię Activityo zdarzeniach, aby mógł następnie podjąć odpowiednie działania, takie jak załadowanie innego Fragment. Zobacz tę stronę, aby uzyskać szczegółowe informacje, poszukaj sekcji „Tworzenie wywołań zwrotnych zdarzeń dla działania”: http://developer.android.com/guide/components/fragments.html

Larry Schiefer
źródło
W dokumentacji pokazano, jak zaimplementować nasłuchiwanie we fragmencie (który jest już wygenerowany przez kreatora), ale nie w głównym działaniu powodującym awarię aplikacji.
Mario M,
3
Nie dokładnie. Dokument (i wygenerowany kod) definiuje interfejs i sprawdza go, gdy Activityplik jest dołączony do Fragment. Awaria, którą widzisz, jest spowodowana Activitytym, że interfejs nie został zaimplementowany. Musisz wejść do swojego Activityi dodać, a implements YourFragment.OnFragmentInteractionListenernastępnie dodać implementację metody zdefiniowanej w interfejsie.
Larry Schiefer,
ok, ale nie wiem jak dodać tę implementację bo nie ma jej nigdzie w dokumentacji sdk androida
Mario M
1
To nie jest część zestawu SDK, ale najlepsza praktyka. Kod szablonu utworzony przez kreatora po prostu kładzie podwaliny pod Ciebie. Interfejs onFragmentInteraction(Uri uri)to tylko skrót. Możesz zrobić z tej metody wszystko, co chcesz i swoje Activitypotrzeby, aby ją wdrożyć. Sprawdź, czy to pomoże.
Larry Schiefer,
3
Ta wskazówka pozwoliła zaoszczędzić wiele godzin. Podczas tworzenia fragmentu UN-check „dołącz metody fabryki fragmentów” i „dołącz wywołania zwrotne interfejsu”. I nie musisz implementować OnFragmentInteractionListener. Używam Android Studio 1.3.2 z Java SDK 8. Android 6.0 (API 23) i sdk-platforma 23 jest. Dziękuję Larry Schiefer.
uczeń
28

Dla tych z Was, którzy odwiedzają tę stronę i szukają dalszych wyjaśnień na temat tego błędu, w moim przypadku czynność wywołująca fragment wymagała w tym przypadku posiadania 2 narzędzi, takich jak ten:

public class MyActivity extends Activity implements 
    MyFragment.OnFragmentInteractionListener, 
    NavigationDrawerFragment.NaviationDrawerCallbacks {
    ...// rest of the code
}
Bwvolleyball
źródło
9

Powinieneś spróbować usunąć następujący kod ze swoich fragmentów

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

Interfejs / nasłuchiwanie jest domyślnym interfejsem stworzonym, aby Twoja aktywność i fragmenty mogły się łatwiej komunikować

Joe Plante
źródło
2
To bardzo dobra uwaga, ponieważ ten słuchacz nie jest potrzebny w większości aplikacji dla początkujących.
Code-Apprentice
5

Oprócz odpowiedzi @ user26409021, jeśli dodałeś ItemFragment, wiadomość w ItemFragment to;

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

I powinieneś dodać do swojej działalności;

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
}

Tutaj fikcyjny przedmiot jest tym, co masz na dole elementu ItemFragment

oneNiceFriend
źródło
5

U mnie zadziałało usuń ten kod:

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

Kończące się w ten sposób:

@Override
public void onAttach(Context context) {
    super.onAttach(context);
}
Vinicius Petrachin
źródło
4

OnFragmentInteractionListenerjest domyślną implementacją obsługi komunikacji fragmentu do aktywności. Można to wdrożyć w zależności od potrzeb. Załóżmy, że jeśli potrzebujesz funkcji w swoim działaniu, która ma być wykonana podczas określonej akcji w Twoim fragmencie, możesz skorzystać z tej metody wywołania zwrotnego. Jeśli nie potrzebujesz tej interakcji między hostingiem activitya fragment, możesz usunąć tę implementację.

Krótko mówiąc, powinieneś implementsłuchać w swojej aktywności hostowania fragmentów, jeśli potrzebujesz takiej interakcji fragment-aktywność

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

a twój fragment powinien mieć taką definicję

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

podaj również definicję void onFragmentInteraction(Uri uri);w swojej działalności

albo po prostu usuń listenerinicjalizację z fragmentów, onAttachjeśli nie masz żadnej interakcji między fragmentami

Navneet Krishna
źródło
3

Zamiast Aktywności użyj kontekstu, to działa dla mnie.

@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
źródło
3

Tylko dodatek:

OnFragmentInteractionListener obsługuje komunikację między Activity i Fragment przy użyciu interfejsu (OnFragmentInteractionListener) i jest domyślnie tworzony przez Android Studio, ale jeśli nie musisz komunikować się ze swoją aktywnością, możesz po prostu jeździć.

Celem jest dołączenie fragmentu do wielu działań i nadal ponowne używanie tego samego podejścia do komunikacji (każde działanie może mieć własny OnFragmentInteractionListener dla każdego fragmentu).

Ale jeśli jestem pewien, że mój fragment będzie powiązany tylko z jednym rodzajem działania i chcę się z nim komunikować?

Następnie, jeśli nie chcesz używać OnFragmentInteractionListener ze względu na jego szczegółowość, możesz uzyskać dostęp do swoich metod działania za pomocą:

((MyActivityClass) getActivity()).someMethod()
zwisy
źródło
Chociaż zadziałałoby to w większości przypadków, czasami getActivity () może zwrócić wartość null, jeśli fragmenty odłączyły się od działania podczas jego wywoływania, na przykład w metodzie postExecute zadania asynchronicznego, jeśli wywołujesz działanie get, ale już opuściłeś fragment przed zakończeniem asyncTask wystąpiłby wyjątek wskaźnika zerowego. Z tego powodu dokumenty Androida wyraźnie mówią o używaniu interfejsu odbiornika interakcji fragmentów
MichaelStoddart
2

Po prostu przejdź do Aktywności fragmentu i usuń całą metodę ..... zamiast tego na metodzie createview.

Twój fragment ma tylko na metodzie oncreateview to wszystko.

// tylko ta metoda implementuje inną metodę delete

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

i upewnij się, że twój układ jest dla ciebie demo.

Kalpesh A. Nikam
źródło
Dziękuję ... Jeśli potrzebujesz Coś [email protected] drop mail
Kalpesh A. Nikam
1

Chciałbym dodać zniszczenie słuchacza, gdy fragment zostanie oderwany od działania lub zniszczony.

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

i podczas używania nowej metody onStart () z Context

@Override
public void onDestroy() {
    super.onDestroy();
    mListener = null;
}
rexxar
źródło