Cómo implementar OnFragmentInteractionListener

151

Tengo una aplicación generada por un asistente con un cajón de navegación en Android Studio 0.8.2

He creado un fragmento y lo agregué con newInstance () y aparece este error:

com.domain.myapp E / AndroidRuntime ﹕ EXCEPCIÓN FATAL: main java.lang.ClassCastException: com.domain.myapp.MainActivity@422fb8f0 debe implementar OnFragmentInteractionListener

No puedo encontrar en ninguna parte cómo implementar este OnFragmentInteractionListener ?? ¡No se puede encontrar incluso en la documentación de 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
fuente

Respuestas:

120

Las respuestas publicadas aquí no ayudaron, pero el siguiente enlace sí:

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

Definir una interfaz

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

    ...
}

Por ejemplo, se llama al siguiente método en el fragmento cuando el usuario hace clic en un elemento de la lista. El fragmento utiliza la interfaz de devolución de llamada para entregar el evento a la actividad principal.

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

Implemente la interfaz

Por ejemplo, la siguiente actividad implementa la interfaz del ejemplo anterior.

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

Actualización para API 23: 31/08/2015

El método anulado onAttach(Activity activity)ahora está en desuso android.app.Fragment, el código debe actualizarse aonAttach(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
fuente
77
tenga en cuenta que onAttach(Activity activity);está en desuso y reemplazado cononAttach(Context context)
EpicPandaForce
1
@EpicPandaForce De hecho tienes razón, actualicé mi publicación para reflejar eso
meda
1
¿Hay alguna razón por la cual se mueve de onAttach a onStart? ¿Puedo ponerlo en Attach solo usando el contexto en lugar de la actividad?
Louis Tsai
Creo que usarlo onAttach(context)funcionaría bien
EpicPandaForce
212

Para aquellos de ustedes que todavía no entienden después de leer la respuesta de @meda, aquí está mi explicación concisa y completa de este problema:

Digamos que tiene 2 Fragmentos, Fragment_Ay Fragment_Bque se generan automáticamente desde la aplicación. En la parte inferior de los fragmentos generados, encontrará este código:

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

Para superar el problema, debe agregar un onFragmentInteractionmétodo a su actividad, que en mi caso se llama MainActivity2. Después de eso, necesita implementstodos los fragmentos de la MainActivitysiguiente manera:

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

PD: En resumen, este método podría usarse para comunicarse entre fragmentos. Para aquellos de ustedes que quieran saber más sobre este método, consulte este enlace .

Bla ...
fuente
10
Con la versión actual del SDK en Android Studio, requiere que implemente el onFragmentIntereactior(Uri)método, que no se menciona en ninguna de las otras respuestas. +1
2
¡Muchas gracias!
ofir_aghai
Gracias por mencionar esto. Me ayudo mucho.
hablema
2
Requiere? Simplemente puede borrar el código relacionado con los oyentes ... Si tiene fragmentos que no necesitan interactuar con otros fragmentos, esos oyentes son inútiles.
Trace
44
Mucho más fácil de seguir y comprender que la respuesta aceptada.
jerrythebum
44

Vea su autogenerado Fragmentcreado por Android Studio. Cuando creaste el nuevo Fragment, Studio aplicó un código para ti. En la parte inferior de la plantilla autogenerada hay una definición de interfaz interna llamada OnFragmentInteractionListener. Sus Activitynecesidades para implementar esta interfaz. Este es el patrón recomendado para Fragmentque le notifique a usted Activityde los eventos para que luego pueda tomar las medidas apropiadas, como cargar otro Fragment. Consulte esta página para obtener más detalles, busque la sección "Creación de devoluciones de llamadas de eventos para la actividad": http://developer.android.com/guide/components/fragments.html

Larry Schiefer
fuente
En la documentación se muestra cómo implementar el oyente en el fragmento (que ya ha sido generado por el asistente), pero no en la actividad que hace que la aplicación se bloquee.
Mario M
3
No exactamente. El documento (y el código generado) definen la interfaz y la comprueban cuando Activityse adjunta al Fragment. El bloqueo que está viendo es porque Activityno ha implementado la interfaz. Debe ir a su Activityy agregar y implements YourFragment.OnFragmentInteractionListenerluego agregar una implementación del método definido en la interfaz.
Larry Schiefer
ok, pero no sé cómo agregar esa implementación porque no está en ninguna parte de la documentación de Android SDK
Mario M
1
No es parte del SDK, sino una mejor práctica. El código de plantilla producido por el asistente solo está sentando las bases para usted. La interfaz onFragmentInteraction(Uri uri)es solo un trozo. Puede hacer que este método sea lo que quiera y sus Activitynecesidades para implementarlo. A ver si esto ayuda.
Larry Schiefer
3
Esta pista ahorró muchas horas. Al crear fragmentos, UN-check "incluye métodos de fábrica de fragmentos" e "incluye devoluciones de llamada de interfaz". Y no tiene que implementar OnFragmentInteractionListener. Estoy usando Android studio 1.3.2 con Java sdk 8. Android 6.0 (API 23) y sdk-platform 23 es. Gracias Larry Schiefer.
aprendiz
28

Para aquellos de ustedes que visitan esta página en busca de más aclaraciones sobre este error, en mi caso, la actividad que realiza la llamada al fragmento necesitaba tener 2 implementos en este caso, como este:

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

Deberías intentar eliminar el siguiente código de tus fragmentos

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

La interfaz / escucha se crea por defecto para que su actividad y fragmentos puedan comunicarse más fácilmente

Joe Plante
fuente
2
Este es un muy buen punto ya que este oyente no es necesario en la mayoría de las aplicaciones para principiantes.
Code-Apprentice
5

Además de la respuesta de @ user26409021, si ha agregado un ItemFragment, el mensaje en el ItemFragment es;

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

Y debes agregar tu actividad;

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
}

Aquí el objeto ficticio es lo que tienes en la parte inferior de tu Fragmento de artículo

oneNiceFriend
fuente
5

Conmigo funcionó eliminar este código:

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

Terminando así:

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

OnFragmentInteractionListeneres la implementación predeterminada para manejar fragmentos de comunicación de actividad. Esto se puede implementar en función de sus necesidades. Suponga que si necesita que se ejecute una función en su actividad durante una acción particular dentro de su fragmento, puede utilizar este método de devolución de llamada. Si no necesita tener esta interacción entre su alojamiento activityy fragment, puede eliminar esta implementación.

En resumen, debe ser implementel oyente en su actividad de alojamiento de fragmentos si necesita la interacción fragmento-actividad como esta

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

y tu fragmento debería tenerlo definido así

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

también proporcionar definición para void onFragmentInteraction(Uri uri); su actividad

o simplemente elimine la listenerinicialización de su fragmento onAttachsi no tiene ninguna interacción fragmento-actividad

Navneet Krishna
fuente
3

En lugar de usar el contexto de la actividad, me funciona.

@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
fuente
3

Solo un apéndice:

OnFragmentInteractionListener maneja la comunicación entre Activity y Fragment usando una interfaz (OnFragmentInteractionListener) y es creado de manera predeterminada por Android Studio, pero si no necesita comunicarse con su actividad, simplemente puede manejarla .

El objetivo es que pueda adjuntar su fragmento a múltiples actividades y aún así reutilizar el mismo enfoque de comunicación (cada actividad podría tener su propio OnFragmentInteractionListener para cada fragmento).

¿Pero y si estoy seguro de que mi fragmento se adjuntará a un solo tipo de actividad y quiero comunicarme con esa actividad?

Luego, si no desea utilizar OnFragmentInteractionListener debido a su verbosidad, puede acceder a sus métodos de actividad utilizando:

((MyActivityClass) getActivity()).someMethod()
sagits
fuente
Aunque esto funcionaría en la mayoría de los casos, a veces getActivity () puede devolver nulo si los fragmentos se han separado de la actividad mientras se está llamando, por ejemplo, en el método postExecute de un asinctask, si llama a get activity pero ya ha abandonado el fragmento antes de que se complete asyncTask, obtendría una excepción de puntero nulo. Por esta razón, los documentos de Android dicen específicamente que use una interfaz de escucha de interacción de fragmentos
MichaelStoddart
2

Simplemente vaya a su actividad Fragmento y elimine todos los métodos ..... en su lugar, en el método createview.

su fragmento solo tiene en el método oncreateview eso es todo.

// solo este método implementa otro método delete

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

y asegúrese de que su diseño sea demo para usted.

Kalpesh A. Nikam
fuente
Gracias ... Si necesita algo [email protected] drop mail
Kalpesh A. Nikam
1

Me gustaría agregar la destrucción del oyente cuando el fragmento se separa de la actividad o se destruye.

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

y cuando se usa el nuevo método onStart () con Context

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