2016-09-14 79 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