Pasar datos entre un fragmento y su actividad de contenedor

176

¿Cómo puedo pasar datos entre un fragmento y su actividad de contenedor? ¿Hay algo similar a pasar datos entre actividades a través de intentos?

Leí esto, pero no ayudó mucho:
http://developer.android.com/guide/topics/fundamentals/fragments.html#CommunicatingWithActivity

tyb
fuente
¿Eso es insuficiente? Puedes enviar lo que quieras, tal vez puedas explicar más, ¿qué quieres lograr?
Marcin Milejski
Del documento no entendí cómo, por ejemplo, pasaría un valor o una cadena de la actividad al fragmento o viceversa. Gracias
tyb
2
La mejor manera de manejar llamadas asíncronas desde fragmentos y devolverles datos es implementar BroadcastReceivers en ambos lados. De todos modos, debe hacerlo asíncrono si está trabajando con un número no específico de fragmentos.
Marek Sebera
@punisher_malade No, funciona para mí
Vadim Kotov

Respuestas:

211

En tu fragmento puedes llamar getActivity().

Esto le dará acceso a la actividad que creó el fragmento. Desde allí, obviamente, puede llamar a cualquier tipo de método de acceso que esté en la actividad.

por ejemplo, para un método llamado getResult()en su Actividad:

((MyActivity) getActivity()).getResult();
Mella
fuente
86
Dado que está accediendo a una función dentro de SU actividad (y no a la actividad principal de Android), deberá emitir su llamada getActivity (): ((MyActivity) getActivity ()). GetResult ();
Nick
3
¿Cómo será posiblemente el método de retroceso, de fragmento a actividad?
Vasil Valchev
66
@VasilValchev, puede crear una interfaz y obligar a la actividad a implementarla y luego llamar a un método desde su fragmento para pasar los datos. Use el método onAttach para verificar si la actividad implementa la interfaz.
Ivan Nikolov
3
Excelente respuesta de @IvanNikolov. Puede encontrar una explicación detallada en el Fragments Training Link
bogdan
8
Esta no es una buena práctica ni un patrón. La respuesta con interfaces es la correcta.
moictab
303

Intenta usar interfaces.

Cualquier fragmento que debe pasar datos a su actividad que contiene debe declarar una interfaz para manejar y pasar los datos. Luego, asegúrese de que su actividad de contención implemente esas interfaces. Por ejemplo:

JAVA

En su fragmento, declare la interfaz ...

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

Luego, conecte la implementación de la clase que contiene la interfaz al fragmento en el método onAttach, de esta manera:

OnDataPass dataPasser;

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

Dentro de su fragmento, cuando necesite manejar el paso de datos, simplemente instálelo en el objeto dataPasser:

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

Finalmente, en su actividad de contención que implementa OnDataPass ...

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

KOTLIN

Paso 1. Crear interfaz

interface OnDataPass {
    fun onDataPass(data: String)
}

Paso 2. Luego, conecta la implementación de la clase que contiene la interfaz al fragmento en el método onAttach (YourFragment), así:

lateinit var dataPasser: OnDataPass

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

Paso 3. Dentro de su fragmento, cuando necesite manejar el paso de datos, simplemente instálelo en el objeto dataPasser:

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

Paso 4. Finalmente, en tu actividad implementa OnDataPass

class MyActivity : AppCompatActivity(), OnDataPass {}

override fun onDataPass(data: String) {
    Log.d("LOG","hello " + data)
}
Harlo Holmes
fuente
1
Gracias buena respuesta, al punto. Esto también se explica muy bien aquí . Lo que no me di cuenta es que puedes implementar múltiples interfaces, ya estaba implementando una ActionBar.TabListenery tuve que agregar una interfaz adicional.
Eugene van der Merwe
55
Este es definitivamente el camino a seguir y, en mi opinión, el ÚNICO camino a seguir. Y proporciona una interacción bidireccional entre la Actividad y el Fragmento. También permitirá un medio para comunicarse entre fragmentos cuando tenga múltiples fragmentos en una actividad, ya sea a través de pestañas o diseño de múltiples fragmentos.
Christopher
1
Hay algo que me pregunto ... Esta es la respuesta oficial, y desde el sitio oficial del desarrollador dicen que esta es la forma correcta de comunicar frag-act-frag, pero ¿por qué es posible hacerlo a través de casting getActivity ( )?
unmultimedio
3
¿Por qué alguien necesita una interfaz adicional para hacer eso? ¿Por qué considera que la siguiente solución es mala o que su solución es mejor? ((MyActivity) getActivity) .myMethod (...)
spacifici
3
onAttach (Actividad de actividad) está en desuso, por favor use onAttach (Contexto contextual) en su lugar.
Chris.C
23

Enfoque más fácil pero no recomendado

Puede acceder a los datos de actividad desde el fragmento:

Actividad:

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

Fragmento:

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
fuente
55
Esto agrega un alto acoplamiento a su código y es una mala práctica. Ahora no puede usar el Fragmento en ninguna Actividad que no seaMyActivity
AmeyaB
¿Por qué de esta manera se considera una mala práctica y esta interfaz es la única solución de esta pregunta?
androidXP
AmeyaB si toda la actividad se extiende desde una BaseActivity o y la lanzamos como BaseActivity activity = (BaseActivity) getActivity (); entonces funcionará en todas las actividades
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;
 }
Jeetu
fuente
14
Esta es la forma de obtenerlo en el Fragmento, pero ¿cómo lo configura en la clase que llama al fragmento si tiene su fragmento en el archivo de diseño xml?
Zapnologica
17

Pasar datos entre un fragmento y su actividad de contenedor

Actividad:

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

Fragmento:

Leer el valor en el fragmento

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        String myValue = this.getArguments().getString("message");
        ...
        ...
        ...
        }
Jorgesys
fuente
hola @Elenasys. ¿Cómo puedo enviar datos de paquete a fragmentos de actividad? Pasé del fragmento a la actividad y necesito volver al fragmento que ya está creado (solo invoca en Start, onResume, ...) y necesito los datos que pude obtener en la actividad. ¿Hay alguna forma de hacerlo o estoy haciendo algo mal?
Michal Moravik,
7

No sé si esta es la mejor manera o no, Bu. He estado buscando en Google durante bastante tiempo para encontrar cómo puedo pasar un paquete de un fragmento a su actividad de contenedor, pero todo lo que encontré fue enviar datos de la actividad a un fragmento. (lo cual fue un poco confuso para mí ya que soy un novato).

luego probé algo propio que me funcionó exactamente como quería. así que lo publicaré aquí en caso de que alguien como yo busque lo mismo.

// Pasando datos desde Fragment.

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

// Obteniendo datos del paquete de su actividad contenedor.

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();
        }
Diario congelado
fuente
6

La interfaz es una de las mejores soluciones:

Interfaz de pegamento:

public interface DataProviderFromActivity {

    public String getName();
    public String getId);

}  

Mi actividad:

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();
    }
}
Hassan Tareq
fuente
Hola @HassanMakarov Me gusta tu interfaz, simple y limpia. Sin embargo, tengo un problema. No sé si es específico de mi visor, pero ¿solo obtengo los datos como máximo en 2 fragmentos a la vez? Las cadenas "Makarov" y "sys533" aparecen en los fragmentos 1 y 2 cuando se crea la actividad, pero las cadenas no aparecen en el fragmento 3 hasta que se selecciona el fragmento 2 o 3. ¿Algunas ideas?
JC23
@ JC23 Supongo que el problema está relacionado con la transacción de fragmentos. ¿Qué fragmento se establece cuando se inicia MainActivity?
Hassan Tareq
@ JC23 Lo que hago: en lugar de Tab / ViewPager, uso Toolbar & NavigationView. En onNavigationItemSelected () realizo transacciones de fragmentos para establecer fragmentos en consecuencia.
Hassan Tareq
2

Usé una AppCompatActivity que implementa Date Listeners. Los fragmentos llegaron como una necesidad ya que necesitaba codificar un selector de rango de fechas. Y también necesitaba que el contenedor recibiera las fechas seleccionadas para devolverlas a la actividad principal.

Para la actividad de contenedor, esta es la declaración de clase:

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

Y las interfaces para las devoluciones de llamada:

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

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

Las devoluciones de llamada son cadenas porque las fechas son parámetros en una selección de consulta.

El código para los fragmentos (basado en el fragmento de fecha inicial):

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

}

Para componer el contenedor + fragmentos, utilicé un ViewPager (AppCompat) con una clase personalizada que extiende FragmentPagerAdapter. Sin diálogos.

Pablo
fuente
2

Simplemente puede usar EventBus , es fácil y funciona muy bien

EventBus en 3 pasos

  1. Definir eventos:

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

  2. Preparar suscriptores: declare y anote su método de suscripción, opcionalmente especifique un modo de hilo:

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

Registre y anule el registro de su suscriptor. Por ejemplo, en Android, las actividades y los fragmentos generalmente deberían registrarse de acuerdo con su ciclo de vida:

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

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

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

Sattar
fuente
1

Sé que esto podría ser tarde. Pero también siempre me perdí en esta pregunta. Estoy compartiendo este enlace ... porque esta es posiblemente la mejor explicación que he encontrado en la web para esto. ¡Esto resuelve Fragmento a actividad y Fragmento a fragmento !

Resuelto y explicado muy bien

Yo Apps
fuente
0

Esto es trabajo para mí..

en Actividad agregue este método

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

y en Fragment agrega esta línea

((YourActivity)getContext).GetData("your data here");
Basant
fuente
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;
  }
}
Shweta Chandarana
fuente
-12

Otra forma simple de obtener datos, pasados ​​de otra actividad, en un fragmento de una actividad de contenedor: por ejemplo:

Activity_A => Activity_B (Fragment)

En su Activity_A crea una intención como si estuviera enviando datos (String aquí) a otra actividad:

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

en tu Fragmento, contenido en tu Activity_B:

String data = getActivity().getIntent().getExtras();
SuperZouzou
fuente
getBaseContext()me da el siguiente error:The method getBaseContext() is undefined for the type new View.OnClickListener(){}
Si8