Android: Daten (Extras) an ein Fragment übergeben

83

Ich bin neu in der Android-Programmierung und habe Probleme beim Übergeben einer ArrayList eines Pakets an ein Fragment. Dies ist die Aktivität , die gestartet wird (gut funktioniert!) , Wo Feed ist ein Arraylist einer Parcel Musik .

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

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

Die Fragment Activity onCreate () -Methode:

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

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

Der Fragmentcode:

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

Ich denke, das Problem liegt in der Leitung

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

Endlich ist das meine Musikklasse:

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

Wenn ich diesen Code ausführe, ist das Problem eine java.lang.NullPointerException in der Fragment onCreateView () -Methode . Ich würde mich sehr freuen, wenn mich jemand in die richtige Richtung weisen würde, um zu sehen, wo ich versage.

BEARBEITEN : Problem gelöst: Ich musste nur diese Zeile zur Fragment Activity onCreate () -Methode hinzufügen (andernfalls würde getArguments () null zurückgeben):

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

Und fügen Sie dies dem Fragmentcode hinzu:

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

wo ist listMusics ein ArrayListvon ParcelableMusik.

Pluralismus
quelle
Der erste Schritt besteht darin, das LogCat-Fenster zu öffnen, die Ausnahme zu überprüfen und festzustellen, was die NullPointerException verursacht. Wenn Sie es nicht herausfinden können, fügen Sie den Stacktrace aus der Ausnahme in Ihre Fragen ein, damit jemand helfen kann.
Brian
BrianV: Danke für den Tipp! Hier ist es die LogCat, die ich bekomme, wenn ich das Programm ausführe! pastebin.com/ycVcXLFf
Pluralismus
Sieht so aus, als hätten Sie einen Fehler in Ihrer Ansichtslayoutdatei: ** Auslöser: android.view.InflateException: Binäre XML-Dateizeile Nr. 10: Fehler beim Aufblasen des Klassenfragments **
Brian
Nein, der Fehler liegt im Code der ListView, da bin ich mir sicher. Wenn ich im ListMusicFragment eine ArrayList of Music-Objekte erstelle, funktioniert der Code einwandfrei. Wie auch immer, hier sind meine Layout-Dateien: pastebin.com/4DwHh1yk
Pluralismus

Antworten:

196

Zwei Dinge. Erstens glaube ich nicht, dass Sie die Daten, die Sie an das Fragment übergeben möchten, korrekt hinzufügen. Was Sie an das Fragment übergeben müssen, ist ein Bündel, keine Absicht. Wenn ich beispielsweise einen intWert an ein Fragment senden möchte, würde ich ein Bundle erstellen, das intin dieses Bundle einfügen und dieses Bundle dann als Argument festlegen, das beim Erstellen des Fragments verwendet werden soll.

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

Zweitens, um diese Informationen abzurufen, müssen Sie die Argumente an das Fragment senden lassen. Anschließend extrahieren Sie den Wert basierend auf dem Schlüssel, mit dem Sie ihn identifiziert haben. Zum Beispiel in Ihrem Fragment:

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

Was Sie bekommen, ändert sich je nachdem, was Sie setzen. Auch der Standardwert ist normalerweise null, muss aber nicht sein. Dies hängt davon ab, ob Sie einen Standardwert für dieses Argument festlegen.

Schließlich glaube ich nicht, dass Sie dies in tun können onCreateView. Ich denke, Sie müssen diese Daten innerhalb der onActivityCreatedMethode Ihres Fragments abrufen . Meine Argumentation ist wie folgt. onActivityCreatedwird ausgeführt, nachdem die zugrunde liegende Aktivität ihre eigene onCreateMethode beendet hat. Wenn Sie die Informationen, die Sie abrufen möchten, onCreatewährend der Methode Ihrer Aktivität in das Bundle einfügen , sind sie während der Fragmente nicht vorhanden onCreateView. Versuchen Sie dies in onActivityCreatedund aktualisieren Sie Ihre ListViewInhalte später.

Rarw
quelle
3
Einfache Antwort, großartig. Kopierte dein Codebeispiel und es hat geklappt!
Iamanyone
1
Also ... was ist, wenn Argumente von Objekten angegeben werden, die bestimmte Schnittstellen und keine Grundelemente implementieren?
Piotr
2
Wenn Sie ein Objekt verwenden möchten, stellen Sie einfach sicher, dass diese die ParcelableSchnittstelle implementieren . Bundleunterstützt das Hinzufügen / AbrufenParcelable so dass Sie diese auch problemlos senden können.
Rarw
1
Haben Sie einen Workflow, wenn das Fragment in der layout.xml der Aktivität definiert ist? In diesem Fall glaube ich, dass Android das Fragment selbst instanziiert, was bedeutet, dass Sie keine Argumente liefern können? Persönlich verwende ich einen Eventbus (otto) und einen Produzenten, um die Daten von der Aktivität an das Fragment zu übergeben.
Alec Holmes
Sicher - wenn Sie das Fragment in Ihrem XML definieren, verwenden Sie das Attribut android: tag, um ein Tag für dieses Fragment festzulegen. In Ihrer Aktivität können Sie FragmentManager.findFragmentByTag () verwenden, um dieses Fragment zu suchen, und dann eine FragmentTransaction verwenden, um das XML-Fragment durch ein neues Fragment zu ersetzen, das wie gewünscht konfiguriert wurde. Ein anderer Ansatz wäre, findFragmentById zu verwenden und einfach den android: id-Wert Ihres XML-Fragments zu verwenden.
Rarw
2

Ich bevorzuge Serializable= kein Boilerplate-Code. Für die Übergabe von Daten an andere Fragmente oder Aktivitäten spielt der Geschwindigkeitsunterschied zu aParcelable keine Rolle.

Ich würde auch immer eine Hilfsmethode für a bereitstellen Fragment oder auf Activitydiese Weise immer wissen, welche Daten übergeben werden müssen. Hier ein Beispiel für Ihre 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
quelle
1

Es gibt einen einfachen Grund, warum ich das Bundle aufgrund der fehlenden doppelten Daten im Speicher vorgezogen habe. Es besteht aus einer öffentlichen init-Methode für das Fragment

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

Mit zwei Worten, Sie erstellen eine Instanz des Fragments und übergeben mit der init-Methode (Sie können es wie gewünscht aufrufen) die Referenz Ihrer Liste, ohne eine Kopie durch Serialisierung an die Instanz des Fragments zu erstellen. Dies ist sehr nützlich, denn wenn Sie etwas in der Liste ändern und es in den anderen Teilen der App erhalten, verbrauchen Sie natürlich weniger Speicher.

Ivan
quelle
1
In Android ist es ein falscher Weg. Denn bei der Fragmentwiederherstellung (bei Konfigurationsänderung (Bildschirmdrehung) und einigen anderen) wird ein leerer Konstruktor aufgerufen, und alle Parameter gehen verloren. Verwenden Sie newInstanceund setArgumentswie empfohlen.
CoolMind
-4

tolle Antwort von @Rarw. Versuchen Sie, mithilfe eines Bundles Informationen von einem Fragment an ein anderes zu übergeben

Masterwambua
quelle