Як реалізувати OnFragmentInteractionListener


151

У мене в андроїд-студії 0.8.2 створено майстер додаток з навігаційним ящиком

Я створив фрагмент і додав його до newInstance (), і я отримую цю помилку:

com.domain.myapp E / AndroidRuntime ﹕ FATAL EXCEPTION: 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 Дійсно, ти маєш рацію, я оновив свій пост, щоб відобразити це
meda

1
Чи є якась причина, чому вона переходить від onAttach до onStart? чи можу я все-таки помістити його в onAttach, просто використовуючи контекст замість діяльності?
Луї Цай

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


10
З поточною версією SDK в Android Studio він вимагає від вас реалізувати onFragmentIntereactior(Uri)метод, який не згадується в жодній з інших відповідей. +1

2
Велике спасибі!
ofir_aghai

Дякуємо, що згадали про це. Мені дуже допомагали.
hablema

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
Цей натяк врятував багато годин. Під час створення фрагмента UN-перевірки "включають фабричні методи заводу фрагментів" та "включають зворотні виклики інтерфейсу". І вам не доведеться реалізовувати 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
}

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


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

Замість діяльності використовуйте контекст. Він працює для мене.

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

3

Просто додаток:

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

Мета полягає в тому, що ви можете приєднати свій фрагмент до декількох видів діяльності та все-таки використовувати один і той же підхід до комунікацій (Кожна діяльність може мати свій власний OnFragmentInteractionListener для кожного фрагмента).

Але якщо я впевнений, що мій фрагмент буде прив’язаний лише до одного виду діяльності, і я хочу спілкуватися з цією діяльністю?

Потім, якщо ви не хочете використовувати OnFragmentInteractionListener через його багатослівність, ви можете отримати доступ до методів своєї діяльності, використовуючи:

((MyActivityClass) getActivity()).someMethod()

Хоча це працювало б у більшості випадків, іноді getActivity () може повернути нуль, якщо фрагменти відірвалися від активності, коли це викликається, наприклад, у методі postExecute асинтакта, якщо ви викликаєте отримати активність, але вже залишили фрагмент до того, як asyncTask завершиться, ви отримаєте нульове виключення вказівника. З цієї причини документи для Android спеціально кажуть використовувати інтерфейс для прослуховування фрагментів взаємодії
MichaelStoddart

2

Просто перейдіть до фрагмента «Активність» і видаліть увесь метод ..... замість цього на 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, викиньте пошту
Kalpesh A. Nikam

1

Я хотів би додати знищення слухача, коли фрагмент відірваний від активності або знищений.

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

і при використанні нового методу onStart () з контекстом

@Override
public void onDestroy() {
    super.onDestroy();
    mListener = null;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.