在Android中从活动发送数据到片段

361

我有两个类。第一个是活动(activity),第二个是片段(fragment),在其中我有一些EditText. 在活动(activity)中,我有一个带有异步任务的子类,并在方法doInBackground中获取一些结果,然后将其保存到变量中。我如何将这个变量从"我的活动"(my activity)的子类发送到这个片段(fragment

)?
24个回答

743

从 Activity 中使用 Intent 发送数据的方式如下:

Bundle bundle = new Bundle();
bundle.putString("edttext", "From Activity");
// set Fragmentclass Arguments
Fragmentclass fragobj = new Fragmentclass();
fragobj.setArguments(bundle);

在Fragment的onCreateView方法中:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    String strtext = getArguments().getString("edttext");    
    return inflater.inflate(R.layout.fragment, container, false);
}

73
调用getArguments().getString(key)时出现了空指针异常。 - Nima GT
9
空指针异常 "String strtext = setArguments().getString("edttext");" - Jorgesys
4
在读取片段中的捆绑内容时,始终首先使用 getArguments 方法将捆绑接收到 Bundle 对象中,并检查其是否为 null。否则,将在 null 上应用 getString 方法,因此当未传递任何 bundle 时会出现 NPE。这将避免在未传递 bundle 时发生空指针异常。 - balachandarkm
3
不建议为片段创建构造函数。 - Azam
2
@DileepaNipunSalinda:是的,但请确保类实现了Serializable或Parcelable。 - ρяσѕρєя K
显示剩余24条评论

125

同时您可以从片段中访问活动数据:

活动:

public class MyActivity extends Activity {

    private String myString = "hello";

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

    public String getMyData() {
        return myString;
    }
}

片段:

public class MyFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        MyActivity activity = (MyActivity) getActivity();
        String myDataFromActivity = activity.getMyData();
        return view;
    }
}

90
这个解决方案将你的活动和片段紧密耦合,最好使用 Bundle 类来传递信息。或者,您可以使 getMyData() 方法从一个接口继承并实现,并让 FragmentonAttach() 中检查 getActivity 是否返回接口的 instanceof - Rudi Kershaw
1
这个解决方案对我最好。另外,如果myString是公共的,你没有声明getMyData()方法。 - Phd. Burak Öztürk
2
当前被接受的答案返回空指针异常。这应该是被接受的答案。 - Richmond
11
这不应该成为被接受的答案。紧密耦合是可以轻松避免的。这通常是一个不好的想法,使得片段变得无用,如果你只会将其与一个活动绑定使用,那么也许就没有必要有一个片段了。该片段不能在其他活动中重复使用。 - Martin Marconcini
这对我的需求有效,我需要传递一个自定义对象列表,而我不认为使用Bundle答案可以做到。 - Eman

59

我在stackoverflow.com上找到了很多答案,但肯定这是“在Android中从活动向片段发送数据”的正确答案。

活动:

        Bundle bundle = new Bundle();
        String myMessage = "Stackoverflow is cool!";
        bundle.putString("message", myMessage );
        FragmentClass fragInfo = new FragmentClass();
        fragInfo.setArguments(bundle);
        transaction.replace(R.id.fragment_single, fragInfo);
        transaction.commit();

片段(Fragment):

读取片段中的值

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Bundle bundle = this.getArguments();
        String myValue = bundle.getString("message");
        ...
        ...
        ...
        }

或者只是

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        String myValue = this.getArguments().getString("message");
        ...
        ...
        ...
        }

我猜这不是最好的方法来更新嵌套在Fragment中的ListView中的onQueryTextSubmit-search-results(如果有人打字很快,他会每秒发送两次参数)? - Martin Pfeffer
3
用对象代替字符串或整数怎么样? - Dario
3
这个解决方案怎么样?https://developer.android.com/training/basics/fragments/communicating.html - yozhik
谢谢Yozhik,我知道他在随机“transaction”变量上漏了些东西。 - Anon

31

这个答案可能有些晚了,但对于未来的读者仍然有用。

我有一些标准。我已经编写了从意图中选择文件并将所选文件传递给特定片段以进行进一步处理的代码。我有许多具有文件选择功能的片段。每次检查条件并获取片段以传递值相当繁琐。因此,我决定使用接口传递值。

步骤1:在主活动上创建接口。

   public interface SelectedBundle {
    void onBundleSelect(Bundle bundle);
   }

步骤2:在同一活动上创建SelectedBundle引用

   SelectedBundle selectedBundle;

第三步:在同一活动中创建该方法

   public void setOnBundleSelected(SelectedBundle selectedBundle) {
       this.selectedBundle = selectedBundle;
   }

步骤 4:需要初始化 SelectedBundle 引用,这些引用是所有需要文件选择器功能的碎片。您可以将此代码放置在您的碎片 onCreateView(..) 方法中。

    ((MainActivity)getActivity()).setOnBundleSelected(new MainActivity.SelectedBundle() {
          @Override
         public void onBundleSelect(Bundle bundle) {
            updateList(bundle);
        }
     });

步骤5: 我的情况是需要将图像Uri从HomeActivity传递到片段。因此,我在onActivityResult方法中使用了这个功能。

从MainActivity的onActivityResult方法中,使用接口将值传递给碎片。

注意:您的情况可能不同。您可以从任何地方调用它,只要在HomeActivity中就可以。

 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent  data) {
       selectedBundle.onBundleSelect(bundle);
  }

就这些了。在FragmentClass上实现您需要的每个片段。你很棒。你做到了。哇...


20

最好和方便的方法是在调用片段实例的同时发送数据。每个片段默认都有instance方法

例如: 如果你的片段名称是MyFragment

因此,您将从活动中调用您的片段,如下所示:

getSupportFragmentManager().beginTransaction().add(R.id.container, MyFragment.newInstance("data1","data2"),"MyFragment").commit();

*R.id.container是我FrameLayout的ID

因此,在MyFragment.newInstance("data1","data2")中,您可以将数据发送到片段中,并在片段中使用MyFragment newInstance(String param1, String param2)获取这些数据。

public static MyFragment newInstance(String param1, String param2) {
        MyFragment fragment = new MyFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

然后在片段的onCreate方法中,您将获得数据:

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }
    }

现在 mParam1 有 data1 的数据mParam2 有 data2 的数据

现在你可以在你的碎片中使用这些mParam1mParam2


这里的R.id.container是什么?你是不是指的R.id.container_current?这是一个整数值。 - Shaurya Uppal
@s *R.id.container 是我 FrameLayout 的一个 ID。 - Pre_hacker
到目前为止,这是我发现的关于使用Fragment Manager传递数据以添加/替换片段的最好解释。非常好用!谢谢! - vss
使用 R.id.nav_host_fragment 而不是 R.id.container。 - Bahaa Hany

16

使用Fragments(F)的基本思想是在Android应用程序中创建可重复使用的自包含UI组件。这些Fragments包含在活动中,并且有通用(最佳)的创建A->F和F->A通信路径的方式。必须通过活动在F-F之间进行通信,因为只有这样Fragments才变得解耦且自包含。

因此,从A->F传递数据与 ρяσѕρєя K所解释的一样。除此之外,在Activity内部创建Fragments之后,我们还可以通过调用Fragments中的方法将数据传递给Fragments。

例如:

    ArticleFragment articleFrag = (ArticleFragment)
                    getSupportFragmentManager().findFragmentById(R.id.article_fragment);
    articleFrag.updateArticleView(position);

请参考以下链接:https://developer.android.com/training/basics/fragments/communicating.html - yozhik

13
我想为初学者补充一点,这两个最受欢迎的答案之间的区别在于对片段的不同使用。
如果您在具有要传递的数据的Java类中使用片段,则可以使用第一个答案来传递数据。
Bundle bundle = new Bundle();
bundle.putString("edttext", "From Activity");
Fragmentclass fragobj = new Fragmentclass();
fragobj.setArguments(bundle);

然而,如果您使用的是Android Studio提供的标签碎片默认代码,则此代码将无法正常工作。

即使您将默认的PlaceholderFragment替换为您的FragmentClasses,甚至在将FragmentPagerAdapter更改为新情况并添加一个用于getItem()的switch和另一个用于getPageTitle()的switch后,它也无法正常工作(如此处所示)。

警告:上述剪辑存在代码错误,我稍后会在这里进行解释,但如果您考虑该剪辑中的java类和xml文件(代表初学者场景下标签碎片的首次使用),则仍然很有用!

此页面上排名最高的答案无法正常工作的主要原因是,在标签碎片的默认代码中,碎片在另一个java类中使用:FragmentPagerAdapter!

因此,为了发送数据,您可能会尝试在MotherActivity中创建一个bundle,并在FragmentPagerAdapter中传递它,使用答案2。

只是这又是错误的。(可能您可以这样做,但这只是一个不真正需要的复杂性)。

我认为正确/更简单的方法是直接将数据传递给相关的片段,使用答案2。 是的,活动和片段之间会有紧密耦合,但是对于选项卡片段,这是预期的。我甚至建议您在MotherActivity java类中创建选项卡片段(作为子类,因为它们永远不会在MotherActivity外部使用)-这很容易,只需像这样在MotherActivity java类中添加所需数量的片段:

 public static class Tab1 extends Fragment {

    public Tab1() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.your_layout_name_for_fragment_1, container, false);
        return rootView;
    }
}.

因此,要将数据从MotherActivity传递到这样的Fragment,您需要在Mother activity的onCreate之前创建私有的Strings/Bundles,您可以使用要传递给片段的数据来填充它们,并通过在onCreate之后创建的方法(此处称为getMyData())传递它们。

public class MotherActivity extends Activity {

    private String out;
    private Bundle results;

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

       // for example get a value from the previous activity
        Intent intent = getIntent();
        out = intent.getExtras().getString("Key");

    }

    public Bundle getMyData() {
        Bundle hm = new Bundle();
        hm.putString("val1",out);
        return hm;
    }
}

然后在片段类中,您使用getMyData:

public static class Tab1 extends Fragment {
        /**
         * The fragment argument representing the section number for this
         * fragment.
         */
        public Tab1() {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.your_layout_name_for_fragment_1, container, false);
            TextView output = (TextView)rootView.findViewById(R.id.your_id_for_a_text_view_within_the_layout);

            MotherActivity activity = (MotherActivity)getActivity();

            Bundle results = activity.getMyData();
            String value1 = results.getString("val1");

            output.setText(value1);
            return rootView;
        }
    }

如果您有数据库查询,请建议您在MotherActivity中执行它们(并将它们的结果作为附加到键的字符串/整数传递给Bundle),因为在选项卡片段中,您的语法会变得更加复杂(例如,这变成了getActivity(),而getIntent则变成了getActivity().getIntent),但您也可以按照自己的意愿进行操作。对于初学者的建议是专注于小步骤。首先,获取您的意图以打开一个非常简单的选项卡式活动,而不传递任何数据。它工作吗?它打开了您期望的选项卡吗?如果没有,为什么?从那里开始,通过应用此剪辑中提出的解决方案,看看缺少什么。对于那个特定的剪辑,mainactivity.xml从未显示。那肯定会让你困惑。但是,如果您注意一下,您会发现例如xml片段文件中的上下文(tools:context)是错误的。每个片段XML都需要指向正确的片段类(或使用分隔符$的子类)。您还将看到,在主要活动Java类中,您需要添加tabLayout.setupWithViewPager(mViewPager) - 就在TabLayout tabLayout =(TabLayout)findViewById(R.id.tabs)的下一行之后;如果没有此行,则您的视图实际上未链接到片段的XML文件,而仅显示主要活动的xml文件。
除了主活动Java类中的行外,您还需要在主活动XML文件中更改选项卡以适应您的情况(例如添加或删除TabItems)。如果您在主活动XML中没有选项卡,则可能在创建它时没有选择正确的活动类型(新活动-选项卡式活动)。
请注意,在最后三段中,我谈论的是视频!因此,当我说主活动XML时,它是视频中的主活动XML,在您的情况下是MotherActivity XML文件。

8

Activity中,您可以通过Bundle将数据发送如下:

Bundle bundle = new Bundle();
bundle.putString("data", "Data you want to send");

// Your fragment
MyFragment obj = new MyFragment();
obj.setArguments(bundle);

Fragment的onCreateView方法中获取数据:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
{
    String data = getArguments().getString("data");// data which sent from activity  
    return inflater.inflate(R.layout.myfragment, container, false);
}

8
如果你将(具体子类的)fragment 的引用传递给异步任务,那么你就可以直接访问该 fragment。传递 fragment 引用的一些方式包括:
  • 如果你的异步任务是一个完整的类(class FooTask extends AsyncTask),那么就将 fragment 传递到构造函数中。
  • 如果你的异步任务是一个内部类,只需在定义异步任务的范围内声明一个 final Fragment 变量,或者作为外部类的字段。你可以从内部类中访问它。

7
有时您在活动中收到Intent并需要将信息传递给工作片段。
如果需要启动该片段,给出的答案是可以的,但如果它仍在工作中,则setArguments()没有太大用处。
如果传递的信息会导致与用户界面交互的问题,则不能调用类似于myfragment.passData()的内容,因为Android会迅速告诉您只有创建视图的线程才能进行交互。

因此我的建议是使用接收器。这样,您可以从任何地方发送数据,包括活动,但工作将在片段的上下文中完成。

在您的片段的onCreate()中:

protected DataReceiver dataReceiver;
public static final String REC_DATA = "REC_DATA";

@Override
public void onCreate(Bundle savedInstanceState) {


    data Receiver = new DataReceiver();
    intentFilter = new IntentFilter(REC_DATA);

    getActivity().registerReceiver(dataReceiver, intentFilter);
}

private class DataReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        int data= intent.getIntExtra("data", -1);

        // Do anything including interact with your UI
    }
}

在你的活动中:
// somewhere
Intent retIntent = new Intent(RE_DATA);
retIntent.putExtra("data", myData);
sendBroadcast(retIntent);

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