2016-09-14 11 views
8

私は最近how to make nested fragments in Androidを学びました。しかし、どうやってコミュニケーションが起こるのか分かりません。Androidのネストされたフラグメント間の通信

enter image description here

私は

すべてのフラグメント・ツー・フラグメントの通信が関連する 活動を通して行われることを知っているfragment communication documentationを読んでから。 2つのフラグメントは決して直接通信するべきではありません。

これは、アクティビティ内の兄弟フラグメントには意味がありますが、親子フラグメント通信にはあまり意味がありません。子どもの断片が親の断片と話すだけで、私はアクティビティまですべて行く必要がありますか?答えが単純な「はい」の場合、私はそれを行うことができます。 「いいえ」の場合、コードデザインはどのように見えますか?

Nested Fragment documentationでは、getParentFragment()を使用して親フラグメントへの参照を得ることができます。それは子供が親と直接コミュニケーションを取るべきであることを意味しますか?それは、親アクティビティと通信している通常のフラグメントでは奨励されていることとは反対に思えます。

+0

ParentFragmentインスタンスメソッドを作成する必要があります –

+2

これはアプリで**コールバック**を使用して行うことができます。 –

+0

[Otto](http://square.github.io/otto/)や[GreenRobot](https://github.com/greenrobot/EventBus)のようなEventBusを使用するオプションもあります –

答えて

12

コメントの中でRahul Sharmaのアドバイスに従って、私はインターフェースコールバックを使用して、Child FragmentからParent FragmentおよびActivityに通信しました。私もsubmitted this answer to Code Reviewです。私は、このデザインパターンに大きな問題はないという兆候であることを、この執筆時点で無回答とみなしています。公式のfragment communication docsに記載されている一般的なガイダンスと一致するように思われます。

例プロジェクト

次のプロジェクト例は、質問の例を展開しています。これは、フラグメントからアクティビティへ、および子フラグメントから親フラグメントへの上向きの通信を開始するボタンを備えています。それは彼らからのメッセージを得ることができるように活動が両断片からリスナーを実装し

enter image description here

主な活動

は、私はこのようなプロジェクトのレイアウトを設定します。

オプションTODO:アクティビティがフラグメントとの通信を開始したい場合、アクティビティがそれらのフラグメントへの直接参照を取得し、パブリックメソッドの1つを呼び出すことができます。それはそれからメッセージを受信できるように

import android.support.v4.app.FragmentTransaction; 
import android.support.v7.app.AppCompatActivity; 
import android.os.Bundle; 
import android.util.Log; 

public class MainActivity extends AppCompatActivity implements ParentFragment.OnFragmentInteractionListener, ChildFragment.OnChildFragmentToActivityInteractionListener { 

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

     FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); 
     ft.replace(R.id.parent_fragment_container, new ParentFragment()); 
     ft.commit(); 
    } 

    @Override 
    public void messageFromParentFragmentToActivity(String myString) { 
     Log.i("TAG", myString); 
    } 

    @Override 
    public void messageFromChildFragmentToActivity(String myString) { 
     Log.i("TAG", myString); 
    } 
} 

親フラグメント

親フラグメントは子供フラグメントからリスナーを実装しています。

オプションTODO:親フラグメントが子フラグメントとの通信を開始したい場合は、それを直接参照するだけで、パブリックメソッドの1つを呼び出すことができます。

import android.content.Context; 
import android.os.Bundle; 
import android.support.v4.app.Fragment; 
import android.support.v4.app.FragmentTransaction; 
import android.util.Log; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 


public class ParentFragment extends Fragment implements View.OnClickListener, ChildFragment.OnChildFragmentInteractionListener { 


    // **************** start interesting part ************************ 

    private OnFragmentInteractionListener mListener; 


    @Override 
    public void onClick(View v) { 
     mListener.messageFromParentFragmentToActivity("I am the parent fragment."); 
    } 

    @Override 
    public void messageFromChildToParent(String myString) { 
     Log.i("TAG", myString); 
    } 

    public interface OnFragmentInteractionListener { 
     void messageFromParentFragmentToActivity(String myString); 
    } 

    // **************** end interesting part ************************ 



    @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 View onCreateView(LayoutInflater inflater, ViewGroup container, 
          Bundle savedInstanceState) { 
     View view = inflater.inflate(R.layout.fragment_parent, container, false); 
     view.findViewById(R.id.parent_fragment_button).setOnClickListener(this); 
     return view; 
    } 

    @Override 
    public void onViewCreated(View view, Bundle savedInstanceState) { 
     Fragment childFragment = new ChildFragment(); 
     FragmentTransaction transaction = getChildFragmentManager().beginTransaction(); 
     transaction.replace(R.id.child_fragment_container, childFragment).commit(); 
    } 

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

} 

子フラグメント

子断片が活性の両方のために親フラグメントのリスナーインターフェースを定義します。子フラグメントがそれらのうちの1つと通信する必要があるだけであれば、他のインタフェースを削除することができます。

import android.content.Context; 
import android.os.Bundle; 
import android.support.v4.app.Fragment; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 


public class ChildFragment extends Fragment implements View.OnClickListener { 


    // **************** start interesting part ************************ 

    private OnChildFragmentToActivityInteractionListener mActivityListener; 
    private OnChildFragmentInteractionListener mParentListener; 

    @Override 
    public void onClick(View v) { 
     switch (v.getId()) { 
      case R.id.child_fragment_contact_activity_button: 
       mActivityListener.messageFromChildFragmentToActivity("Hello, Activity. I am the child fragment."); 
       break; 
      case R.id.child_fragment_contact_parent_button: 
       mParentListener.messageFromChildToParent("Hello, parent. I am your child."); 
       break; 
     } 
    } 

    public interface OnChildFragmentToActivityInteractionListener { 
     void messageFromChildFragmentToActivity(String myString); 
    } 

    public interface OnChildFragmentInteractionListener { 
     void messageFromChildToParent(String myString); 
    } 

    @Override 
    public void onAttach(Context context) { 
     super.onAttach(context); 

     // check if Activity implements listener 
     if (context instanceof OnChildFragmentToActivityInteractionListener) { 
      mActivityListener = (OnChildFragmentToActivityInteractionListener) context; 
     } else { 
      throw new RuntimeException(context.toString() 
        + " must implement OnChildFragmentToActivityInteractionListener"); 
     } 

     // check if parent Fragment implements listener 
     if (getParentFragment() instanceof OnChildFragmentInteractionListener) { 
      mParentListener = (OnChildFragmentInteractionListener) getParentFragment(); 
     } else { 
      throw new RuntimeException("The parent fragment must implement OnChildFragmentInteractionListener"); 
     } 
    } 

    // **************** end interesting part ************************ 



    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
          Bundle savedInstanceState) { 
     View view = inflater.inflate(R.layout.fragment_child, container, false); 
     view.findViewById(R.id.child_fragment_contact_activity_button).setOnClickListener(this); 
     view.findViewById(R.id.child_fragment_contact_parent_button).setOnClickListener(this); 
     return view; 
    } 

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

} 
+0

すばらしい記事!私はちょうどあなたの両方の "オプションのTODO:"メソッドを使用してFragmentまたはChildFragmentへの参照を取得することによって達成することができます:Activity.onAttachFragment(フラグメントフラグメント)とFragment.onAttachFragment(フラグメントchildFragment) – AppiDevo

+0

@Suragch EventBus ???あなたの答えは完璧ですが、このパターンが(イベントバスと比較して)アプリケーションに複雑さを加えることに同意しないでください。 –

+0

@MiladFaridnia、可能性があります。私はEventBusを使用していません。あなたが答えを加えたいと思うなら、私はそれの例を見るのに興味があります。 – Suragch

3

@ Suragchの答えは正しいです、しかし、私はFragmentsまたはActivityの間でデータを渡すための別の方法を追加したいが。それはあなたが3つの段階でイベントバスにデータを渡すことができActivityFragmentだどんなに:イベントの

public class OrderMessage { 
    private final long orderId; 
    /* Additional fields if needed */ 
    public OrderMessage(long orderId) { 
     this.orderId = orderId; 
    } 
    public long getOrderId() { 
     return orderId; 
    } 
} 

2 - 登録と登録解除:

1-(メッセージ)イベントを定義します。イベントを受け取るには、イベントバスの登録/登録解除が必要です。 ActivitiesFragmentsのための最高の場所は、あなたがそのイベントをサブスクライブする必要がイベントを受信できるようにするにonStart()onStop()

@Override 
public void onStart() { 
    super.onStart(); 
    EventBus.getDefault().register(this); 
} 

@Override 
public void onStop() { 
    EventBus.getDefault().unregister(this); 
    super.onStop(); 
} 

です。これを行うには、クラス内のいずれかのメソッドに@Subscribeアノテーションを追加します。その他のドキュメントとサンプルがHere見つけることができ

@Subscribe(threadMode = ThreadMode.MAIN) 
    public void onMessage(OrderMessage message){ 
     /* Do something for example: */ 
     getContractDetails(message.getOrderId()); 
    } 

、3-ポストイベント

EventBus.getDefault().post(new OrderMessage(recievedDataFromWeb.getOrderId())); 

。 他のライブラリもあります:Otto

+1

この回答を追加していただきありがとうございます。人々が別のオプションを比較するのに便利です。 – Suragch

関連する問題