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


176

Як я можу передавати дані між фрагментом та його активністю контейнера? Чи є щось подібне до передачі даних між видами діяльності через наміри?

Я читав це, але це мало допомогло:
http://developer.android.com/guide/topics/fundamentals/fragments.html#CommunicatingWithActivity


Це недостатньо? Ви можете надсилати все, що завгодно, можливо, ви можете пояснити більше, що ви хочете досягти?
Марцін Мілейський

З документа я не зрозумів, як, наприклад, я передавав би значення або рядок від активності до фрагменту чи навпаки. Спасибі
тиб

2
Найкращий спосіб обробки асинхронних дзвінків з фрагментів і повернення їх даних - це реалізація BroadcastReceivers з обох сторін. У будь-якому випадку ви повинні зробити його асинхронним, якщо ви працюєте з неспецифічною кількістю фрагментів.
Марек Себера

@punisher_malade Ні, працює на мене
Вадим Котов

Відповіді:


211

У своєму фрагменті ви можете зателефонувати getActivity().

Це дасть вам доступ до діяльності, яка створила фрагмент. Звідти ви, очевидно, можете викликати будь-які методи аксесуарів, які є в діяльності.

наприклад, для методу, який викликається getResult()вашою діяльністю:

((MyActivity) getActivity()).getResult();

86
Оскільки ви отримуєте доступ до функції у ВАШІЙ діяльності (а не батьківській діяльності Android), вам потрібно буде передати свій виклик getActivity (): ((MyActivity) getActivity ()). GetResult ();
Нік

3
Яким може бути метод "назад", від фрагмента до діяльності?
Василь Валчев

6
@VasilValchev, ви можете створити інтерфейс і змусити діяльність реалізувати його, а потім викликати метод з вашого фрагмента для передачі даних. Використовуйте метод onAttach, щоб перевірити, чи реалізує діяльність інтерфейс.
Іван Ніколов

3
Відмінна відповідь від @IvanNikolov. Ви можете знайти ґрунтовне пояснення на посиланнях на навчання фрагментів
bogdan

8
Це не є гарною практикою чи закономірністю. Відповідь з інтерфейсами правильна.
moictab

303

Спробуйте використовувати інтерфейси.

Будь-який фрагмент, який повинен повернути дані назад до його вмісту, повинен оголосити інтерфейс для обробки та передачі даних. Потім переконайтеся, що ваша діяльність містить реалізовані інтерфейси. Наприклад:

JAVA

У своєму фрагменті оголосіть інтерфейс ...

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

1
Дякую добру відповідь, до речі. Це теж дуже красиво пояснено тут . Що я не усвідомлював, це те, що ви можете реалізувати декілька інтерфейсів, я вже реалізовував ActionBar.TabListenerі повинен був додати додатковий інтерфейс.
Євген ван дер Мерве

5
Це, безумовно, шлях, і, на мою думку, ТІЛЬКИ шлях. І забезпечує двосторонню взаємодію між активністю та фрагментом. Також дозволить засобу спілкуватися між фрагментами, коли у вас є кілька фрагментів в одній діяльності, будь то через вкладки або мульти фрагмент макета.
Крістофер

1
Мені щось цікаво ... Це офіційна відповідь, і з офіційного сайту розробників вони кажуть, що це правильний спосіб поєднати frag-act-frag, але чому це навіть можливо зробити через кастинг getActivity ( )?
unmultimedio

3
Чому комусь потрібен додатковий інтерфейс для цього? Чому ви вважаєте наступне рішення поганим або ваше рішення краще? ((MyActivity) getActivity) .myMethod (...)
spacifici

3
onAttach (активність діяльності) застарілий, замість цього використовуйте onAttach (контекст контексту).
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;
    }
}

5
Це додає високого зв’язку з вашим кодом і є поганою практикою. Тепер ви не можете використовувати Фрагмент в будь-якій діяльності, крімMyActivity
AmeyaB

чому цей спосіб вважається поганою практикою і чи інтерфейс є лише вирішенням цього питання?
androidXP

AmeyaB, якщо вся діяльність поширюється на BaseActivity або ми передаємо це як BaseActivity activity = (BaseActivity) getActivity (); тоді він працюватиме над усіма видами діяльності
Зар Е Ахмер

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

14
Це як отримати його у фрагменті, але як встановити його у класі, що викликає фрагмент, якщо у вас є фрагмент у файлі макета xml.
Zapnologica

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

привіт @Elenasys. Як я можу повернути дані зв’язку назад до фрагменту від діяльності. Я перейшов від фрагмента до активності, і мені потрібно повернутися до вже створеного фрагмента (дзвінки тільки наStart, onResume, ...) і мені потрібні дані, які мені вдалося отримати в діяльності. Чи є якийсь спосіб, як це зробити чи я роблю щось не так?
Міхал Моравік

7

Я не знаю, чи це найкращий спосіб, чи ні. Я довго шукав у google, шукаючи, як я можу передати пакет із фрагмента до його контейнерної активності, але все, що я знайшов, - надіслати дані з активності на фрагмент замість цього (що було трохи заплутано для мене, оскільки я новачок).

пізніше я спробував щось своє, що саме працювало для мене так, як я хотів. тому я опублікую його тут, якщо хтось, як я, шукає те саме

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

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 Мені подобається ваш інтерфейс, простий і чистий. Однак у мене є одне питання. Я не знаю, чи є вона специфічною для мого оглядача, але я отримую дані лише за 2 фрагменти одночасно? Рядки "Makarov" & "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. Немає діалогів.


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

Я знаю, це може пізно. Але я також завжди губився з цього питання. Я ділюсь цим посиланням ... тому що це, мабуть, найкраще пояснення, яке я коли-небудь знаходив у мережі. Це вирішує фрагмент до активності та фрагмент до фрагменту !

Вирішено та пояснено дуже добре


0

Це працює для мене ..

у "Діяльність" додайте цей метод

    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 (фрагмент)

У своїй діяльності_A ви створюєте такий намір, як ви надсилаєте дані (рядок тут) до іншої діяльності:

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

у вашому фрагменті, що міститься у вашій Activity_B:

String data = getActivity().getIntent().getExtras();

getBaseContext()дає мені таку помилку:The method getBaseContext() is undefined for the type new View.OnClickListener(){}
Si8
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.