Android: передача данных (дополнительных) во фрагмент

84

Я новичок в программировании Android, и у меня возникают проблемы при передаче ArrayList Parcelable фрагменту. Это запущенное действие (работающее хорошо!), Где список каналов представляет собой список массивов музыки .

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 () . Я был бы очень признателен, если бы кто-нибудь указал мне правильное направление, чтобы увидеть, где я терплю неудачу.

РЕДАКТИРОВАТЬ : Проблема решена: мне просто нужно было добавить эту строку в метод фрагмента Activity onCreate () (в противном случае getArguments () вернет null):

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содержимое позже.

Rarw
источник
3
Простой ответ, круто. Скопировал ваш пример кода, и это сработало!
iamanyone
1
Итак ... что, если аргументы указаны объектами, реализующими определенные интерфейсы, а не примитивами?
Петр
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);
    ...
}
artkoenig
источник
1

Я предпочел связку по простой причине, потому что в памяти нет повторяющихся данных. Он состоит из публичного метода init для фрагмента

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. Попробуйте использовать пакет для передачи информации от одного фрагмента к другому

мастервамбуа
источник