Как реализовать OnFragmentInteractionListener

151

У меня есть приложение сгенерированное мастером с навигационным ящиком в Android Studio 0.8.2

Я создал фрагмент и добавил его с помощью newInstance (), и я получаю эту ошибку:

com.domain.myapp E / AndroidRuntime ﹕ ИСКЛЮЧИТЕЛЬНОЕ ИСКЛЮЧЕНИЕ: main java.lang.ClassCastException: com.domain.myapp.MainActivity@422fb8f0 должен реализовывать OnFragmentInteractionListener

Я не могу найти где-нибудь, как реализовать этот OnFragmentInteractionListener ?? Это не может быть найдено даже в документации 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));
    }
}

}

Марио М
источник

Ответы:

120

Ответы, размещенные здесь, не помогли, но следующая ссылка сделала:

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

Определить интерфейс

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

    ...
}

Например, следующий метод во фрагменте вызывается, когда пользователь щелкает элемент списка. Фрагмент использует интерфейс обратного вызова для доставки события в родительское действие.

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

Реализуйте интерфейс

Например, следующее действие реализует интерфейс из приведенного выше примера.

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

Обновление для API 23: 31.08.2015

Переопределенный метод onAttach(Activity activity)теперь устарел android.app.Fragment, код должен быть обновлен доonAttach(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");
    }
}
МЕДА
источник
7
обратите внимание, что onAttach(Activity activity);это устарело и заменено наonAttach(Context context)
EpicPandaForce
1
@EpicPandaForce Действительно, вы правы, я обновил свой пост, чтобы отразить это
меда
1
Есть ли причина, по которой он переходит из onAttach в onStart? я могу все еще поместить это в Attach, просто используя контекст вместо активности?
Луи Цай
Я думаю, что просто использование onAttach(context)будет работать нормально
EpicPandaForce
212

Для тех из вас, кто все еще не понимает после прочтения ответа @meda, вот мое краткое и полное объяснение этой проблемы:

Допустим, у вас есть 2 фрагментов, Fragment_Aи Fragment_Bкоторые автоматически генерируется из приложения. В нижней части сгенерированных фрагментов вы найдете этот код:

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

Чтобы преодолеть проблему, вы должны добавить onFragmentInteractionметод в вашу деятельность, которая в моем случае называется MainActivity2. После этого вам нужно implementsвсе фрагменты MainActivityподобным образом:

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: Короче говоря, этот метод может быть использован для связи между фрагментами. Для тех из вас, кто хочет узнать больше об этом методе, пожалуйста, перейдите по этой ссылке .

Bla ...
источник
10
В текущей версии SDK в Android Studio требуется реализовать onFragmentIntereactior(Uri)метод, который не упоминается ни в одном из других ответов. +1
2
Большое спасибо!
ofir_aghai
Спасибо за упоминание этого. Мне очень помогло.
Хаблема
2
Требуется? Вы можете просто стереть код, относящийся к слушателям ... Если у вас есть фрагменты, которым не нужно взаимодействовать с другими фрагментами, эти слушатели бесполезны.
Трассировка
4
Намного легче понять и понять, чем принятый ответ.
Jerrythebum
44

Смотрите ваш автоматически Fragmentсозданный Android Studio. Когда вы создали новый Fragment, Studio заткнул кучу кода для вас. В нижней части автоматически сгенерированного шаблона находится определение внутреннего интерфейса OnFragmentInteractionListener. Вам Activityнеобходимо реализовать этот интерфейс. Это рекомендуемый шаблон для Fragmentуведомления Activityо событиях, чтобы он мог предпринять соответствующие действия, например загрузить другое Fragment. Для получения дополнительной информации см. Эту страницу, см. Раздел «Создание обратных вызовов событий для действия»: http://developer.android.com/guide/components/fragments.html

Ларри Шифер
источник
В документации показано, как реализовать прослушиватель во фрагменте (который уже создан мастером), но не в основной активности, которая вызывает сбой приложения.
Марио М,
3
Не совсем. Документ (и сгенерированный код) определяют интерфейс и проверяют его, когда Activityон присоединен к Fragment. Вы видите сбой, потому что ваш Activityинтерфейс не реализован. Вам нужно зайти в свой Activityи добавить, а implements YourFragment.OnFragmentInteractionListenerзатем добавить реализацию метода, определенного в интерфейсе.
Ларри Шифер
хорошо, но я не знаю, как добавить эту реализацию, потому что ее нет в документации Android SDK
Mario M
1
Это не часть SDK, а лучшая практика. Код шаблона, созданный мастером, просто закладывает основу для вас. Интерфейс onFragmentInteraction(Uri uri)просто заглушка. Вы можете сделать этот метод все, что вы хотите, и ваши Activityпотребности для его реализации. Посмотрите, поможет ли это .
Ларри Шифер
3
Этот намек сэкономил много часов. При создании фрагмента снимите флажок «Включить методы фабрики фрагментов» и «Включить обратные вызовы интерфейса». И вам не нужно реализовывать OnFragmentInteractionListener. Я использую Android Studio 1.3.2 с Java SDK 8. Android 6.0 (API 23) и SDK-платформа 23 есть. Спасибо Ларри Шифер.
ученик
28

Для тех из вас, кто посещает эту страницу и ищет дальнейших разъяснений по этой ошибке, в моем случае для операции, выполняющей вызов фрагмента, в этом случае необходимо иметь 2 инструмента, например:

public class MyActivity extends Activity implements 
    MyFragment.OnFragmentInteractionListener, 
    NavigationDrawerFragment.NaviationDrawerCallbacks {
    ...// rest of the code
}
Bwvolleyball
источник
9

Вы должны попытаться удалить следующий код из ваших фрагментов

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

Интерфейс / прослушиватель создан по умолчанию, чтобы ваши действия и фрагменты могли общаться

Джо Планте
источник
2
Это очень хороший момент, так как этот слушатель не нужен в большинстве приложений для начинающих.
Код-ученик
5

В дополнение к ответу @ user26409021, если вы добавили ItemFragment, сообщение в ItemFragment будет таким;

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

И Вы должны добавить в свою деятельность;

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
}

Здесь фиктивный элемент - это то, что у вас есть в нижней части вашего ItemFragment

oneNiceFriend
источник
5

У меня получилось удалить этот код:

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

Заканчивается так:

@Override
public void onAttach(Context context) {
    super.onAttach(context);
}
Виниций Петрачин
источник
4

OnFragmentInteractionListenerявляется реализацией по умолчанию для обработки фрагмента для связи активности. Это может быть реализовано в зависимости от ваших потребностей. Предположим, если вам нужно, чтобы функция в вашей деятельности выполнялась во время определенного действия в вашем фрагменте, вы можете использовать этот метод обратного вызова. Если вам не нужно это взаимодействие между вашим хостингом activityи fragment, вы можете удалить эту реализацию.

Короче говоря, вы должны implementслушать слушателя в вашей активности хостинга фрагментов, если вам нужно взаимодействие фрагмента с активностью, подобное этому

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

и ваш фрагмент должен иметь такое определение

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

также дать определение void onFragmentInteraction(Uri uri);в вашей деятельности

или просто удалите listenerинициализацию из вашего фрагмента, onAttachесли у вас нет никакого взаимодействия фрагмент-активность

Навнеет Кришна
источник
3

Вместо Activity используйте context.It работает для меня.

@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
источник
3

Просто добавление:

OnFragmentInteractionListener обрабатывает связь между Activity и Fragment с помощью интерфейса (OnFragmentInteractionListener) и создается по умолчанию в Android Studio, но если вам не нужно общаться с вашей активностью, вы можете просто ее использовать.

Цель состоит в том, чтобы вы могли прикрепить свой фрагмент к нескольким действиям и при этом использовать один и тот же подход к общению (каждое действие может иметь собственный OnFragmentInteractionListener для каждого фрагмента).

Но и если я уверен, что мой фрагмент будет привязан только к одному виду деятельности, и я хочу общаться с этим действием?

Затем, если вы не хотите использовать OnFragmentInteractionListener из-за его многословия, вы можете получить доступ к своим методам активности, используя:

((MyActivityClass) getActivity()).someMethod()
sagits
источник
Хотя это будет работать в большинстве случаев, иногда getActivity () может возвращать ноль, если фрагменты отсоединились от действия во время его вызова, например, в методе асинхронной операции postExecute, если вы вызываете функцию get, но уже оставили фрагмент перед завершением asyncTask вы получите исключение нулевого указателя. По этой причине в документах Android специально говорится об использовании интерфейса прослушивателя взаимодействия фрагментов
MichaelStoddart
2

Просто перейдите к своему фрагменту Activity и удалите все методы ..... вместо этого в методе createview.

ваш фрагмент имеет только по методу oncreateview вот и все.

// только этот метод реализует другой метод delete

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

и убедитесь, что ваш макет это демо для вас.

Кальпеш А. Никам
источник
Спасибо ... Если нужно Что-то kalpeshnikam1080@gmail.com напиши письмо
Калпеш А. Никам
1

Я хотел бы добавить уничтожение слушателя, когда фрагмент отсоединен от действия или уничтожен.

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

и при использовании нового метода onStart () с Context

@Override
public void onDestroy() {
    super.onDestroy();
    mListener = null;
}
Рексар
источник