Como implementar OnFragmentInteractionListener

151

Eu tenho um aplicativo gerado por assistente com gaveta de navegação no android studio 0.8.2

Eu criei um fragmento e o adicionei com newInstance () e recebo este erro:

com.domain.myapp E / AndroidRuntime, EXCEÇÃO FATAL: java.lang.ClassCastException principal: com.domain.myapp.MainActivity@422fb8f0 deve implementar OnFragmentInteractionListener

Não consigo encontrar em nenhum lugar como implementar este OnFragmentInteractionListener ?? Ele não pode ser encontrado nem na documentação do 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
fonte

Respostas:

120

As respostas postadas aqui não ajudaram, mas o seguinte link ajudou:

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

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

    ...
}

Por exemplo, o seguinte método no fragmento é chamado quando o usuário clica em um item da lista. O fragmento usa a interface de retorno de chamada para entregar o evento à atividade pai.

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

Implementar a interface

Por exemplo, a atividade a seguir implementa a interface do exemplo acima.

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

Atualização para API 23: 31/8/2015

O método substituído onAttach(Activity activity)agora está obsoleto android.app.Fragment, o código deve ser atualizado paraonAttach(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
fonte
7
observe que onAttach(Activity activity);foi descontinuado e substituído poronAttach(Context context)
EpicPandaForce
1
@EpicPandaForce Na verdade você está certo, eu atualizei meu post para refletir que
meda
1
Existe alguma razão para passar de onAttach para onStart? ainda posso colocá-lo no onAttach usando apenas o contexto em vez da atividade?
Louis Tsai
Eu acho que apenas usando onAttach(context)iria funcionar bem
EpicPandaForce
212

Para aqueles que ainda não entendem depois de lerem a resposta @meda, aqui está minha explicação concisa e completa para esse problema:

Digamos que você tenha 2 fragmentos Fragment_Ae Fragment_Bque são gerados automaticamente a partir do aplicativo. Na parte inferior dos fragmentos gerados, você 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 o problema, você deve adicionar um onFragmentInteractionmétodo à sua atividade, que no meu caso é nomeada MainActivity2. Depois disso, você precisa de implementstodos os fragmentos MainActivityassim:

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: Em resumo, esse método pode ser usado para a comunicação entre fragmentos. Para aqueles que desejam saber mais sobre esse método, consulte este link .

Bla ...
fonte
10
Com a versão atual do SDK no Android Studio, é necessário implementar o onFragmentIntereactior(Uri)método, que não é mencionado em nenhuma das outras respostas. 1
2
Muito obrigado!
ofir_aghai
Obrigado por mencionar isso. Me ajudou muito.
hablema
2
Requer? Você pode simplesmente apagar o código relacionado aos ouvintes ... Se houver fragmentos que não precisem interagir com outros fragmentos, esses ouvintes serão inúteis.
Rastreio
4
Muito mais fácil de seguir e entender do que a resposta aceita.
Jerrythebum # 15/18
44

Veja o seu gerado automaticamente Fragmentcriado pelo Android Studio. Quando você criou o novo Fragment, o Studio colocou um monte de código para você. Na parte inferior do modelo gerado automaticamente, há uma definição de interface interna chamada OnFragmentInteractionListener. Suas Activitynecessidades para implementar essa interface. Esse é o padrão recomendado para você Fragmentnotificar seus Activityeventos, para que ele possa tomar as ações apropriadas, como carregar outro Fragment. Consulte esta página para obter detalhes, procure a seção "Criando retornos de chamada de evento para a atividade": http://developer.android.com/guide/components/fragments.html

Larry Schiefer
fonte
Na documentação mostra como implementar o ouvinte no fragmento (que já é gerado pelo assistente), mas não na atividade principal que causa a falha do aplicativo.
Mario M
3
Não exatamente. O documento (e o código gerado) define a interface e verifica quando ela Activityé anexada ao Fragment. A falha que você está vendo é porque o seu Activitynão implementou a interface. Você precisa entrar no seu Activitye adicionar um implements YourFragment.OnFragmentInteractionListenere adicionar uma implementação do método definido na interface.
Larry Schiefer
ok, mas eu não sei como adicionar que a implementação porque é em nenhum lugar a documentação SDK do Android
Mario M
1
Não faz parte do SDK, mas é uma prática recomendada. O código do modelo produzido pelo assistente está apenas preparando as bases para você. A interface onFragmentInteraction(Uri uri)é apenas um esboço. Você pode fazer esse método o que quiser e suas Activitynecessidades para implementá-lo. Veja se isso ajuda.
Larry Schiefer
3
Essa dica economizou muitas horas. Ao criar o fragmento UN-check "inclua métodos de fábrica de fragmentos" e "inclua retornos de chamada de interface". E você não precisa implementar OnFragmentInteractionListener. Estou usando o Android studio 1.3.2 com Java sdk 8. O Android 6.0 (API 23) e a plataforma sdk 23 são. Obrigado Larry Schiefer.
aluno
28

Para aqueles que visitam esta página procurando mais esclarecimentos sobre esse erro, no meu caso, a atividade que fez a chamada para o fragmento precisava ter 2 implementos neste caso, como este:

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

Você deve tentar remover o seguinte código dos seus fragmentos

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

A interface / ouvinte é um padrão criado para que sua atividade e fragmentos possam se comunicar com mais facilidade

Joe Plante
fonte
2
Esse é um ponto muito bom, pois esse ouvinte não é necessário na maioria dos aplicativos iniciantes.
Code-Apprentice
5

Além da resposta de @ user26409021, se você adicionou um ItemFragment, a mensagem no ItemFragment é;

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

E você deve adicionar sua atividade;

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
}

Aqui, o item fictício é o que você tem na parte inferior do ItemFragment

oneNiceFriend
fonte
5

Comigo funcionou excluir 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 assim:

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

OnFragmentInteractionListeneré a implementação padrão para manipular comunicação de fragmento para atividade. Isso pode ser implementado com base em suas necessidades. Suponha que, se você precisar que uma função em sua atividade seja executada durante uma ação específica em seu fragmento, você poderá fazer uso desse método de retorno de chamada. Se você não precisar ter essa interação entre sua hospedagem activitye fragment, poderá remover esta implementação.

Em resumo, você deve implementouvir o ouvinte em sua atividade de hospedagem de fragmentos, se precisar da interação atividade-fragmento como esta

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

e seu fragmento deve defini-lo assim

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

também forneça definição para void onFragmentInteraction(Uri uri);em sua atividade

ou então remova a listenerinicialização do seu fragmento onAttachse você não tiver nenhuma interação atividade-fragmento

Navneet Krishna
fonte
3

Em vez de Atividade, use o contexto. Funciona para mim.

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

Apenas um adendo:

OnFragmentInteractionListener controla a comunicação entre Activity e Fragment usando uma interface (OnFragmentInteractionListener) e é criado por padrão pelo Android Studio, mas se você não precisar se comunicar com sua atividade, pode simplesmente aproveitá-la.

O objetivo é que você possa anexar seu fragmento a várias atividades e ainda reutilizar a mesma abordagem de comunicação (cada atividade pode ter seu próprio OnFragmentInteractionListener para cada fragmento).

Mas e se tiver certeza de que meu fragmento será anexado a apenas um tipo de atividade e eu quero me comunicar com essa atividade?

Então, se você não quiser usar OnFragmentInteractionListener devido à sua verbosidade, poderá acessar seus métodos de atividade usando:

((MyActivityClass) getActivity()).someMethod()
sagits
fonte
Embora isso funcione na maioria dos casos, às vezes getActivity () pode retornar nulo se os fragmentos foram desanexados da atividade enquanto isso está sendo chamado, por exemplo, no método postExecute de uma assíncrona, se você chamar a atividade get, mas já deixou o fragmento antes que o asyncTask seja concluído, você receberá uma exceção de ponteiro nulo. Por esse motivo, os documentos do Android dizem especificamente para usar uma interface de ouvinte de interação com fragmentos
MichaelStoddart
2

Basta ir ao seu fragmento Activity e remover todo o método ..... em vez disso, no método createview.

seu fragmento possui apenas o método oncreateview.

// somente este método implementa outro 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;
}

e verifique se o layout é demo para você.

Kalpesh A. Nikam
fonte
Obrigado ... Se precisar de algo [email protected] drop mail
Kalpesh A. Nikam
1

Eu gostaria de adicionar a destruição do ouvinte quando o fragmento é desanexado da atividade ou destruído.

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

e ao usar o novo método onStart () com Context

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