Przekazywanie danych między fragmentem a jego działaniem w kontenerze


176

Jak mogę przekazać dane między fragmentem a jego działaniem w kontenerze? Czy jest coś podobnego do przekazywania danych między działaniami poprzez intencje?

Czytałem to, ale niewiele to pomogło:
http://developer.android.com/guide/topics/fundamentals/fragments.html#CommunicatingWithActivity


Czy to nie wystarczy? Możesz wysłać wszystko, co chcesz, może wyjaśnisz więcej, co chcesz osiągnąć?
Marcin Milejski

Z dokumentu nie rozumiałem, jak na przykład przekazywać wartość lub ciąg znaków z działania do fragmentu lub odwrotnie. Dzięki
tyb

2
Najlepszym sposobem obsługi wywołań asynchronicznych z fragmentów i zwracania im danych jest implementacja BroadcastReceivers po obu stronach. W każdym razie powinieneś uczynić go asynchronicznym, jeśli pracujesz z nieokreśloną liczbą fragmentów.
Marek Sebera 20.07.2013

@punisher_malade Nie, pracuje dla mnie
Vadim Kotov

Odpowiedzi:


211

W swoim fragmencie możesz zadzwonić getActivity().

Dzięki temu uzyskasz dostęp do aktywności, która utworzyła fragment. Stamtąd możesz oczywiście wywołać dowolne metody akcesorów, które są w działaniu.

np. dla metody wywoływanej getResult()w Twoim działaniu:

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

86
Ponieważ uzyskujesz dostęp do funkcji w ramach TWOJEGO działania (a nie nadrzędnej aktywności Androida), będziesz musiał rzutować wywołanie getActivity (): ((MyActivity) getActivity ()). GetResult ();
Nick

3
Jaka będzie prawdopodobnie metoda wsteczna, od fragmentu do działania?
Vasil Valchev

6
@VasilValchev, możesz utworzyć interfejs i wymusić na działaniu jego implementację, a następnie wywołać metodę z fragmentu, aby przekazać dane. Użyj metody onAttach, aby sprawdzić, czy działanie implementuje interfejs.
Ivan Nikolov

3
Doskonała odpowiedź od @IvanNikolov. Dokładne wyjaśnienie można znaleźć pod linkiem Fragments Training Link
bogdan

8
To nie jest dobra praktyka ani wzorzec. Odpowiedź z interfejsami jest prawidłowa.
moictab

303

Spróbuj użyć interfejsów.

Każdy fragment, który powinien przekazywać dane z powrotem do swojego działania zawierającego, powinien deklarować interfejs do obsługi i przekazywania danych. Następnie upewnij się, że działanie zawierające implementuje te interfejsy. Na przykład:

JAWA

W swoim fragmencie zadeklaruj interfejs ...

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

Następnie podłącz implementację interfejsu klasy zawierającej do fragmentu w metodzie onAttach, tak jak poniżej:

OnDataPass dataPasser;

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

W swoim fragmencie, gdy musisz obsłużyć przekazywanie danych, po prostu wywołaj go w obiekcie dataPasser:

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

Wreszcie, w Twojej działalności zawierającej, która implementuje OnDataPass ...

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

KOTLIN

Krok 1. Utwórz interfejs

interface OnDataPass {
    fun onDataPass(data: String)
}

Krok 2. Następnie podłącz implementację interfejsu zawierającej klasę do fragmentu w metodzie onAttach (YourFragment), tak jak poniżej:

lateinit var dataPasser: OnDataPass

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

Krok 3. W obrębie twojego fragmentu, kiedy musisz obsłużyć przekazywanie danych, po prostu wywołaj go w obiekcie dataPasser:

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

Krok 4. Wreszcie, w swojej aktywności implementuje OnDataPass

class MyActivity : AppCompatActivity(), OnDataPass {}

override fun onDataPass(data: String) {
    Log.d("LOG","hello " + data)
}

1
Dzięki, dobra odpowiedź, do rzeczy. Jest to również bardzo ładnie wyjaśnione tutaj . Nie zdawałem sobie sprawy, że można zaimplementować wiele interfejsów, już implementowałem jeden ActionBar.TabListeneri musiałem dodać dodatkowy interfejs.
Eugene van der Merwe

5
Jest to zdecydowanie droga i moim zdaniem JEDYNA droga. I zapewnia dwukierunkową interakcję między działaniem a fragmentem. Pozwoli również na komunikowanie się między fragmentami, gdy masz wiele fragmentów w jednym działaniu, czy to za pośrednictwem kart, czy układu multi-frag.
Christopher,

1
Jest coś, nad czym się zastanawiam ... To jest oficjalna odpowiedź, a na oficjalnej stronie programistów mówią, że jest to właściwy sposób komunikowania się frag-act-frag, ale dlaczego jest to w ogóle możliwe poprzez casting getActivity ( )?
unmultimedio

3
Dlaczego ktoś potrzebuje do tego dodatkowego interfejsu? Dlaczego uważasz poniższe rozwiązanie za złe lub swoje rozwiązanie za lepsze? ((MyActivity) getActivity) .myMethod (...)
spacifici

3
onAttach (aktywność) jest przestarzała, zamiast tego użyj onAttach (kontekst kontekstowy).
Chris.C,

23

Najłatwiejsze podejście, ale nie zalecane

Możesz uzyskać dostęp do danych aktywności z fragmentu:

Czynność:

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

Fragment:

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
To zwiększa sprzężenie z Twoim kodem i jest złą praktyką. Teraz nie możesz użyć tego fragmentu w żadnej innej aktywności niżMyActivity
AmeyaB

dlaczego ten sposób jest uważany za złą praktykę i czy interfejs jest tylko rozwiązaniem tego pytania?
androidXP

AmeyaB, jeśli cała aktywność rozciąga się od BaseActivity lub i rzucamy ją jak BaseActivity activity = (BaseActivity) getActivity (); wtedy będzie działać na wszystkich działaniach
Zar E Ahmer

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
Oto jak go zdobyć we fragmencie, ale jak ustawić go w klasie wywołującej fragment, jeśli masz swój fragment w pliku układu xml.
Zapnologica

17

Przekazywanie danych między fragmentem a jego działaniem w kontenerze

Czynność:

        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();

Fragment:

Czytanie wartości we fragmencie

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        String myValue = this.getArguments().getString("message");
        ...
        ...
        ...
        }

cześć @Elenasys. Jak mogę odesłać pakiet danych z powrotem do fragmentu z aktywności. Przeszedłem od fragmentu do działania i muszę wrócić do fragmentu, który jest już utworzony (wywołuje tylko onStart, onResume, ...) i potrzebuję danych, które udało mi się uzyskać w działaniu. Czy jest jakiś sposób, jak to zrobić, czy też robię coś źle?
Michał Moravik

7

Nie wiem, czy to najlepszy sposób, czy nie Bu Szukałem w Google od dłuższego czasu znajdując, jak mogę przekazać pakiet z fragmentu do jego aktywności w kontenerze, ale zamiast tego znalazłem tylko wysyłanie danych z działania do fragmentu (co było dla mnie trochę zagmatwane, ponieważ jestem nowicjuszem).

później spróbowałem czegoś własnego, co działało dla mnie dokładnie tak, jak chciałem. więc opublikuję to tutaj, jeśli ktoś taki jak ja szuka tego samego.

// Przekazywanie danych z fragmentu.

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

// Pobieranie danych z pakietu z jego aktywności w kontenerze.

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

Interfejs to jedno z najlepszych rozwiązań:

Interfejs kleju:

public interface DataProviderFromActivity {

    public String getName();
    public String getId);

}  

Moja aktywność:

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

Cześć @HassanMakarov Podoba mi się twój interfejs, prosty i przejrzysty. Mam jednak jeden problem. Nie wiem, czy jest to specyficzne dla mojego viewpager, ale dostaję dane tylko w maksymalnie 2 fragmentach naraz? Ciągi „Makarov” i „sys533” pojawiają się we fragmentach 1 i 2, gdy aktywność jest tworzona, ale łańcuchy nie pojawiają się we fragmencie 3, dopóki nie zostanie wybrany fragment 2 lub 3? Jakieś pomysły?
JC23

@ JC23 Myślę, że problem jest związany z transakcją fragmentu. Który fragment jest ustawiany po uruchomieniu MainActivity?
Hassan Tareq,

@ JC23 Co robię: zamiast Tab / ViewPager używam Toolbar & NavigationView. W onNavigationItemSelected () wykonuję transakcje fragmentowe, aby odpowiednio ustawić fragmenty.
Hassan Tareq,

2

Użyłem AppCompatActivity, który implementuje nasłuchiwanie dat. Fragmenty były konieczne, ponieważ musiałem zakodować selektor zakresu dat. Potrzebowałem również kontenera, aby otrzymać wybrane daty, aby przywrócić je do aktywności nadrzędnej.

W przypadku działania kontenera jest to deklaracja klasy:

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

I interfejsy dla wywołań zwrotnych:

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

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

Wywołania zwrotne są ciągami, ponieważ daty są parametrami w wyborze zapytania.

Kod fragmentów (na podstawie początkowego fragmentu daty):

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

}

Aby skomponować kontener + fragmenty, użyłem ViewPager (AppCompat) z niestandardową klasą, która rozszerza FragmentPagerAdapter. Brak okien dialogowych.


2

Po prostu możesz skorzystać z EventBus, jest to łatwe i działa świetnie

EventBus w 3 krokach

  1. Zdefiniuj wydarzenia:

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

  2. Przygotuj subskrybentów: zadeklaruj i dodaj adnotacje do swojej metody subskrypcji, opcjonalnie określ tryb wątku:

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

Zarejestruj i wyrejestruj swojego subskrybenta. Na przykład na Androidzie działania i fragmenty powinny zwykle rejestrować się zgodnie z ich cyklem życia:

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

 @Override
 public void onStop() {
     super.onStop();
     EventBus.getDefault().unregister(this);
 }
  1. Opublikuj wydarzenia:

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


1

Wiem, że to może być późno. Ale ja też zawsze byłem zagubiony w tej kwestii. Udostępniam ten link ... ponieważ jest to prawdopodobnie najlepsze wyjaśnienie tego problemu, jakie kiedykolwiek znalazłem w Internecie. To rozwiązuje fragment do działania i fragment do fragmentu !

Naprawdę dobrze rozwiązany i wyjaśniony


0

To działa dla mnie ...

w Aktywności dodaj tę metodę

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

i we fragmencie dodaj tę linię

((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

Innym prostym sposobem na pobranie danych, przekazanych z innej aktywności, we fragmencie w działaniu kontenera: na przykład:

Activity_A => Activity_B (fragment)

W swoim Activity_A tworzysz intencję, taką jak wysyłanie danych (tutaj String) do innego działania:

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

w Twoim fragmencie zawartym w Twoim Activity_B:

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

getBaseContext()daje mi następujący błąd:The method getBaseContext() is undefined for the type new View.OnClickListener(){}
Si8
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.