Android: передайте дані (додатки) фрагменту


83

Я новачок у програмуванні Android, і у мене виникають проблеми під час передачі ArrayList Parcelable фрагменту. Це Діяльність, яка запускається (працює добре!), Де канал подання - це ArrayList музики, що підлягає посиланню .

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

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

Метод фрагмента 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());
    }
}

Код фрагмента:

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

Думаю, проблема в суті

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

Нарешті, це мій музичний клас:

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

Коли я запускаю цей код, проблемою, яку я отримую, є java.lang.NullPointerException у методі Fragment onCreateView () . Я був би дуже вдячний, якби хтось направив мене у правильному напрямку, щоб побачити, де я зазнаю невдачі.

EDIT : Проблема вирішена: мені просто потрібно було додати цей рядок до методу фрагмента Activity onCreate () (або, якщо getArguments () поверне нуль):

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

І додайте це до коду фрагмента:

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

де listMusics це ArrayListз Parcelableмузики.


Першим кроком буде відкрити вікно LogCat, переглянути виняток і подивитися, що спричиняє NullPointerException. Якщо ви не можете це зрозуміти, то додайте стек із винятку у свої запитання, або хтось може вам допомогти.
Брайан

BrianV: Дякую за підказку! Ось це LogCat, який я отримую при запуску програми! pastebin.com/ycVcXLFf
плюралізм

Схоже, у вас сталася помилка у файлі макета подання: ** Викликано: android.view.InflateException: Бінарний рядок XML-файлу №10: Помилка роздування фрагмента класу **
Брайан

Ні, помилка в коді ListView, я в цьому впевнений. Якщо я створюю ArrayList of Music об'єкти в ListMusicFragment, код працює просто чудово. У будь-якому випадку, це мої файли макета: pastebin.com/4DwHh1yk
плюралізм

Відповіді:


196

Дві речі. По-перше, я не думаю, що ви правильно додаєте дані, які хочете передати у фрагмент. Вам потрібно передати фрагменту пакет, а не намір. Наприклад, якщо я хотів надіслати intзначення фрагменту, я б створив пакет, помістив його intв цей пакет, а потім встановив цей пакет як аргумент, який буде використовуватися при створенні фрагмента.

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

По-друге, для отримання цієї інформації вам потрібні аргументи, надіслані фрагменту. Потім ви витягуєте значення на основі ключа, з яким його ідентифікували. Наприклад у вашому фрагменті:

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

Те, що ви отримуєте, змінюється залежно від того, що ви ставите. Також значення за замовчуванням, як правило, nullале не потрібно. Це залежить від того, чи встановлено значення за замовчуванням для цього аргументу.

Нарешті, я не думаю, що ви можете це зробити onCreateView. Я думаю, ви повинні отримати ці дані в рамках onActivityCreatedметоду вашого фрагмента . Мої міркування такі. onActivityCreatedзапускається після того, як основна діяльність закінчила власний onCreateметод. Якщо ви розміщуєте інформацію, яку ви хочете отримати, у наборі під час onCreateметоду вашої активності , вона не буде існувати під час вашого фрагмента onCreateView. Спробуйте використовувати це в onActivityCreatedі просто оновіть свій ListViewвміст пізніше.


3
Проста відповідь, приголомшливо. Скопійовано приклад коду, і це вдало!
iamanyone

1
Отже ... що, якщо аргументи задаються об’єктами, що реалізують конкретні інтерфейси, а не примітивами?
Piotr

2
Якщо ви хочете використовувати об'єкт, просто переконайтеся, що він реалізує Parcelableінтерфейс. Bundleпідтримує додавання / отримання, Parcelableщоб ви могли надсилати їх також без проблем.
Rarw

1
Чи є у вас робочий процес, коли фрагмент визначено в layout.xml діяльності? У цьому випадку я вважаю, що android створить екземпляр самого фрагмента, тобто ви не можете наводити аргументи? Особисто я використовую шину подій (otto) та продюсера, щоб передати дані з активності фрагменту.
Алек Холмс,

Звичайно - коли ви визначаєте фрагмент у своєму XML, використовуйте атрибут android: tag, щоб встановити тег для цього фрагмента. У вашій діяльності ви можете використовувати FragmentManager.findFragmentByTag (), щоб знайти цей фрагмент, а потім використовувати FragmentTransaction, щоб замінити фрагмент XML новим фрагментом, налаштованим так, як ви хочете. Іншим підходом було б використовувати findFragmentById і просто використовувати значення android: id вашого XML-фрагмента.
Rarw

2

Я віддаю перевагу Serializable= відсутність типового коду. Для передачі даних до інших фрагментів або дій різниця швидкості доParcelable значення не має значення.

Я також завжди надав би допоміжний метод для Fragmentабо Activity, таким чином, ви завжди знаєте, які дані потрібно передавати. Ось приклад для вашого 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);
    ...
}

1

Існує проста причина, чому я віддав перевагу набору через відсутність дублікатів даних у пам'яті. Він складається з відкритого методу ініціалізації для фрагмента

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

У двох словах, ви створюєте екземпляр фрагмента за допомогою методу init (ви можете назвати його як вам потрібно), ви передаєте посилання на свій список, не створюючи копії шляхом серіалізації до екземпляра фрагмента. Це дуже корисно, тому що якщо ви щось зміните у списку, ви отримаєте це в інших частинах програми та, звичайно, ви використовуєте менше пам'яті.


1
В Android це неправильний шлях. Оскільки при відтворенні фрагментів (при зміні конфігурації (обертання екрану) та деяких інших) він буде викликати порожній конструктор, і всі параметри будуть втрачені. Використовуйте newInstanceта відповідно setArgumentsдо рекомендацій.
CoolMind

-5

чудова відповідь від @Rarw. Спробуйте використовувати пакет для передачі інформації від одного фрагмента до іншого

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.