Android: pasar datos (extras) a un fragmento

83

Soy nuevo en la programación de Android y tengo problemas al pasar una ArrayList de un Parcelable a un fragmento. Esta es la actividad que se lanza (¡funcionando bien!) Donde feedlist es un ArrayList de una música parcelable .

Intent in = new Intent(context, ListMusicsActivity.class);

in.putExtra("arrayMusic", feedList);
activity.startActivity(in);

El método del fragmento Activity onCreate ():

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

    if(savedInstanceState != null)
    {
        ListMusicFragment frag = new ListMusicFragment();
        frag.setArguments(getIntent().getExtras());
    }
}

El código del fragmento:

public class ListMusicFragment extends SherlockFragment{

private ArrayList<Music> listMusics = new ArrayList<Music>();
private ListView listMusic;


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, 
    Bundle savedInstanceState)
{
    listMusics = (ArrayList<Music>) getArguments().getSerializable("arrayMusic");
    View view = inflater.inflate(R.layout.musiclistview, container, false);
    listMusic = (ListView) view.findViewById(R.id.musicListView);
    listMusic.setAdapter(new MusicBaseAdapter(getActivity(), listMusics));

    return view;
}
}

Creo que el problema esta en la linea

listMusics = (ArrayList<Music>) getArguments().getSerializable("arrayMusic");

Finalmente esta es mi clase de Música:

public class Music implements Parcelable{

private String url;
private String artist;
private String title;
private String duration;
private String info_url;
private String lyrics;


public Music(String url, String artist, String title, 
    String duration, String lyrics, String info_url)
{
    this.url = url;
    this.artist = artist;
    this.title = title;
    this.duration = duration;
    this.lyrics = lyrics;
    this.info_url = info_url;
}

public Music(Parcel in)
{
    url = ParcelUtils.readString(in);
    artist = ParcelUtils.readString(in);
    title = ParcelUtils.readString(in);
    duration = ParcelUtils.readString(in);
    info_url = ParcelUtils.readString(in);
    lyrics = ParcelUtils.readString(in);
}

public String getUrl()
{
    return url;
}

public String getArtist()
{
    return artist;
}

public String getTitle()
{
    return title;
}

public String getDuration()
{
    return duration;
}

public String getLyrics()
{
    return lyrics;
}

public String getInfo()
{
    return info_url;
}

@Override
public int describeContents() {
    return 0;
}


@Override
public void writeToParcel(Parcel dest, int flags)
{
    ParcelUtils.writeString(dest, url);
    ParcelUtils.writeString(dest, artist);
    ParcelUtils.writeString(dest, title);
    ParcelUtils.writeString(dest, duration);
    ParcelUtils.writeString(dest, lyrics);
    ParcelUtils.writeString(dest, info_url);
}

public static final Parcelable.Creator<Music> CREATOR = 
    new Parcelable.Creator<Music>() {

    public Music createFromParcel(Parcel in)
    {
        return new Music(in);
    }

    public Music[] newArray(int size)
    {
        return new Music[size];
    }
};
}

Cuando ejecuto este código, el problema que obtengo es una excepción java.lang.NullPointerException en el método Fragment onCreateView () . Apreciaría mucho si alguien me indicara la dirección correcta para ver dónde estoy fallando.

EDITAR : Problema resuelto: solo necesitaba agregar esta línea al método del fragmento Activity onCreate () (de lo contrario, getArguments () devolvería nulo):

getSupportFragmentManager().beginTransaction()
    .add(android.R.id.content, frag).commit();

Y agregue esto al código del fragmento:

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

    Bundle bundle = getArguments();
    if(bundle != null)
    {
        listMusics = bundle.getParcelableArrayList("arrayMusic");
        listMusic.setAdapter(new MusicBaseAdapter(getActivity(), listMusics));
    }
}

donde, listMusics es una ArrayListde ParcelableMusic.

pluralismo
fuente
El primer paso es abrir la ventana de LogCat, mirar la excepción y ver qué está causando la NullPointerException. Si no puede resolverlo, coloque el seguimiento de pila de la excepción en sus preguntas o alguien puede ayudarlo.
Brian
BrianV: ¡Gracias por el consejo! ¡Aquí está el LogCat que obtengo cuando ejecuto el programa! pastebin.com/ycVcXLFf
pluralismo
Parece que tiene un error en el archivo de diseño de la vista: ** Causado por: android.view.InflateException: línea de archivo XML binario n. ° 10: error al inflar el fragmento de clase **
Brian
No, el error está en el código de ListView, estoy seguro. Si creo un objeto ArrayList of Music en ListMusicFragment, el código funciona bien. De todos modos, aquí están mis archivos de diseño: pastebin.com/4DwHh1yk
pluralismo

Respuestas:

196

Dos cosas. Primero, no creo que esté agregando los datos que desea pasar al fragmento correctamente. Lo que necesita para pasar al fragmento es un paquete, no una intención. Por ejemplo, si quisiera enviar un intvalor a un fragmento, crearía un paquete, pondría el inten ese paquete y luego establecería ese paquete como un argumento para usar cuando se creó el fragmento.

Bundle bundle = new Bundle();
bundle.putInt(key, value);
fragment.setArguments(bundle);

En segundo lugar, para recuperar esa información, necesita obtener los argumentos enviados al fragmento. Luego extrae el valor en función de la clave con la que lo identificó. Por ejemplo en tu fragmento:

Bundle bundle = this.getArguments();
if (bundle != null) {
    int i = bundle.getInt(key, defaulValue);
}

Lo que estás obteniendo cambia dependiendo de lo que pones. Además, el valor predeterminado suele ser, nullpero no es necesario. Depende de si establece un valor predeterminado para ese argumento.

Por último, no creo que puedas hacer esto en onCreateView. Creo que debes recuperar estos datos dentro del onActivityCreatedmétodo de tu fragmento . Mi razonamiento es el siguiente. onActivityCreatedse ejecuta después de que la actividad subyacente haya finalizado su propio onCreatemétodo. Si está colocando la información que desea recuperar dentro del paquete durante el onCreatemétodo de su actividad , no existirá durante su fragmento onCreateView. Intente usar esto onActivityCreatedy actualice su ListViewcontenido más tarde.

Rarw
fuente
3
Respuesta simple, impresionante. ¡Copió su ejemplo de código y funcionó de maravilla!
iamanyone
1
Entonces ... ¿qué pasa si los argumentos son especificados por objetos que implementan interfaces específicas y no primitivas?
Piotr
2
Si desea utilizar un objeto, asegúrese de que implementen la Parcelableinterfaz. Bundleadmite agregar / obtener Parcelablepara que pueda enviarlos sin problema.
Rarw
1
¿Tiene un flujo de trabajo para cuando el fragmento se define en el layout.xml de la actividad? En este caso, creo que Android creará una instancia del fragmento en sí, lo que significa que no puede proporcionar argumentos. Personalmente, estoy usando un eventbus (otto) y un productor para pasar los datos de la actividad al fragmento.
Alec Holmes
Claro, cuando defina el fragmento en su XML, use el atributo android: tag para establecer una etiqueta para ese fragmento. En su actividad, puede usar FragmentManager.findFragmentByTag () para ubicar ese fragmento y luego usar FragmentTransaction para reemplazar el fragmento XML con un nuevo fragmento configurado de la manera que desee. Otro enfoque sería usar findFragmentById y simplemente usar el valor android: id de su fragmento XML.
Rarw
2

Prefiero Serializable= sin código repetitivo. Para pasar datos a otros Fragmentos o Actividades, la diferencia de velocidad con aParcelable no importa.

También siempre proporcionaría un método auxiliar para un Fragmento Activity, de esta manera, siempre sabrá qué datos se deben pasar. Aquí un ejemplo para su ListMusicFragment:

private static final String EXTRA_MUSIC_LIST = "music_list";

public static ListMusicFragment createInstance(List<Music> music) {
    ListMusicFragment fragment = new ListMusicFragment();
    Bundle bundle = new Bundle();
    bundle.putSerializable(EXTRA_MUSIC_LIST, music);
    fragment.setArguments(bundle);
    return fragment;
}

@Override
public View onCreateView(...) { 
    ...
    Bundle bundle = intent.getArguments();
    List<Music> musicList = (List<Music>)bundle.getSerializable(EXTRA_MUSIC_LIST);
    ...
}
artkoenig
fuente
1

Hay una razón simple por la que preferí el paquete debido a que no hay datos duplicados en la memoria. Consiste en un método público de inicio para el fragmento

private ArrayList<Music> listMusics = new ArrayList<Music>();
private ListView listMusic;


public static ListMusicFragment createInstance(List<Music> music) {
    ListMusicFragment fragment = new ListMusicFragment();
    fragment.init(music);
    return fragment;
}

public void init(List<Music> music){
    this.listMusic = music;
}

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

    View view = inflater.inflate(R.layout.musiclistview, container, false);
    listMusic = (ListView) view.findViewById(R.id.musicListView);
    listMusic.setAdapter(new MusicBaseAdapter(getActivity(), listMusics));

    return view;
}
}

En dos palabras, creas una instancia del fragmento y mediante el método init (puedes llamarlo como quieras) pasas la referencia de tu lista sin crear una copia por serialización a la instancia del fragmento. Esto es muy útil porque si cambia algo en la lista, lo obtendrá en las otras partes de la aplicación y, por supuesto, usa menos memoria.

Iván
fuente
1
En Android es una forma incorrecta. Porque en la recreación de fragmentos (en el cambio de configuración (rotación de pantalla) y algunos otros) invocará un constructor vacío, y todos los parámetros se perderán. Utilice newInstancey setArgumentssegún lo recomendado.
CoolMind
-5

gran respuesta de @Rarw. Intente usar un paquete para pasar información de un fragmento a otro

masterwambua
fuente