2013/11/14

[Android]FragmentからActivityへのイベントの通知方法

Fragment から Activity へのイベントの通知方法のベストな方法を紹介します。
@vvakameさんの CallbackPicker のことなんですが、
文書化されていないようなので記事を書いておきます。
CallbackPicker よりも CallbackProvider の方が
なんとなくしっくり来たので勝手に名前を変えました。

とりあえず以下の方法を理解していることが前提です。
Y.A.M の 雑記帳: Fragment から Activity にコールバックする方法
リンク先の内容を簡単に説明すると、
onAttach で activity がリスナーを実装しているかチェックしてコールバックメソッドを呼ぼう!ってことです。

今回の方法はその方法を1段深くしたような形になっています。
public class MainActivity extends FragmentActivity implements MainFragmnetCallbackProvider {

    @Override
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);

        MainFragment fragment = MainFragment.newInstance();
        getFragmentManager().beginTransaction().add(android.R.id.content, fragment, MainFragment.TAG).commit();
    }

    @Override
    public MainFragmnetCallback getMainFragmnetCallback(){
        return new MainFragmnetCallback(){
            @Override
            public void onAttach(MainFragmnet mainFragmnet){

            }
        }
    }
}
public class MainFragment extends Fragment {

    public static final String   TAG = MainFragment.class.getSimpleName();
    private MainFragmentCallback mCallback;

    public static interface MainFragmentCallbackProvider {

        public MainFragmentCallback getMainCallback();

    }

    public static interface MainFragmentCallback {

        public void onAttach(MainFragment mainFragment);

    }

    public static MainFragment newInstance(){
        MainFragment fragment = new MainFragment();
        Bundle args = new Bundle();
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onAttach(Activity activity){
        super.onAttach(activity);

        if(activity instanceof MainFragmentCallbackProvider == false){
            throw new ClassCastException(activity.getLocalClassName() + " must implements " + MainFragmentCallbackProvider.class.getSimpleName());
        }
        MainFragmentCallbackProvider provider = (MainFragmentCallbackProvider)activity;
        mCallback = provider.getMainCallback();
        mCallback.onAttach(this);
    }

}
MainFragment に2つの interface を用意して Activity で MainFragmnetCallbackProvider を実装します。
MainFragmnetCallbackProvider は MainFragmentCallback を返す getMainCallback() のみを提供します。
getMainCallback() で MainFragmentCallback を 匿名インナークラスとして書いてあげれば
MainFragment に関するイベントはその匿名インナークラス内に集約されます(←重要!※)。
あとは MainFragment で MainFragmentCallback のメソッド宣言に好きなコールバックを書いてあげるだけ!

※ MainFragmnetCallbackProvider を使うことでコールバックメソッドのスコープが小さくなるから便利ですよ!
MainFragment のちょっとしたリスナーだけだったらいいかもしれないけど
MainActivity で 他にも Fragment を扱ったりするとコールバックメソッドが入り乱れて見難くなります。
7, 8個くらい Fragment を Activity にくっつけたときはコレじゃなかったら可読性低くて大変だったかも。

interface の名前的に自由にコールバックメソッドを追加しやすかったのも良いと思いました。
Fragment 同士の連携も Activity に一旦コールバックして
Activity から Fragment のメソッド実行すれば良いので
お互いの実装に関わりを持たずにコールバックを投げておけば良いというのも開発しやすいポイントです。

この MainFragment は Fragment クラスの拡張を作る際のテンプレにしておきたいので記事を書きました。

7 件のコメント :

  1. 始めまして. Java+Androidともに初心者です.
    Scrollable Tabs + Swipeで開発しているのですが,3点質問させて下さい.

    getFragmentManager().beginTransaction().add(android.R.id.content, fragment, MainFragment.TAG).commit();
    }はフラグメントを設定しているだけでなくても大丈夫でしょうか? これを入れるとMainFragmentが重なって表示されます.

    2つめは,mCallback.onAttach(this);をいれると強制終了されてしまいます.

    3つめ,肝心な変数の引き渡しがうまくできません. GenderとAgeという二つの変数をMainFragmentからMainActivityに渡したいのですが…

    お忙しいと思うのですがご教授いただければ幸いです.

    返信削除
    返信
    1. はじめまして。
      質問を頂きましたが、こちらでどのようになっているのか確認できないので回答することは難しいかと思います。
      強制終了する場合はそのエラーを元に検索して頂いて解決していくしかないかと思います。
      3つ目の質問の変数の引き渡しですが、記事中のソースコードでいうところの MainFragmentCallback に変数を引き渡すためのメソッドを定義して呼び出せば MainActivity へ変数を引き渡すことが可能かと思います。

      削除
  2. 差し出がましいお願いとは存じますが,用例お願いできませんでしょうか?
    Google先生に聞いても上手くいかず,悩んでおります.
    お忙しいとは存じますが,宜しければお願いします.

    返信削除
  3. 3点目の変数の引き渡しでよろしいでしょうか?(他の2点はソースが無いのでわかりません)
    interfaceにメソッドを定義してFragmentで変更時にメソッドを呼び出してあげれば
    Activity側で変数の値を受け取ることができます。

    public class MainFragment extends Fragment {
     
        public static interface MainFragmentCallback {
     
            public void onGenderChanged(Gender gender);

            public void onAgeChanged(int age);
     
        }

        private void setGender(Gender gender){
            mCallback.onGenderChanged(gender);
        }

        private void setAge(int age){
            mCallback.onAgeChanged(age);
        }
     
    }

    public class MainActivity extends Activity implements MainFragmentCallbackProvider {
        
        @Override
        public MainFragmentCallback getMainFragmentCallback(){
            return new MainFragmentCallback(){
                @Override
                public void onGenderChanged(Gender gender){
                    
                }
                @Override
                public void onAgeChanged(int age){
                    
                }
            }
        }
    }

    返信削除
  4. このコメントは投稿者によって削除されました。

    返信削除
    返信
    1. とりあえず上手くいったようで良かったです。
      PagerStripでの問題はお力になれませんが、
      試行錯誤して頑張ってください。
      何かあればまた。
      それでは失礼します。

      削除
  5. 誠に有り難うございます.
    Testは上手くいきました.
    ただ,FragmentPagerAdapter(訂正です.)下ではライフサイクルの問題か上手くいっていません.
    もう暫く色々実験してみます.
    本当に有り難うございます.

    返信削除

タグ(RSS)