Android:向片段传递数据(extras)

87

我刚开始学习 Android 编程,我在将 Parcelable 类型的 ArrayList 传递到 Fragment 时遇到了问题。这是启动的 Activity(运行良好!)feedlist 是一个Parcelable类型的ArrayListMusic

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

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

片段(Fragment)的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());
    }
}

Fragment代码:

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];
    }
};
}
当我运行这段代码时,我遇到的问题是在Fragment的onCreateView()方法中出现了java.lang.NullPointerException。 如果有人能指点我一下,让我知道我错在哪里,我会非常感激。
编辑:问题解决了:我只需要在Fragment 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 Music。


第一步是打开LogCat窗口,查看异常并查看是什么导致了NullPointerException。如果您无法找出原因,请将异常的堆栈跟踪放入问题中以寻求帮助。 - Brian
BrianV:谢谢你的提示!这是我执行程序时得到的LogCat日志!http://pastebin.com/ycVcXLFf - pluralism
看起来你的视图布局文件有错误:导致原因:android.view.InflateException:二进制 XML 文件第 #10 行:错误膨胀类片段 - Brian
不,错误在ListView的代码中,我很确定。如果我在ListMusicFragment中创建一个Music对象的ArrayList,那么代码就可以正常工作。无论如何,这是我的布局文件:http://pastebin.com/4DwHh1yk - pluralism
3个回答

203

两件事。首先,我认为您没有正确地添加要传递给片段的数据。您需要传递一个Bundle而不是Intent。例如,如果我想要向一个片段发送一个int值,我会创建一个Bundle,将int放入该Bundle中,然后将该Bundle设置为在创建片段时使用的参数。

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 方法期间将要检索的信息放置在 bundle 中,那么在片段的 onCreateView 中它将不存在。请尝试在 onActivityCreated 中使用此项,并稍后更新您的 ListView 内容。


3
简单回答,太好了。我复制了你的代码示例,结果非常好! - iamanyone
1
那么,如果参数由实现特定接口的对象指定而不是原始类型呢? - Piotr
2
如果你想使用对象,请确保它们实现了“Parcelable”接口。Bundle支持添加/获取Parcelable,因此您也可以轻松发送它们。 - Rarw
1
你是否有针对在activity的layout.xml中定义fragment的工作流程?在这种情况下,我认为Android会自行实例化fragment,这意味着你无法提供参数?个人而言,我使用了一个事件总线(otto)和生产者来将数据从activity传递到fragment。 - Alec Holmes
当您在XML中定义片段时,请使用android:tag属性为该片段设置标记。在您的活动中,您可以使用FragmentManager.findFragmentByTag()来定位该片段,然后使用FragmentTransaction将XML片段替换为新的片段,并按照您想要的方式进行配置。另一种方法是使用findFragmentById并简单地使用您的XML片段的android:id值。 - Rarw
显示剩余2条评论

2
我喜欢Serializable,因为它没有样板代码。对于将数据传递到其他片段或活动,与Parcelable相比速度差异并不重要。 我还会为FragmentActivity提供帮助方法,以便您始终知道需要传递什么数据。下面是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

由于内存中没有重复数据,我更喜欢bundle的简单方法。它包括片段的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方法(可以随意调用)将列表的引用传递给片段实例,而不是通过序列化创建副本。这非常有用,因为如果您更改列表中的内容,则会在应用程序的其他部分中得到它,当然,您使用的内存更少。

3
在Android中,这是错误的方式。因为在片段重建(配置更改(屏幕旋转)和其他一些情况下),它将调用一个空构造函数,并且所有参数都将丢失。建议使用newInstancesetArguments - CoolMind

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接