OnFragmentInteractionListenerを実装する方法


151

私はAndroid Studio 0.8.2にナビゲーションドロワーを備えたウィザード生成アプリを持っています

フラグメントを作成してnewInstance()で追加したところ、次のエラーが発生しました。

com.domain.myapp E / AndroidRuntime:致命的な例外:メイン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の更新:2015年8月31日

オーバーライドされたメソッド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に配置できますか?
Louis Tsai

私はちょうど使用することは考えて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
Android StudioのSDKの現在のバージョンでは、メソッドを実装する必要がありますがonFragmentIntereactior(Uri)、これは他の回答には記載されていません。+1

2
どうもありがとうございました!
ofir_aghai

これについて言及していただきありがとうございます。私を大いに助けてくれました。
hablema

2
必要ですか?リスナーに関連するコードを消去するだけです...他のフラグメントと相互作用する必要のないフラグメントがある場合、それらのリスナーは役に立ちません。
トレース

4
受け入れられた答えよりもずっと理解しやすく理解しやすいです。
jerrythebum

44

FragmentAndroid Studio によって作成された自動生成を確認します。新しいを作成したときFragment、Studioは一連のコードをスタブしました。自動生成されたテンプレートの下部には、という内部インターフェース定義がありますOnFragmentInteractionListener。あなたのActivityこのインタフェースを実装する必要があります。これは、がイベントFragmentを通知し、Activity別のをロードするなどの適切なアクションを実行できるようにするための推奨パターンですFragment。詳細については、このページを参照し、「アクティビティのイベントコールバックを作成する」セクションを探してください:http : //developer.android.com/guide/components/fragments.html


ドキュメントでは、(ウィザードによって既に生成されている)フラグメントにリスナーを実装する方法を示していますが、アプリをクラッシュさせるmainactivityにはありません。
マリオM

3
ではない正確に。doc(および生成されたコード)はインターフェイスを定義し、ActivityがにアタッチされたときにそれをチェックしますFragment。発生しているクラッシュはActivity、インターフェースが実装されていないためです。に移動してActivity追加し、implements YourFragment.OnFragmentInteractionListener次にインターフェイスで定義されたメソッドの実装を追加する必要があります。
ラリーシーファー2014

わかりましたが、Android sdkのドキュメントにはないため、その実装を追加する方法はわかりません
Mario M

1
これはSDKの一部ではありませんが、ベストプラクティスです。ウィザードによって生成されたテンプレートコードは、単に土台を築いているだけです。インターフェースonFragmentInteraction(Uri uri)は単なるスタブです。このメソッドは好きなように作成でき、Activity必要に応じて実装できます。これが役立つかどうかを確認ます。
ラリーシーファー2014

3
このヒントは多くの時間を節約しました。フラグメントの作成中に、「フラグメントファクトリメソッドを含める」と「インターフェイスコールバックを含める」をチェック解除します。また、OnFragmentInteractionListenerを実装する必要はありません。私はJava sdk 8でAndroid Studio 1.3.2を使用しています。Android6.0(API 23)とsdk-platform 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
ほとんどの初心者アプリではこのリスナーは必要ないため、これは非常に良い点です。
Code-Apprentice

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は、インターフェース(OnFragmentInteractionListener)を使用してActivityとFragment間の通信を処理し、デフォルトでAndroid Studioによって作成されますが、アクティビティと通信する必要がない場合は、アクティビティに乗ることができます。

目標は、フラグメントを複数のアクティビティにアタッチし、それでも同じ通信アプローチを再利用できることです(すべてのアクティビティは、フラグメントごとに独自のOnFragmentInteractionListenerを持つことができます)。

しかし、フラグメントが1つのタイプのアクティビティのみにアタッチされることが確実で、そのアクティビティと通信したい場合はどうすればよいですか?

次に、その冗長性のためにOnFragmentInteractionListenerを使用したくない場合は、以下を使用してアクティビティメソッドにアクセスできます。

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

これはほとんどの場合に機能しますが、たとえばasynctaskのpostExecuteメソッドで、呼び出しが行われている間にフラグメントがアクティビティから切り離されている場合、getActivity()はnullを返すことがあります。 asyncTaskが完了する前に、nullポインタ例外が発生します。このため、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;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.