Android: Truyền dữ liệu (tính năng bổ sung) vào một phân đoạn


83

Tôi là người mới lập trình Android và tôi đang gặp sự cố khi chuyển ArrayList of a Parcelable vào một phân mảnh. Đây là Activity được khởi chạy (hoạt động tốt!) Trong đó feedlist là ArrayList của một bản nhạc có giá trị .

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

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

Phương thức Activity onCreate () phân mảnh:

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

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

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

Tôi nghĩ vấn đề nằm ở hàng

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

Cuối cùng đây là lớp học Âm nhạc của tôi:

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

Khi tôi chạy mã này, vấn đề tôi nhận được là java.lang.NullPointerException trong phương thức Fragment onCreateView () . Tôi sẽ đánh giá cao rất nhiều nếu ai đó chỉ cho tôi đúng hướng để xem tôi đang thất bại ở đâu.

CHỈNH SỬA : Sự cố đã được giải quyết: Tôi chỉ cần thêm dòng này vào phương thức Activity onCreate () phân mảnh (othwewise the getArguments () sẽ trả về null):

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

Và thêm điều này vào mã phân đoạn:

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

trong đó, listMusics là một ArrayListcủa ParcelableÂm nhạc.


Bước đầu tiên là mở cửa sổ LogCat, nhìn vào ngoại lệ và xem điều gì gây ra NullPointerException. Nếu bạn không thể tìm ra điều đó, thì hãy đặt ngăn xếp từ ngoại lệ vào các câu hỏi của bạn để ai đó có thể giúp.
Brian

BrianV: Cảm ơn vì mẹo! Đây là LogCat tôi nhận được khi thực hiện chương trình! pastebin.com/ycVcXLFf
pluralism

Có vẻ như bạn gặp lỗi trong tệp bố cục chế độ xem của mình: ** Nguyên nhân do: android.view.InflateException: Dòng tệp XML nhị phân # 10: Lỗi thổi phồng phân đoạn lớp **
Brian

Không, lỗi nằm ở mã của ListView, tôi chắc chắn về điều đó. Nếu tôi tạo một đối tượng ArrayList of Music trong ListMusicFragment, mã hoạt động tốt. Dù sao, ở đây nó là file layout của tôi: pastebin.com/4DwHh1yk
đa nguyên

Câu trả lời:


196

Hai điều. Đầu tiên, tôi không nghĩ rằng bạn đang thêm dữ liệu mà bạn muốn chuyển vào phân đoạn một cách chính xác. Những gì bạn cần chuyển tới phân mảnh là một gói chứ không phải một ý định. Ví dụ: nếu tôi muốn gửi một intgiá trị đến một phân đoạn, tôi sẽ tạo một gói, đặt intgói đó và sau đó đặt gói đó làm đối số được sử dụng khi phân đoạn được tạo.

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

Thứ hai để truy xuất thông tin đó, bạn cần lấy các đối số được gửi đến phân mảnh. Sau đó, bạn trích xuất giá trị dựa trên khóa mà bạn đã xác định nó. Ví dụ trong phân đoạn của bạn:

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

Những gì bạn nhận được sẽ thay đổi tùy thuộc vào những gì bạn đặt. Ngoài ra, giá trị mặc định thường là nullnhưng không cần thiết. Nó phụ thuộc vào việc bạn đặt giá trị mặc định cho đối số đó hay không.

Cuối cùng, tôi không nghĩ rằng bạn có thể làm điều này trong onCreateView. Tôi nghĩ rằng bạn phải truy xuất dữ liệu này trong onActivityCreatedphương pháp phân mảnh của bạn . Lý luận của tôi như sau. onActivityCreatedchạy sau khi hoạt động cơ bản đã hoàn thành onCreatephương thức riêng của nó . Nếu bạn đang đặt thông tin mà bạn muốn lấy trong gói làm mờ onCreatephương thức hoạt động của bạn , thì thông tin đó sẽ không tồn tại trong phân đoạn của bạn onCreateView. Hãy thử sử dụng điều này trong onActivityCreatedvà chỉ cần cập nhật ListViewnội dung của bạn sau.


3
Câu trả lời đơn giản, tuyệt vời. Đã sao chép ví dụ mã của bạn và nó hoạt động tốt!
iamanyone

1
Vì vậy, ... điều gì sẽ xảy ra nếu các đối số được chỉ định bởi các đối tượng thực hiện các giao diện cụ thể chứ không phải nguyên thủy?
Piotr

2
Nếu bạn muốn sử dụng đối tượng, chỉ cần đảm bảo rằng chúng triển khai Parcelablegiao diện. Bundlehỗ trợ thêm / nhận Parcelableđể bạn có thể gửi chúng mà không có vấn đề gì.
Rarw

1
Bạn có quy trình làm việc khi phân đoạn được xác định trong layout.xml của hoạt động không? Trong trường hợp này, tôi tin rằng android sẽ tự khởi tạo phân đoạn có nghĩa là bạn không thể cung cấp đối số? Cá nhân tôi đang sử dụng một eventbus (otto) và một trình sản xuất để chuyển dữ liệu từ hoạt động sang phân mảnh.
Alec Holmes

Chắc chắn rồi - khi bạn xác định phân đoạn trong XML của mình, hãy sử dụng thuộc tính android: tag để đặt thẻ cho phân đoạn đó. Trong hoạt động của mình, bạn có thể sử dụng FragmentManager.findFragmentByTag () để định vị đoạn đó và sau đó sử dụng FragmentTransaction để thay thế đoạn XML bằng một đoạn mới được định cấu hình theo cách bạn muốn. Một cách tiếp cận khác là sử dụng findFragmentById và chỉ cần sử dụng giá trị android: id của phân đoạn XML của bạn.
Rarw

2

Tôi thích Serializable= không có mã soạn sẵn. Đối với việc truyền dữ liệu đến các Phân đoạn hoặc Hoạt động khác, sự khác biệt về tốc độ đối với aParcelable không thành vấn đề.

Tôi cũng sẽ luôn cung cấp một phương thức trợ giúp cho một Fragmenthoặc Activity, theo cách này, bạn luôn biết, dữ liệu nào phải được chuyển. Đây là một ví dụ cho 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

Có một lý do đơn giản tại sao tôi thích gói do không có dữ liệu trùng lặp trong bộ nhớ. Nó bao gồm một phương thức công khai init cho phân đoạn

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

Nói cách khác, bạn tạo một thể hiện của đoạn an bằng phương thức init (bạn có thể gọi nó như bạn muốn), bạn chuyển tham chiếu đến danh sách của mình mà không cần tạo một bản sao bằng cách tuần tự hóa cho đối tượng của đoạn. Điều này rất hữu ích vì nếu bạn thay đổi thứ gì đó trong danh sách, bạn sẽ nhận được nó trong các phần khác của ứng dụng và dĩ nhiên, bạn sử dụng ít bộ nhớ hơn.


1
Trong Android, đó là một cách sai lầm. Bởi vì khi tái tạo phân mảnh (khi thay đổi cấu hình (xoay màn hình) và một số khác), nó sẽ gọi một hàm tạo trống và tất cả các tham số sẽ bị mất. Sử dụng newInstancesetArgumentstheo khuyến nghị.
CoolMind

-5

câu trả lời tuyệt vời của @Rarw. Hãy thử sử dụng một gói để chuyển thông tin từ phân đoạn này sang phân đoạn khác

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.