2016-09-14 2 views
8

Недавно я узнал how to make nested fragments in Android. Однако я не знаю, как должно случиться.Связь между вложенными фрагментами в Android

enter image description here

От чтения fragment communication documentation Я знаю, что

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

Это имеет смысл для фрагментов сестры в рамках деятельности, но это не имеет особого смысла для связи между родительским и детским фрагментами. Нужно ли мне полностью перейти к Управлению только для детского фрагмента, чтобы поговорить с родительским фрагментом? Если ответ - это просто «да», то я могу это сделать. Если это «нет», то как выглядит код?

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

+0

Необходимо создать метод экземпляра ParentFragment Instance –

+2

Вы можете сделать это, используя ** Callbacks ** в своем приложении. –

+0

У вас также есть возможность использовать EventBus, например [Otto] (http://square.github.io/otto/) или [GreenRobot] (https://github.com/greenrobot/EventBus) –

ответ

12

Следуя совету Rahul Sharma в комментариях, я использовал обратные вызовы интерфейса для связи из Детского фрагмента с родительским фрагментом и Activity. Я также submitted this answer to Code Review. Я беру там не ответ (на момент написания этой статьи), чтобы быть признаком отсутствия серьезных проблем с этим шаблоном проектирования. Мне кажется, что я согласен с общим руководством, данным официальным fragment communication docs.

Пример проекта

В следующем примере проект расширяет пример, приведенный в вопросе. У этого есть кнопки, которые инициируют восходящую связь от фрагментов к активности и от Детского Фрагмента до родительского фрагмента.

Я создал макет проекта, как это:

enter image description here

Основная активность

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

Необязательный TODO: Если Activity хотел инициировать связь с фрагментами, он мог просто получить прямую ссылку на них, а затем вызвать один из своих общедоступных методов.

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

Отличные сообщения! Я просто хочу добавить, что ваши оба варианта «Необязательный TODO:» могут быть достигнуты путем получения ссылки на фрагмент или ChildFragment с помощью методов: Activity.onAttachFragment (фрагмент фрагмента) и Fragment.onAttachFragment (фрагмент childFragment) – AppiDevo

+0

@Suragch Не удалось выполнить EventBus ??? Ваш ответ прекрасен, но не согласны ли вы с тем, что этот шаблон добавляет больше сложности в приложение (по сравнению с Event Bus). –

+0

@MiladFaridnia, возможно. Я не использовал EventBus. Мне бы хотелось увидеть пример, если вы хотите добавить ответ. – Suragch

3

Хотя @ ответ 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 является 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

Смежные вопросы