Android: Przekaż dane (dodatki) do fragmentu

84

Jestem nowy w programowaniu na Androida i mam problemy podczas przekazywania ArrayList of a Parcelable do fragmentu. Jest to działanie, które jest uruchamiane (działa dobrze!), W którym lista kanałów jest tablicą listy plików muzycznych, które można sparować .

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

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

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

Kod fragmentu:

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

Myślę, że problem leży na linii

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

Wreszcie to moja klasa muzyczna:

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

Kiedy uruchamiam ten kod, pojawia się problem java.lang.NullPointerException w metodzie Fragment onCreateView () . Byłbym wdzięczny, gdyby ktoś wskazał mi właściwy kierunek, aby zobaczyć, gdzie zawodzę.

EDYCJA : Problem rozwiązany: po prostu musiałem dodać tę linię do fragmentu metody Activity onCreate () (w przeciwnym razie getArguments () zwróci wartość null):

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

I dodaj to do kodu fragmentu:

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

gdzie listMusics jest ArrayListod Parcelablemuzyki.

pluralizm
źródło
Pierwszym krokiem będzie otwarcie okna LogCat, przyjrzenie się wyjątkowi i zobaczenie, co powoduje wyjątek NullPointerException. Jeśli nie możesz tego rozgryźć, umieść ślad stosu wyjątku w swoich pytaniach, aby ktoś mógł pomóc.
Brian
BrianV: Dzięki za wskazówkę! Oto LogCat, który otrzymuję po uruchomieniu programu! pastebin.com/ycVcXLFf
pluralizm
Wygląda na to, że masz błąd w pliku układu widoku: ** Spowodowany przez: android.view.InflateException: wiersz pliku binarnego XML nr 10: Błąd nadmuchiwania fragmentu klasy **
Brian
Nie, błąd tkwi w kodzie ListView, jestem tego pewien. Jeśli utworzę obiekty ArrayList of Music w ListMusicFragment, kod działa dobrze. W każdym razie, oto moje pliki układu: pastebin.com/4DwHh1yk
pluralizm

Odpowiedzi:

196

Dwie rzeczy. Po pierwsze, nie sądzę, że poprawnie dodajesz dane, które chcesz przekazać do fragmentu. To, co musisz przekazać fragmentowi, to pakiet, a nie zamiar. Na przykład, gdybym chciał wysłać intwartość do fragmentu, utworzyłbym paczkę, umieściłbym intw tej paczce, a następnie ustawiłbym ten pakiet jako argument, który ma być używany podczas tworzenia fragmentu.

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

Po drugie, aby odzyskać te informacje, musisz pobrać argumenty wysłane do fragmentu. Następnie wyodrębniasz wartość na podstawie klucza, który ją zidentyfikowałeś. Na przykład w Twoim fragmencie:

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

To, co otrzymujesz, zmienia się w zależności od tego, co umieścisz. Wartość domyślna to zazwyczaj, nullale nie musi. Zależy to od tego, czy ustawisz wartość domyślną dla tego argumentu.

Wreszcie nie sądzę, że możesz to zrobić w onCreateView. Myślę, że musisz odzyskać te dane w ramach onActivityCreatedmetody swojego fragmentu . Moje rozumowanie jest następujące. onActivityCreatedjest uruchamiany po zakończeniu działania bazowego własnej onCreatemetody. Jeśli umieszczasz informacje, które chcesz odzyskać w paczce podczas onCreatemetody swojej aktywności , nie będą one istnieć w czasie trwania Twojego fragmentu onCreateView. Spróbuj użyć tego w programie onActivityCreatedi zaktualizuj ListViewzawartość później.

Rarw
źródło
3
Prosta odpowiedź, super. Skopiowałem Twój przykładowy kod i zadziałało!
iamanyone
1
Więc ... co, jeśli argumenty są określone przez obiekty implementujące określone interfejsy, a nie prymitywy?
Piotr
2
Jeśli chcesz użyć obiektu, po prostu upewnij się, że implementują Parcelableinterfejs. Bundleobsługuje dodawanie / pobieranie, Parcelablewięc możesz je również wysłać bez problemu.
Rarw
1
Czy masz przepływ pracy, gdy fragment jest zdefiniowany w layout.xml działania? W takim przypadku uważam, że Android utworzy instancję samego fragmentu, co oznacza, że ​​nie możesz podać argumentów? Osobiście korzystam z eventbus (otto) i producenta, aby przekazać dane z działania do fragmentu.
Alec Holmes,
Jasne - kiedy definiujesz fragment w swoim kodzie XML, użyj atrybutu android: tag, aby ustawić tag dla tego fragmentu. W swoim działaniu możesz użyć FragmentManager.findFragmentByTag (), aby zlokalizować ten fragment, a następnie użyć FragmentTransaction, aby zastąpić fragment XML nowym fragmentem skonfigurowanym tak, jak chcesz. Innym podejściem byłoby użycie findFragmentById i po prostu użycie wartości android: id fragmentu XML.
Rarw
2

Wolę Serializable= bez standardowego kodu. W przypadku przekazywania danych do innych fragmentów lub działań różnica szybkości względem aParcelable nie ma znaczenia.

Ja też zawsze dostarczenie sposobu dla pomocnika Fragmentlub Activity, w ten sposób zawsze wiesz, jakie dane mają być przekazywane. Oto przykład dla twojego 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
źródło
1

Jest proste, dlaczego wolałem pakiet ze względu na brak zduplikowanych danych w pamięci. Składa się z publicznej metody init dla fragmentu

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

W dwóch słowach, tworzysz instancję fragmentu za pomocą metody init (możesz ją wywołać jak chcesz) przekazujesz referencję swojej listy bez tworzenia kopii przez serializację do instancji fragmentu. Jest to bardzo przydatne, ponieważ jeśli zmienisz coś na liście, otrzymasz to w innych częściach aplikacji i oczywiście zużyjesz mniej pamięci.

Ivan
źródło
1
W Androidzie jest to zły sposób. Ponieważ przy odtwarzaniu fragmentu (przy zmianie konfiguracji (obrót ekranu) i kilku innych) wywoła pusty konstruktor, a wszystkie parametry zostaną utracone. Użyj newInstancei setArgumentszgodnie z zaleceniami.
CoolMind
-5

świetna odpowiedź od @Rarw. Spróbuj użyć pakietu, aby przekazać informacje z jednego fragmentu do drugiego

masterwambua
źródło