2016-09-14 18 views
8

Gần đây tôi đã học được how to make nested fragments in Android. Tôi không biết làm thế nào giao tiếp là vụ phải xảy ra, mặc dù.Giao tiếp giữa các đoạn lồng nhau trong Android

enter image description here

Từ đọc fragment communication documentation Tôi biết rằng

Tất cả các thông tin liên lạc Fragment-to-Fragment được thực hiện thông qua việc liên quan đến Hoạt động. Hai mảnh vỡ không bao giờ nên giao tiếp trực tiếp.

Điều này có ý nghĩa đối với các mảnh anh chị em trong một hoạt động, nhưng nó không có ý nghĩa nhiều đối với giao tiếp đoạn con-cha-con. Tôi có cần phải đi tất cả các con đường lên đến Activity chỉ dành cho Fragment Child để nói chuyện với Parent Fragment không? Nếu câu trả lời đơn giản là "có" thì tôi có thể làm điều đó. Nếu nó là "không", thì thiết kế mã sẽ trông như thế nào?

Tôi thấy trong số Nested Fragment documentation mà người dùng có thể sử dụng getParentFragment() để nhận tham chiếu đến phân đoạn gốc. Vậy điều đó có nghĩa là đứa trẻ nên giao tiếp trực tiếp với cha mẹ? Điều đó có vẻ trái ngược với những gì được khuyến khích với một đoạn bình thường giao tiếp với hoạt động của phụ huynh.

+0

Bạn phải tạo phương thức Ví dụ về bố cục cha mẹ –

+2

Bạn có thể thực hiện việc này bằng cách sử dụng ** Gọi lại ** trong ứng dụng của mình. –

+0

Bạn cũng có tùy chọn sử dụng EventBus như [Otto] (http://square.github.io/otto/) hoặc [GreenRobot] (https://github.com/greenrobot/EventBus) –

Trả lời

12

Làm theo lời khuyên của Rahul Sharma trong các nhận xét, tôi đã sử dụng các cuộc gọi lại giao diện để liên lạc từ Phân đoạn con đến Phân đoạn dành cho phụ huynh và đến Hoạt động. Tôi cũng submitted this answer to Code Review. Tôi lấy câu trả lời không có (tại thời điểm viết bài này) là một dấu hiệu cho thấy không có vấn đề gì lớn với mẫu thiết kế này. Dường như với tôi để phù hợp với hướng dẫn chung được đưa ra trong chính thức fragment communication docs.

Dự án ví dụ

Dự án ví dụ sau mở rộng ví dụ được đưa ra trong câu hỏi. Nó có các nút bắt đầu truyền thông từ các mảnh vỡ đến hoạt động và từ Child Fragment đến Parent Fragment.

tôi thiết lập bố trí dự án như thế này:

enter image description here

Hoạt động chính

Các Hoạt động thực hiện các thính giả của cả hai mảnh để nó có thể nhận được tin nhắn từ họ.

TODO tùy chọn: Nếu Hoạt động muốn bắt đầu giao tiếp với các đoạn, nó chỉ có thể tham chiếu trực tiếp đến chúng và sau đó gọi một trong các phương pháp công khai của chúng.

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

Fragment Chánh

Các Fragment Chánh thực hiện người nghe từ Fragment trẻ em để nó có thể nhận tin nhắn từ nó.

TODO tùy chọn: Nếu Phân đoạn gốc muốn bắt đầu giao tiếp với Phân đoạn trẻ em, nó chỉ có thể tham chiếu trực tiếp đến nó và sau đó gọi một trong các phương pháp công khai của nó.

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

} 

Child Fragment

Các Fragment Child định nghĩa giao diện người nghe cho cả hoạt động và cho Fragment phụ huynh. Nếu Child Fragment chỉ cần giao tiếp với một trong số chúng, thì giao diện khác có thể bị loại bỏ.

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

Bài đăng tuyệt vời! Tôi chỉ muốn thêm rằng cả hai "Tùy chọn TODO:" có thể đạt được bằng cách tham chiếu đến Phân đoạn hoặc ChildFragment bằng các phương thức: Activity.onAttachFragment (Fragment fragment) và Fragment.onAttachFragment (Fragment childFragment) – AppiDevo

+0

@Suragch Không thể được thực hiện bởi EventBus ??? Câu trả lời của bạn là hoàn hảo nhưng bạn không đồng ý rằng mẫu này thêm độ phức tạp hơn cho ứng dụng (so với Event Bus) –

+0

@MiladFaridnia, Có thể. Tôi đã không sử dụng EventBus. Tôi muốn được nhìn thấy một ví dụ về nó nếu bạn muốn thêm một câu trả lời. – Suragch

3

Mặc dù câu trả lời @ Suragch là đúng, Nhưng tôi muốn thêm một cách khác để truyền dữ liệu giữa Fragments hoặc Activity. Không có vấn đề nó là một Activity hoặc Fragment bạn có thể truyền dữ liệu với bus sự kiện trong 3 bước:

1- Xác định một sự kiện (message):

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

2- Đăng ký và Unregister cho các sự kiện: Để có thể nhận sự kiện, lớp học phải đăng ký/hủy đăng ký xe buýt sự kiện. Nơi tốt nhất cho ActivitiesFragmentsonStart()onStop()

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

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

Để có thể nhận được một sự kiện, bạn phải đăng ký sự kiện đó. Để làm điều đó, hãy thêm chú thích @Subscribe vào một trong các phương pháp của bạn trong lớp học của bạn.

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

3- bài viết một sự kiện

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

Nhiều tài liệu và các ví dụ có thể được tìm thấy Here. Ngoài ra còn có các thư viện khác như: Otto

+1

Cảm ơn bạn đã dành thời gian để thêm câu trả lời này. Nó sẽ hữu ích cho mọi người để so sánh một tùy chọn khác. – Suragch

Các vấn đề liên quan