Связь между вложенными фрагментами в Android

Я недавно узнал Как сделать вложенные фрагменты в Android. Хотя я не знаю, как должно происходить общение.

enter image description here

читать фрагмент коммуникации документация Я знаю, что

все сообщение фрагмент-к-фрагмента сделано через связанное Деятельность. Два фрагмента никогда не должны общаться напрямую.

это имеет смысл для фрагментов братьев и сестер в рамках деятельности, но это не имеет большого смысла для общения между фрагментами родителей и детей. Должен ли я пройти весь путь до действия только для дочернего фрагмента, чтобы поговорить с родительским фрагментом? Если ответ простой "да", то я могу это сделать. Если это "нет", то как будет выглядеть дизайн кода?

Я вижу вложенный фрагмент документации что можно использовать getParentFragment() получить ссылку на родителя фрагмент. Значит ли это, что ребенок должен напрямую общаться с родителем? Это кажется противоположным тому, что поощряется нормальным фрагментом, сообщающимся с родительской активностью.

2 ответов


следуя совету Рахула шармы в комментариях, я использовал обратные вызовы интерфейса для связи от дочернего фрагмента к родительскому фрагменту и к деятельности. Я тоже отправил этот ответ в Code Review. Я беру номера-ответа нет (на момент написания этой статьи), чтобы быть признаком того, что нет никаких серьезных проблем с этим узором. Мне кажется, это согласуется с общим руководством, данным в официальном коммуникационный фрагмент 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;
    }

}

хотя ответ @Suragch правильный, но я хочу добавить другой способ передачи данных между Fragments или Activity. Неважно, что это Activity или Fragment вы можете передать данные с шины событий в 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-Регистрация и отмена регистрации событий: чтобы иметь возможность получать события, класс должен зарегистрироваться / отменить регистрацию для шины событий. Лучшее место для Activities и Fragments is 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()));

больше документации и примеров можно найти здесь. Есть также другие библиотеки, такие как:Отто