Я пытаюсь передать данные между двумя фрагментами в моей программе. Это просто простая строка, которая хранится в списке. Список публикуется во фрагментах A, и когда пользователь щелкает элемент списка, мне нужно, чтобы он отображался во фрагменте B. Похоже, что поставщик контента поддерживает только идентификаторы, поэтому это не сработает. Какие-либо предложения?
Как передавать данные между фрагментами
Ответы (13)
Почему бы вам не использовать Bundle. Из вашего первого фрагмента вот как его настроить:
Fragment fragment = new Fragment();
Bundle bundle = new Bundle();
bundle.putInt(key, value);
fragment.setArguments(bundle);
Затем во втором фрагменте извлеките данные, используя:
Bundle bundle = this.getArguments();
int myInt = bundle.getInt(key, defaultValue);
В Bundle есть методы put для множества типов данных. См. http://developer.android.com/reference/android/os/Bundle.html
Если вы используете Roboguice, вы можете использовать EventManager в Roboguice для передачи данных без использования Activity в качестве интерфейса. Это довольно чисто ИМО.
Если вы не используете Roboguice, вы также можете использовать Otto в качестве шины событий: http://square.github.com/otto/
Обновление 20150909: теперь вы также можете использовать шину событий Green Robot или даже RxJava. Зависит от вашего варианта использования.
Из Fragment
документации:
Часто вам нужно, чтобы один фрагмент взаимодействовал с другим, например, для изменения содержимого на основе пользовательского события. Вся связь между фрагментами осуществляется через связанное действие. Два Фрагмента никогда не должны общаться напрямую.
Поэтому я предлагаю вам ознакомиться с документами по базовому обучению фрагментам в документации. Они довольно всеобъемлющие с примером и пошаговым руководством.
Допустим, у вас есть Activity AB, который управляет Frag A и Fragment B. Внутри фрагмента A вам нужен интерфейс, который Activity AB может реализовать. В примере кода Android они имеют:
private Callbacks mCallbacks = sDummyCallbacks;
/*Интерфейс обратного вызова, который должны реализовать все действия, содержащие этот фрагмент. Этот механизм позволяет уведомлять действия о выборе элементов. */
public interface Callbacks {
/*Callback for when an item has been selected. */
public void onItemSelected(String id);
}
/*A dummy implementation of the {@link Callbacks} interface that does nothing. Used only when this fragment is not attached to an activity. */
private static Callbacks sDummyCallbacks = new Callbacks() {
@Override
public void onItemSelected(String id) {
}
};
Интерфейс обратного вызова помещается внутрь одного из ваших фрагментов (скажем, фрагмента A). Я думаю, что цель этого интерфейса обратных вызовов похожа на вложенный класс внутри Frag A, который может реализовать любое действие. Таким образом, если фрагмент A был телевизором, CallBacks — это пульт дистанционного управления телевизором (интерфейс), который позволяет использовать фрагмент A для действия AB. Я могу ошибаться в деталях, потому что я нуб, но моя программа отлично работала на всех размерах экрана, и это то, что я использовал.
Итак, внутри Фрагмента А у нас есть: (Я взял это из примеров программ Android)
@Override
public void onListItemClick(ListView listView, View view, int position, long id) {
super.onListItemClick(listView, view, position, id);
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
mCallbacks.onItemSelected(DummyContent.ITEMS.get(position).id);
//mCallbacks.onItemSelected( PUT YOUR SHIT HERE. int, String, etc.);
//mCallbacks.onItemSelected (Object);
}
А внутри Activity AB мы переопределяем метод onItemSelected:
public class AB extends FragmentActivity implements ItemListFragment.Callbacks {
//...
@Override
//public void onItemSelected (CATCH YOUR SHIT HERE) {
//public void onItemSelected (Object obj) {
public void onItemSelected(String id) {
//Pass Data to Fragment B. For example:
Bundle arguments = new Bundle();
arguments.putString(“FragmentB_package”, id);
FragmentB fragment = new FragmentB();
fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction().replace(R.id.item_detail_container, fragment).commit();
}
Таким образом, внутри Activity AB вы в основном бросаете все в Bundle и передаете его B. Если вы не знаете, как использовать Bundle, посмотрите класс.
В основном я использую пример кода, предоставленный Android. Тот, что с материалом DummyContent. Когда вы создаете новый пакет приложений для Android, он называется MasterDetailFlow.
1- Первый способ определить интерфейс
public interface OnMessage{
void sendMessage(int fragmentId, String message);
}
public interface OnReceive{
void onReceive(String message);
}
2- В вашей деятельности реализуется интерфейс OnMessage
public class MyActivity implements OnMessage {
...
@Override
public void sendMessage(int fragmentId, String message){
Fragment fragment = getSupportFragmentManager().findFragmentById(fragmentId);
((OnReceive) fragment).sendMessage();
}
}
3- В вашем фрагменте реализуйте интерфейс OnReceive
public class MyFragment implements OnReceive{
...
@Override
public void onReceive(String message){
myTextView.setText("Received message:" + message);
}
}
Это стандартная версия обработки передачи сообщений между фрагментами.
Другой способ передачи данных между фрагментами — использование шины событий.
1- Регистрация/отмена регистрации в шине событий
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
2- Определите класс события
public class Message{
public final String message;
public Message(String message){
this.message = message;
}
}
3- Разместите это событие в любом месте вашего приложения.
EventBus.getDefault().post(new Message("hello world"));
4- Подпишитесь на это событие, чтобы получить его в своем фрагменте.
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(Message event){
mytextview.setText(event.message);
}
Дополнительные подробности, варианты использования и пример проекта о мероприятии шаблон автобуса.
В моем случае мне пришлось отправить данные в обратном направлении из FragmentB->FragmentA, следовательно, намерения не были вариантом, поскольку фрагмент уже был бы инициализирован Все, хотя все вышеприведенные ответы звучат хорошо, что для реализации требуется много шаблонного кода, поэтому я выбрал гораздо более простой подход с использованием LocalBroadcastManager, он точно делает то же, что и выше сказал, но без всего противного стандартного кода. Пример приведен ниже.
При отправке фрагмента (фрагмент B)
public class FragmentB {
private void sendMessage() {
Intent intent = new Intent("custom-event-name");
intent.putExtra("message", "your message");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
}
И во фрагменте сообщения для получения (ФРАГМЕНТ A)
public class FragmentA {
@Override
public void onCreate(Bundle savedInstanceState) {
...
// Register receiver
LocalBroadcastManager.getInstance(this).registerReceiver(receiver,
new IntentFilter("custom-event-name"));
}
// This will be called whenever an Intent with an action named "custom-event-name" is broadcasted.
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String message = intent.getStringExtra("message");
}
};
}
Надеюсь, это поможет кому-то
Это зависит от того, как структурирован фрагмент. Если вы можете иметь некоторые методы статического класса Fragment Class B, а также статического целевого объекта TextView, вы можете вызвать метод непосредственно в классе фрагмента A. Это лучше, чем слушатель, поскольку метод выполняется мгновенно, и мы не Не нужно иметь дополнительную задачу, которая выполняет прослушивание на протяжении всей деятельности. См. пример ниже:
Fragment_class_B.setmyText(String yourstring);
Во фрагменте B вы можете определить метод как:
public static void setmyText(final String string) {
myTextView.setText(string);
}
Просто не забудьте установить myTextView как статический во фрагменте B и правильно импортировать класс Fragment B во фрагмент A.
Только что сделал процедуру в моем проекте недавно, и это сработало. Надеюсь, это помогло.
вы можете прочитать этот документ. эта концепция хорошо объяснена здесь http://developer.android.com/training/basics/fragments/communicating.html
Я работаю над аналогичным проектом, и я думаю, что мой код может помочь в вышеуказанной ситуации.
Вот обзор того, что я делаю
В моем проекте есть два фрагмента с именами "FragmentA" и "FragmentB"
-FragmentA Содержит одно представление списка, когда вы щелкаете элемент в FragmentA, его INDEX передается в FragmentB с помощью интерфейса Communicator.
- Шаблон проектирования полностью основан на концепции интерфейсов Java, которая гласит: «ссылочные переменные интерфейса могут ссылаться на объект подкласса»
- Пусть MainActivity реализует интерфейс, предоставляемый fragmentA (иначе мы не сможем сделать ссылочную переменную интерфейса так, чтобы она указывала на MainActivity)
- В приведенном ниже коде объект коммуникатора ссылается на объект MainActivity с помощью метода "setCommunicator(Communicatot c)", присутствующего в fragmentA.
Я запускаю метод интерфейса respond() из FrgamentA, используя ссылку MainActivity.
Интерфейсный коммуникатор определен внутри фрагмента A, это должно обеспечить наименьший приоритет доступа к интерфейсу коммуникатор.
ниже мой полный рабочий код
ФрагментA.java
public class FragmentA extends Fragment implements OnItemClickListener {
ListView list;
Communicator communicater;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
return inflater.inflate(R.layout.fragmenta, container,false);
}
public void setCommunicator(Communicator c){
communicater=c;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
communicater=(Communicator) getActivity();
list = (ListView) getActivity().findViewById(R.id.lvModularListView);
ArrayAdapter<?> adapter = ArrayAdapter.createFromResource(getActivity(),
R.array.items, android.R.layout.simple_list_item_1);
list.setAdapter(adapter);
list.setOnItemClickListener(this);
}
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int index, long arg3) {
communicater.respond(index);
}
public interface Communicator{
public void respond(int index);
}
}
фрагментB.java
public class FragmentA extends Fragment implements OnItemClickListener {
ListView list;
Communicator communicater;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
return inflater.inflate(R.layout.fragmenta, container,false);
}
public void setCommunicator(Communicator c){
communicater=c;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
communicater=(Communicator) getActivity();
list = (ListView) getActivity().findViewById(R.id.lvModularListView);
ArrayAdapter<?> adapter = ArrayAdapter.createFromResource(getActivity(),
R.array.items, android.R.layout.simple_list_item_1);
list.setAdapter(adapter);
list.setOnItemClickListener(this);
}
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int index, long arg3) {
communicater.respond(index);
}
public interface Communicator{
public void respond(int index);
}
}
MainActivity.java
public class MainActivity extends Activity implements FragmentA.Communicator {
FragmentManager manager=getFragmentManager();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentA fragA=(FragmentA) manager.findFragmentById(R.id.fragmenta);
fragA.setCommunicator(this);
}
@Override
public void respond(int i) {
// TODO Auto-generated method stub
FragmentB FragB=(FragmentB) manager.findFragmentById(R.id.fragmentb);
FragB.changetext(i);
}
}
В основном реализуйте интерфейс для связи между Activity и фрагментом.
1) Основная деятельность
public class MainActivity extends Activity implements SendFragment.StartCommunication
{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void setComm(String msg) {
// TODO Auto-generated method stub
DisplayFragment mDisplayFragment = (DisplayFragment)getFragmentManager().findFragmentById(R.id.fragment2);
if(mDisplayFragment != null && mDisplayFragment.isInLayout())
{
mDisplayFragment.setText(msg);
}
else
{
Toast.makeText(this, "Error Sending Message", Toast.LENGTH_SHORT).show();
}
}
}
2) фрагмент отправителя (фрагмент-активность)
public class SendFragment extends Fragment
{
StartCommunication mStartCommunicationListner;
String msg = "hi";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View mView = (View) inflater.inflate(R.layout.send_fragment, container);
final EditText mEditText = (EditText)mView.findViewById(R.id.editText1);
Button mButton = (Button) mView.findViewById(R.id.button1);
mButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
msg = mEditText.getText().toString();
sendMessage();
}
});
return mView;
}
interface StartCommunication
{
public void setComm(String msg);
}
@Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
if(activity instanceof StartCommunication)
{
mStartCommunicationListner = (StartCommunication)activity;
}
else
throw new ClassCastException();
}
public void sendMessage()
{
mStartCommunicationListner.setComm(msg);
}
}
3) фрагмент приемника (Activity-to-fragment)
public class DisplayFragment extends Fragment
{
View mView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
mView = (View) inflater.inflate(R.layout.display_frgmt_layout, container);
return mView;
}
void setText(String msg)
{
TextView mTextView = (TextView) mView.findViewById(R.id.textView1);
mTextView.setText(msg);
}
}
Я использовал эту ссылку для того же решения, надеюсь, кто-то найдет ее полезной. Очень простой и базовый пример.
http://infobloggall.com/2014/06/22/коммуникация-между-деятельностью-и-фрагментами/
Фрагмент класса А
public class CountryListFragment extends ListFragment{
/** List of countries to be displayed in the ListFragment */
ListFragmentItemClickListener ifaceItemClickListener;
/** An interface for defining the callback method */
public interface ListFragmentItemClickListener {
/** This method will be invoked when an item in the ListFragment is clicked */
void onListFragmentItemClick(int position);
}
/** A callback function, executed when this fragment is attached to an activity */
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try{
/** This statement ensures that the hosting activity implements ListFragmentItemClickListener */
ifaceItemClickListener = (ListFragmentItemClickListener) activity;
}catch(Exception e){
Toast.makeText(activity.getBaseContext(), "Exception",Toast.LENGTH_SHORT).show();
}
}
Фрагмент класса B
public class CountryDetailsFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
/** Inflating the layout country_details_fragment_layout to the view object v */
View v = inflater.inflate(R.layout.country_details_fragment_layout, null);
/** Getting the textview object of the layout to set the details */
TextView tv = (TextView) v.findViewById(R.id.country_details);
/** Getting the bundle object passed from MainActivity ( in Landscape mode ) or from
* CountryDetailsActivity ( in Portrait Mode )
* */
Bundle b = getArguments();
/** Getting the clicked item's position and setting corresponding details in the textview of the detailed fragment */
tv.setText("Details of " + Country.name[b.getInt("position")]);
return v;
}
}
Основной класс Activity для передачи данных между фрагментами
public class MainActivity extends Activity implements ListFragmentItemClickListener {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
/** This method will be executed when the user clicks on an item in the listview */
@Override
public void onListFragmentItemClick(int position) {
/** Getting the orientation ( Landscape or Portrait ) of the screen */
int orientation = getResources().getConfiguration().orientation;
/** Landscape Mode */
if(orientation == Configuration.ORIENTATION_LANDSCAPE ){
/** Getting the fragment manager for fragment related operations */
FragmentManager fragmentManager = getFragmentManager();
/** Getting the fragmenttransaction object, which can be used to add, remove or replace a fragment */
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
/** Getting the existing detailed fragment object, if it already exists.
* The fragment object is retrieved by its tag name *
*/
Fragment prevFrag = fragmentManager.findFragmentByTag("in.wptrafficanalyzer.country.details");
/** Remove the existing detailed fragment object if it exists */
if(prevFrag!=null)
fragmentTransaction.remove(prevFrag);
/** Instantiating the fragment CountryDetailsFragment */
CountryDetailsFragment fragment = new CountryDetailsFragment();
/** Creating a bundle object to pass the data(the clicked item's position) from the activity to the fragment */
Bundle b = new Bundle();
/** Setting the data to the bundle object */
b.putInt("position", position);
/** Setting the bundle object to the fragment */
fragment.setArguments(b);
/** Adding the fragment to the fragment transaction */
fragmentTransaction.add(R.id.detail_fragment_container, fragment,"in.wptrafficanalyzer.country.details");
/** Adding this transaction to backstack */
fragmentTransaction.addToBackStack(null);
/** Making this transaction in effect */
fragmentTransaction.commit();
}else{ /** Portrait Mode or Square mode */
/** Creating an intent object to start the CountryDetailsActivity */
Intent intent = new Intent("in.wptrafficanalyzer.CountryDetailsActivity");
/** Setting data ( the clicked item's position ) to this intent */
intent.putExtra("position", position);
/** Starting the activity by passing the implicit intent */
startActivity(intent);
}
}
}
Подробный класс активности
public class CountryDetailsActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/** Setting the layout for this activity */
setContentView(R.layout.country_details_activity_layout);
/** Getting the fragment manager for fragment related operations */
FragmentManager fragmentManager = getFragmentManager();
/** Getting the fragmenttransaction object, which can be used to add, remove or replace a fragment */
FragmentTransaction fragmentTransacton = fragmentManager.beginTransaction();
/** Instantiating the fragment CountryDetailsFragment */
CountryDetailsFragment detailsFragment = new CountryDetailsFragment();
/** Creating a bundle object to pass the data(the clicked item's position) from the activity to the fragment */
Bundle b = new Bundle();
/** Setting the data to the bundle object from the Intent*/
b.putInt("position", getIntent().getIntExtra("position", 0));
/** Setting the bundle object to the fragment */
detailsFragment.setArguments(b);
/** Adding the fragment to the fragment transaction */
fragmentTransacton.add(R.id.country_details_fragment_container, detailsFragment);
/** Making this transaction in effect */
fragmentTransacton.commit();
}
}
Массив стран
public class Country {
/** Array of countries used to display in CountryListFragment */
static String name[] = new String[] {
"India",
"Pakistan",
"Sri Lanka",
"China",
"Bangladesh",
"Nepal",
"Afghanistan",
"North Korea",
"South Korea",
"Japan",
"Bhutan"
};
}
Для получения дополнительной информации посетите эту ссылку [http://wptrafficanalyzer.in/blog/itemclick-handler-for-listfragment-in-android/]. Есть полный пример..
getParentFragmentManager().setFragmentResultListener
— это способ сделать это в 2020 году. Единственным ограничением является использование пакета для передачи данных. Дополнительные сведения и примеры см. в документах.
Некоторые другие способы
- Вызовите
getActivity()
и приведите его к общей активности между вашими фрагментами, а затем используйте его в качестве моста для передачи данных. Это решение настоятельно не рекомендуется из-за того, что оно требует купелирования между активностью и фрагментами, но раньше это был популярный способ сделать это еще во времена KitKat... - Используйте обратные вызовы. Подойдет любой механизм событий. Это было бы ванильным решением для Java. Преимущество по сравнению с
FragmentManager
заключается в том, что он не ограничивается пакетами. Недостатком, однако, является то, что вы можете столкнуться с ошибками крайних случаев, когда вы испортите жизненный цикл активности и получите исключения, такие какIllegalStateException
, когда диспетчер фрагментов находится в середине состояния сохранения или активность была уничтожена. Кроме того, он не поддерживает межпроцессорную связь.
В основном здесь мы имеем дело с общением между Фрагментами. Связь между фрагментами никогда не может быть прямой. Он включает в себя деятельность, в контексте которой создаются оба фрагмента.
Вам необходимо создать интерфейс в отправляющем фрагменте и реализовать интерфейс в действии, которое будет откладывать сообщение и передавать его в принимающий фрагмент.