Передача данных между фрагментом и его контейнерной активностью

177

Как я могу передать данные между фрагментом и его контейнерной активностью? Есть ли что-то похожее на передачу данных между действиями через намерения?

Я читал это, но это не сильно помогло:
http://developer.android.com/guide/topics/fundamentals/fragments.html#CommunicatingWithActivity

TYB
источник
Этого недостаточно? Вы можете отправить все, что хотите, может быть, вы можете объяснить больше, чего вы хотите достичь?
Марцин Милейски,
Из документа я не понял, как, например, я бы передал значение или строку из действия во фрагмент или наоборот. Спасибо
tyb
2
Лучший способ обрабатывать асинхронные вызовы из фрагментов и возвращать им данные - это реализовать BroadcastReceivers с обеих сторон. В любом случае вы должны сделать это асинхронным, если вы работаете с неопределенным количеством фрагментов.
Марек Себера
@punisher_malade Нет, у меня работает
Вадим Котов

Ответы:

211

В ваш фрагмент вы можете позвонить getActivity().

Это даст вам доступ к деятельности, которая создала фрагмент. Оттуда вы можете явно вызывать любые методы доступа, которые есть в действии.

например, для метода, вызываемого getResult()в вашей Деятельности:

((MyActivity) getActivity()).getResult();
Ник
источник
86
Так как вы получаете доступ к функции в ВАШЕМ действии (а не в родительском действии Android), вам нужно будет вызвать ваш вызов getActivity (): ((MyActivity) getActivity ()). GetResult ();
Ник
3
Как будет, возможно, обратный метод, от фрагмента к деятельности?
Василь Вальчев
6
@VasilValchev, вы можете создать интерфейс и заставить действие реализовать его, а затем вызвать метод из вашего фрагмента для передачи данных. Используйте метод onAttach, чтобы проверить, реализует ли действие интерфейс.
Иван Николов
3
Отличный ответ от @IvanNikolov. Вы можете найти подробное объяснение по ссылке «Обучение фрагментам»
Богдан
8
Это не хорошая практика и не шаблон. Ответ с интерфейсами является правильным.
Мойктаб
304

Попробуйте использовать интерфейсы.

Любой фрагмент, который должен передавать данные обратно в содержащую его деятельность, должен объявлять интерфейс для обработки и передачи данных. Затем убедитесь, что ваша содержащая деятельность реализует эти интерфейсы. Например:

ЯВА

В вашем фрагменте объявите интерфейс ...

public interface OnDataPass {
    public void onDataPass(String data);
}

Затем подключите реализацию интерфейса содержащего класса к фрагменту в методе onAttach, например, так:

OnDataPass dataPasser;

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    dataPasser = (OnDataPass) context;
}

Внутри вашего фрагмента, когда вам нужно обработать передачу данных, просто вызовите его для объекта dataPasser:

public void passData(String data) {
    dataPasser.onDataPass(data);
}

Наконец, в вашей сдерживающей деятельности, которая реализует OnDataPass ...

@Override
public void onDataPass(String data) {
    Log.d("LOG","hello " + data);
}

Котлин

Шаг 1. Создать интерфейс

interface OnDataPass {
    fun onDataPass(data: String)
}

Шаг 2. Затем подключите реализацию интерфейса содержащего класса к фрагменту в методе onAttach (YourFragment) следующим образом:

lateinit var dataPasser: OnDataPass

override fun onAttach(context: Context) {
    super.onAttach(context)
    dataPasser = context as OnDataPass
}

Шаг 3. В вашем фрагменте, когда вам нужно обработать передачу данных, просто вызовите его для объекта dataPasser:

fun passData(data: String){
    dataPasser.onDataPass(data)
}

Шаг 4. Наконец, в вашей деятельности реализуется OnDataPass

class MyActivity : AppCompatActivity(), OnDataPass {}

override fun onDataPass(data: String) {
    Log.d("LOG","hello " + data)
}
Харло Холмс
источник
2
Спасибо хороший ответ, к делу. Это также очень хорошо объяснено здесь . Чего я не понял, так это того, что вы можете реализовать несколько интерфейсов, я уже реализовывал ActionBar.TabListenerи должен был добавить дополнительный интерфейс.
Юджин ван дер Мерве
5
Это определенно путь и, на мой взгляд, ЕДИНСТВЕННЫЙ путь. И обеспечивает двустороннее взаимодействие между Деятельностью и Фрагментом. Также позволит использовать средства для связи между фрагментами, когда у вас есть несколько фрагментов в одном действии, будь то через вкладки или макет с несколькими фрагментами.
Кристофер
1
Есть кое-что, что мне интересно ... Это официальный ответ, и на официальном сайте разработчиков они говорят, что это правильный способ общения frag-act-frag, но почему это вообще возможно сделать через приведение getActivity ( )?
unmultimedio
3
Зачем кому-то нужен дополнительный интерфейс для этого? Почему вы считаете следующее решение плохим или ваше решение лучше? ((MyActivity) getActivity) .myMethod (...)
spacifici
3
onAttach (Activity Activity) устарела, вместо этого используйте onAttach (Context context).
Chris.C
23

Самый простой подход, но не рекомендуется

Вы можете получить доступ к данным активности из фрагмента:

Деятельность:

public class MyActivity extends Activity {

    private String myString = "hello";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        ...
    }

    public String getMyData() {
        return myString;
    }
}

Фрагмент:

public class MyFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        MyActivity activity = (MyActivity) getActivity();
        String myDataFromActivity = activity.getMyData();
        return view;
    }
}
Zar E Ahmer
источник
5
Это добавляет высокую связь с вашим кодом и является плохой практикой. Теперь вы не можете использовать этот фрагмент в любой деятельности, кромеMyActivity
AmeyaB
почему этот способ считается плохой практикой и является ли интерфейс только решением этого вопроса?
androidXP
AmeyaB, если вся деятельность происходит от BaseActivity или мы разыгрываем ее как BaseActivity Activity = (BaseActivity) getActivity (); тогда он будет работать на все виды деятельности
Zar E
21
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    Bundle b = getActivity().getIntent().getExtras();
            wid = b.getString("wid");
            rid = b.getString("rid");
            View view = inflater.inflate(R.layout.categoryfragment, container, false);
    return view;
 }
Jeetu
источник
14
Вот как получить его во фрагменте, но как установить его в классе, вызывающем фрагмент, если у вас есть фрагмент в файле макета xml.
Запнологика
17

Передача данных между фрагментом и его контейнерной активностью

Деятельность:

        Bundle bundle = new Bundle();
        bundle.putString("message", "Alo Elena!");
        FragmentClass fragInfo = new FragmentClass();
        fragInfo.setArguments(bundle);
        transaction.replace(R.id.fragment_single, fragInfo);
        transaction.commit();

Фрагмент:

Чтение значения во фрагменте

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        String myValue = this.getArguments().getString("message");
        ...
        ...
        ...
        }
Jorgesys
источник
привет @Elenasys. Как я могу отправить данные пакета обратно во фрагмент из деятельности. Я перешел от фрагмента к действию, и мне нужно вернуться к фрагменту, который уже создан (вызывает только onStart, onResume, ...) и мне нужны данные, которые я смог получить в действии. Есть какой-то способ, как это сделать, или я делаю что-то не так?
Михал Моравик
7

Я не знаю, является ли это наилучшим способом или нет. Я долго искал в Google, чтобы найти, как я могу передать Bundle из фрагмента в его контейнерную активность, но все, что я нашел, это вместо этого отправлять данные из действия во фрагмент (что немного смутило меня, так как я новичок).

позже я попробовал что-то свое, что у меня получилось так, как я хотел. поэтому я опубликую это здесь, если кто-то вроде меня ищет то же самое.

// Передача данных из фрагмента.

Bundle gameData = new Bundle();
        gameData.putStringArrayList(Constant.KEY_PLAYERS_ARR,players);
        gameData.putString(Constant.KEY_TEAM_NAME,custom_team_name);
        gameData.putInt(Constant.KEY_REQUESTED_OVER,requestedOver);

        Intent intent = getActivity().getIntent();
        intent.putExtras(gameData);

// Получение данных из пакета по активности контейнера.

Bundle gameData = getIntent().getExtras();
        if (gameData != null)
        {
            int over = gameData.getInt(Constant.KEY_REQUESTED_OVER);
            ArrayList<String> players = gameData.getStringArrayList(Constant.KEY_PLAYERS_ARR);
            String team = gameData.getString(Constant.KEY_TEAM_NAME);

        }
        else if (gameData == null)
        {
            Toast.makeText(this, "Bundle is null", Toast.LENGTH_SHORT).show();
        }
Замороженный дневник
источник
6

Интерфейс является одним из лучших решений:

Клей интерфейс:

public interface DataProviderFromActivity {

    public String getName();
    public String getId);

}  

MyActivity:

public class MyActivity implements DataProviderFromActivity{

    String name = "Makarov";
    String id = "sys533";

    ... ... ... ... ... .... .... 
    ... ... ... ... ... .... .... 

    public String getName(){
        return name;
    };
    public String getId(){
        return id;
    };
}

MyFragment:

public class MyFragment extends Fragment{

    String fragName = "";
    String fragId = "";

    ... ... ... ... ... .... .... 
    ... ... ... ... ... .... .... 

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        DataProviderFromActivity myActivity= (DataProviderFromActivity) getActivity();
        fragName = myActivity.getName();
        fragId = myActivity.getId();

        ... ... ... ... ... .... .... 
        ... ... ... ... ... .... .... 

        updateFragmentView();
    }
}
Хасан Тарек
источник
Привет @HassanMakarov Мне нравится ваш интерфейс, простой и чистый. У меня есть одна проблема, хотя. Я не знаю, относится ли это к моему ViewPager, но я получаю данные не более чем в 2 фрагментах за раз? Строки «Макаров» и «sys533» появляются во фрагментах 1 и 2 при создании действия, но строки не появляются во фрагменте 3 до тех пор, пока не будет выбран фрагмент 2 или 3? Любые идеи?
JC23
@ JC23 Думаю, проблема связана с транзакцией фрагмента. Какой фрагмент установлен при запуске MainActivity?
Хасан Тарек
@ JC23 Что я делаю: вместо Tab / ViewPager я использую панель инструментов и NavigationView. В onNavigationItemSelected () я выполняю транзакции фрагмента, чтобы установить фрагменты соответственно.
Хасан Тарек
2

Я использовал AppCompatActivity, который реализует прослушиватели даты. Фрагменты стали необходимостью, поскольку мне нужно было кодировать селектор диапазона дат. И мне также был нужен контейнер для получения выбранных дат, чтобы вернуть их в родительское действие.

Для действия контейнера это объявление класса:

public class AppCompatDateRange extends AppCompatActivity implements
    DateIniRangeFragment.OnDateIniSelectedListener, DateFimRangeFragment.OnDateFimSelectedListener

И интерфейсы для обратных вызовов:

@Override
public void onDateIniSelected(String dataIni) {
    Log.i("data inicial:", dataIni);
}

@Override
public void onDateFimSelected(String dataFim) {
    Log.i("data final:", dataFim);
}

Обратные вызовы являются строками, потому что даты являются параметрами в выборе запроса.

Код для фрагментов (на основе исходного фрагмента даты):

public class DateIniRangeFragment extends Fragment {
OnDateIniSelectedListener callbackIni;

private DatePicker startDatePicker;

public DateIniRangeFragment() {
    ///required empty constructor
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

///through this interface the fragment sends data to the container activity
public interface OnDateIniSelectedListener {
    void onDateIniSelected(String dataIni);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    ///layout for the fragment
    View v = inflater.inflate(R.layout.date_ini_fragment, container, false);

    ///initial date for the picker, in this case, current date
    startDatePicker = (DatePicker) v.findViewById(R.id.start_date_picker_appcompat);
    Calendar c = Calendar.getInstance();
    int ano = c.get(Calendar.YEAR);
    int mes = c.get(Calendar.MONTH);
    int dia = c.get(Calendar.DAY_OF_MONTH);
    startDatePicker.setSpinnersShown(false);
    startDatePicker.init(ano, mes, dia, dateSetListener);

    return v;
}

///listener that receives the selected date
private DatePicker.OnDateChangedListener dateSetListener = new DatePicker.OnDateChangedListener() {
    public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
        if (view.isShown()) { ///if the datepicker is on the screen
            String sDataIni = year + "-" + (monthOfYear + 1) + "-" + dayOfMonth;
            callbackIni.onDateIniSelected(sDataIni); //apply date to callback, string format
        }
    }
};

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);

    /*
    * this function guarantees that the container activity implemented the callback interface
    * */
    try {
        callbackIni = (OnDateIniSelectedListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString() + " deve implementar OnDateIniSelectedListener");
    }
}

}

Чтобы создать контейнер + фрагменты, я использовал ViewPager (AppCompat) с пользовательским классом, который расширяет FragmentPagerAdapter. Нет диалогов.

Pablo
источник
2

Просто вы можете использовать EventBus, это легко и отлично работает

EventBus в 3 шага

  1. Определите события:

    public static class MessageEvent { /* Additional fields if needed */ }

  2. Подготовьте подписчиков: объявите и аннотируйте свой метод подписки, при желании укажите режим потока:

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(MessageEvent event) {/* Do something */};

Зарегистрируйте и отмените регистрацию вашего подписчика. Например, в Android действия и фрагменты обычно должны регистрироваться в соответствии с их жизненным циклом:

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

 @Override
 public void onStop() {
     super.onStop();
     EventBus.getDefault().unregister(this);
 }
  1. Опубликовать события:

    EventBus.getDefault().post(new MessageEvent());

Саттар
источник
1

Я знаю, что это может быть поздно. Но я также всегда терялся в этом вопросе. Я делюсь этой ссылкой ... потому что это, возможно, лучшее объяснение, которое я когда-либо нашел в Интернете для этого. Это решает фрагмент к деятельности и фрагмент к фрагменту !

Решено и объяснено очень хорошо

Yo Apps
источник
0

Это работает для меня ..

в Activity добавьте этот метод

    public void GetData(String data)
     {
        // do something with your data        
     }

и во фрагмент добавьте эту строку

((YourActivity)getContext).GetData("your data here");
Басант
источник
0
public class Fragmentdemo extends Fragment {

  public interface onDemoEventListener {
    public void demoEvent(String s);
  }

  onDemoEventListener demoEventListener;

  @Override
  public void onAttach(Activity activity) {
    super.onAttach(activity);
        try {
          demoEventListener = (onDemoEventListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement onDemoEventListener");
        }
  }

  final String LOG_TAG = "TAG";

  public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.fragmentdemo, null);

    Button button = (Button) v.findViewById(R.id.button);
    button.setOnClickListener(new OnClickListener() {
      public void onClick(View v) {
        demoEventListener.someEvent("Test text to Fragment1");
      }
    });
    enter code here
    return v;
  }
}
Швета Чандарана
источник
-12

Другой простой способ получить данные, переданные из другого действия, во фрагменте в операции контейнера: например:

Activity_A => Activity_B (Фрагмент)

В вашем Activity_A вы создаете намерение, как будто вы отправляете данные (строка здесь) в другое действие:

Intent intent = new Intent(getBaseContext(),Activity_B.class);
intent.putExtra("NAME", "Value");
startActivity(intent);

в вашем фрагменте, содержащемся в вашем Activity_B:

String data = getActivity().getIntent().getExtras();
SuperZouzou
источник
getBaseContext()выдает мне следующую ошибку:The method getBaseContext() is undefined for the type new View.OnClickListener(){}
Si8