忘れん坊のサンタクロース

iOS,Androidアプリ開発についての忘備録です。技術関連情報など掲載していこうかと考えています。時々関係ないことも書くかも。。

Fragmentでのイベントハンドリング

FragmentにButtonを置いてみたけどレイアウトで定義したButtonのonClickがFragmentで実装できないじゃん!!とかいろいろな問題に直面したので頭を冷やして解決方法をメモします。

まず上記の例に関して言えば、そのFragmentを持つActivityで実装することができます。しかし、Fragmentが複数ある場合、FragmentそれぞれのウィジェットをActivityで実装しなければならないというスパゲティのいい匂いがしてきます。つまりはFragmentのイベントはFragmentで処理したいじゃん??、、では解決方法について説明します。

IDでオブジェクトを取得して、リスナーを登録する

これは超簡単!
まずレイアウトで定義したウィジェットのIDからオブジェクトを取得します。
その後、リスナーを登録してあげれば完成です。

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    View view = (View)inflater.inflate(R.layout.fragment_main, container, false);

    //IDからオブジェクトを取得
    Button button  = (Button)view.findViewById(R.id.button);

    //リスナーを登録
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.v("button:", "clicked!");
        }
    });

    return view;
}

これでButtonをタップするとログにメッセージが表示されると思います。

アクティビティへのイベントコールバックを作成する

こちらは公式サイトのAPIガイドにある方法なのですが、Activityの何かしらの情報の取得やUIの操作をするのであれば以下のように実装しましょう。

まずはFragmentにInterfaceを定義しましょう。

public class MainFragment extends Fragment {

 //Activityにイベントを通知する役割を持つ
    public interface OnClickListener {
        void onClick();
    }
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        return inflater.inflate(R.layout.fragment_main, container, false);
    }
}

このOnClickListenerをActivityのインターフェースにしてonClickを実装します。

public class MainActivity extends AppCompatActivity implements MainFragment.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public void onClick() {
        Log.v("onClick:", "想いよ届け");
    }
}

その後、Fragmentに戻ってOnClickListenerの変数をフィールド持ち、Fragmentのライフサイクルの一つであるonAttach()を以下のように実装します。

public class MainFragment extends Fragment {

    OnClickListener _clickListener;

    public interface OnClickListener {
        void onClick();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        return inflater.inflate(R.layout.fragment_main, container, false);
    }

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

ちなみにonAttach()はFragmentをActivityに追加する際に呼ばれるメソッドです。メモメモ、、。
onAttach()内ではActivityがOnClickListenerを実装しているかをハンドリングし、成功すればそれをフィールドに持つようにしています。これで先ほどのIDでオブジェクトを取得する方法と合わせて、

    //リスナーを登録
 button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
                _clickListener.onClick();
        }
    });

これでFragmentのButtonを押した際のイベントがActivityに通知されるようになります。
この方法、Buttonに限らずListViewのタッチイベント(むしろこっちの方が多いかも)などでも使用する場面があると思うのでいろいろと応用がきくと思います。