2016-09-14 107 views
8

我最近學會了how to make nested fragments in Android。不過,我不知道溝通應該如何發生。Android中嵌套片段之間的通信

enter image description here

從閱讀fragment communication documentation我知道

所有片段到片段通訊都是通過相關 做過的事情。兩個碎片不應該直接通信。

這對於活動中的兄弟片段是有意義的,但它對於父子片段通信沒有多大意義。我是否需要一直到活動只是爲了讓孩子片段與父母片段交談?如果答案是一個簡單的「是」,那麼我可以做到這一點。如果它是「否」,那麼代碼設計會是什麼樣子?

我在Nested Fragment documentation看到,可以使用getParentFragment()來獲得對父片段的引用。那麼這是否意味着孩子應該直接與父母溝通?這與正常片段與父活動進行通信所鼓勵的情況相反。

+0

您必須創建ParentFragment實例方法 –

+2

您可以通過在您的應用程序中使用**回調**來實現此目的。 –

+0

您也可以選擇使用EventBus,如[Otto](http://square.github.io/otto/)或[GreenRobot](https://github.com/greenrobot/EventBus) –

回答

12

按照Rahul Sharma在評論中的建議,我使用接口回調從Child Fragment傳遞給Parent Fragment和Activity。我也submitted this answer to Code Review。我在那裏寫了一個沒有回答的問題(在寫這篇文章的時候),表明這個設計模式沒有大問題。在我看來,它與官方fragment communication docs中的一般指導一致。

實施例項目

下面的示例項目擴展中的問題給出的例子。它具有按鈕,用於啓動從片段到活動以及從子片段到父片段的向上通信。

我成立了項目佈局是這樣的:

enter image description here

主要活動

的活動實現從兩個片段聽衆,以便它可以從他們那裏得到的消息。

可選TODO:如果活動想要啓動與片段的通信,它可以直接引用它們,然後調用其公共方法之一。

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:如果家長片段想要發起與兒童片段的通信,它可以直接引用它,然後調用其公共方法之一。

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; 
    } 

} 

兒童片段

童片段定義了的活性和對父片段偵聽器接口。如果兒童片段只需要與其中一個進行通信,則可以刪除其他接口。

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

好帖子!我只是想補充說,你可以通過使用方法獲取對片段或ChildFragment的引用來實現你的兩個「Optional TODO:」:Activity.onAttachFragment(片段片段)和Fragment.onAttachFragment(片段childFragment) – AppiDevo

+0

@Suragch無法完成EventBus ???你的答案是完美的,但你不同意這種模式增加了應用程序的複雜性(與事件總線相比) –

+0

@MiladFaridnia,可能是。我沒有使用EventBus。如果你想添加一個答案,我會有興趣看到它的一個例子。 – Suragch

3

雖然@ Suragch的答案是正確的,但我想添加另一種方式來FragmentsActivity之間傳遞數據。無論它是一個ActivityFragment您可以在3個步驟,通過與事件總線數據:

1-定義事件(消息):

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

2 - 註冊和註銷的活動:爲了能夠接收事件,班級必須註冊/註銷事件總線。對於ActivitiesFragments最好的地方是onStart()onStop()

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

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

爲了能夠接受,你必須訂閱該事件的事件。爲此,請將@Subscribe註釋添加到您班級中的某個方法中。

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

3-發佈一個事件

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

更多文檔和例子可以發現Here。 還有其他類似庫:Otto

+1

感謝您花時間添加答案。人們可以比較另一種選擇。 – Suragch