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

Posted

技术标签:

【中文标题】在Android中将数据从活动发送到片段【英文标题】:Send data from activity to fragment in Android 【发布时间】:2019-02-26 21:26:40 【问题描述】:

我有两节课。首先是活动,其次是我有一些EditText 的片段。在活动中,我有一个带有异步任务的子类,在 doInBackground 方法中,我得到了一些结果,我将其保存到变量中。如何将这个变量从子类“我的活动”发送到这个片段?

【问题讨论】:

【参考方案1】:

您从 Activity 发送数据的意图如下:

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

【讨论】:

嗯,我在调用 getArguments().getString(key) 时收到 NullPointerException NullPointerException "String strtext setArguments().getString("edttext");" 在读取片段中的包内容时,始终首先使用 getArguments 方法将包接收到 Bundle 对象中,并检查它是否为空。否则,getString 方法将应用在 null 上,因此当没有传递 bundle 时是 NPE。这样可以避免bundle未通过时出现空指针异常。 @Aznix。不建议为fragment创建构造函数。 @DileepaNipunSalinda :是的,但要确保类实现可序列化或可打包【参考方案2】:

您还可以从片段访问活动数据:

活动:

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;
    

【讨论】:

这个解决方案集成了你的activity和Fragment之间的紧密耦合,最好使用Bundle类来传递信息。或者,您可以使getMyData() 方法从接口继承和实现,并让Fragment 检查getActivity 是否在onAttach() 中返回instanceof 接口。 这个解决方案最适合我。此外,如果 myString 是公开的,您还没有声明 getMyData() 方法 当前接受的答案返回空指针异常。这应该是公认的答案 这应该是公认的答案。紧耦合很容易避免。这通常是一个坏主意,并且会使片段无用,如果您只想将片段与活动相关联,则还不如没有片段。该片段不能在其他活动中重复使用。 这可以满足我的需要,我需要传递一个自定义对象列表,但我不相信我可以使用 Bundle 答案来做到这一点。【参考方案3】:

我在这里@***.com 找到了很多答案,但这绝对是正确答案:

“将数据从 Activity 发送到 android 中的片段”。

活动:

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

片段:

读取片段中的值

        @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 的最佳方法(如果某些人快速键入,他将每秒发送两次 args)?跨度> Object insted of string 或 int 怎么样? @Jorgesys 这个解决方案怎么样? developer.android.com/training/basics/fragments/… 谢谢 Yozhik,我知道他遗漏了一些随机“交易”变量。【参考方案4】:

这个答案可能为时已晚。但它对未来的读者很有用。

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

第 1 步:在 Main Activity 上创建界面。

   public interface SelectedBundle 
    void onBundleSelect(Bundle bundle);
   

第 2 步:在同一 Activity 上创建 SelectedBundle 引用

   SelectedBundle selectedBundle;

第 3 步:在同一 Activity 中创建方法

   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 上实现您需要的每个片段。你很棒。你已经完成了。哇...

【讨论】:

【参考方案5】:

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

例如: 如果您的片段名称是 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 这是一个 int 值。 @s *R.id.container 是我的 FrameLayout 的 id 到目前为止,我发现使用片段管理器在添加/替换片段上传递数据的最佳解释。像宝石一样工作!谢谢! 使用 R.id.nav_host_fragment 而不是 R.id.container【参考方案6】:

使用 Fragments (F) 的基本思想是在 android 应用程序中创建可重用的自维持 UI 组件。这些片段包含在活动中,并且有常见(最佳)方式来创建从 A -> F 和 F-A 的通信路径方式,必须通过 Activity 在 F-F 之间进行通信,因为只有片段才会解耦和自我维持。

所以从 A -> F 传递数据将与 ρяσѕρєя K 解释的相同。除了那个答案之外,在 Activity 中创建 Fragments 之后,我们还可以将数据传递给 Fragments 中调用方法的 Fragments .

例如:

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

【讨论】:

证明:developer.android.com/training/basics/fragments/…【参考方案7】:

我想为初学者补充一点,这里两个最受好评的答案之间的差异是由片段的不同使用给出的。

如果您在具有要传递数据的 java 类中使用片段,则可以应用第一个答案来传递数据:

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

但是,如果您使用例如 Android Studio 为选项卡式片段提供的默认代码,则此代码将不起作用。

即使您将默认 PlaceholderFragment 替换为 FragmentClasses,并且即使您将 FragmentPagerAdapter 更正为新情况,为 getItem() 添加一个开关并为 getPageTitle() 添加另一个开关,它也不起作用(如图所示here)

警告:上面提到的剪辑有代码错误,我稍后会在此处解释,但有助于了解如何从默认代码变为选项卡式片段的可编辑代码)!如果您考虑该剪辑中的 java 类和 xml 文件(代表初学者场景首次使用选项卡式片段),我的其余答案会更有意义。

此页面中最受好评的答案不起作用的主要原因是,在选项卡式片段的默认代码中,片段在另一个 java 类中使用:FragmentPagerAdapter!

因此,为了发送数据,您很想在 MotherActivity 中创建一个捆绑包,并使用答案 2 将其传递到 FragmentPagerAdapter。

只是那又错了。 (也许你可以这样做,但这只是一个复杂的问题,并不是真正需要的)。

我认为,正确/更简单的方法是使用答案 2 将数据直接传递给相关片段。 是的,Activity 和 Fragment 之间会有紧密的耦合,但是对于选项卡式 Fragment,这是意料之中的。我什至建议您在 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 上方创建私有字符串/捆绑包 - 您可以在其中填充要传递给 Fragment 的数据,然后传递它们通过 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 中进行(并将它们的结果作为附加到包内键的字符串/整数传递,如上所示),就像在选项卡式片段中一样,您的语法将变得更加复杂(例如,这变成了 getActivity(),getIntent 变成了 getActivity().getIntent),但您也可以随意选择。

我对初学者的建议是专注于小步骤。首先,让您的意图是打开一个非常简单的选项卡式活动,而不传递任何数据。它有效吗?它会打开您期望的标签吗?如果不是,为什么?

从那开始,通过应用this clip 中提供的解决方案,看看缺少什么。对于该特定剪辑,从不显示 mainactivity.xml。那肯定会让你感到困惑。但是,如果您注意,您会看到例如 xml 片段文件中的上下文 (tools:context) 是错误的。每个片段 XML 都需要指向正确的片段类(或使用分隔符 $ 的子类)。

您还将看到,在主活动 java 类中,您需要添加 tabLayout.setupWithViewPager(mViewPager) - 就在 TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs); 行之后没有这一行,您的视图实际上并没有链接到片段的 XML 文件,但它只显示主要活动的 xml 文件。

除了主活动 java 类中的行之外,在主活动 XML 文件中,您需要更改选项卡以适应您的情况(例如,添加或删除 TabItems)。如果您在主活动 XML 中没有选项卡,那么您可能在创建它时没有选择正确的活动类型(新活动 - 选项卡式活动)。

请注意,在最后 3 段中,我谈到了视频!所以当我说主要活动 XML 时,它是视频中的主要活动 XML,在您的情况下是 MotherActivity XML 文件。

【讨论】:

【参考方案8】:

如果您将片段(的具体子类)的引用传递给异步任务,则可以直接访问片段。

将片段引用传递给异步任务的一些方法:

如果您的异步任务是一个成熟的类 (class FooTask extends AsyncTask),则将您的片段传递给构造函数。 如果您的异步任务是内部类,只需在定义异步任务的范围内声明一个最终片段变量,或者作为外部类的字段。您将能够从内部类访问它。

【讨论】:

【参考方案9】:

有时您可以在活动中收到 Intent,您需要将信息传递给您的工作片段。 如果您需要启动片段,给出的答案是可以的,但如果它仍在工作,setArguments() 不是很有用。 如果传递的信息会导致与您的 UI 交互,则会出现另一个问题。在那种情况下,你不能调用像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);

【讨论】:

【参考方案10】:

来自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);

【讨论】:

【参考方案11】:

很老的帖子,我还敢补充一点对我有帮助的解释。

从技术上讲,您可以直接在活动片段中设置任何类型的成员。 那么为什么要捆绑呢? 原因很简单——Bundle 提供了统一的处理方式:-- 创建/打开片段 -- 重新配置(屏幕旋转) - 只需在 onSaveInstanceState() 中将初始/更新的包添加到 outState -- 后台垃圾回收后的应用恢复(与重新配置一样)。

您可以(如果您喜欢实验)在简单的情况下创建一个解决方法,但 Bundle-approach 只是看不出一个片段和一千个片段之间的差异 - 它保持简单明了。 这就是为什么 @Elenasys 的答案是最优雅和通用的解决方案。 这就是为什么 @Martin 的答案有陷阱

【讨论】:

【参考方案12】:

如果activity 需要在初始化后使fragment 执行操作,最简单的方法是让activity 调用fragment 实例上的方法。在fragment中,添加一个方法:

public class DemoFragment extends Fragment 
  public void doSomething(String param) 
      // do something in fragment
  

然后在activity 中,使用fragment 管理器访问fragment 并调用method

public class MainActivity extends FragmentActivity 
    @Override
    public void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        DemoFragment fragmentDemo = (DemoFragment) 
            getSupportFragmentManager().findFragmentById(R.id.fragmentDemo);
        fragmentDemo.doSomething("some param");
    

然后activity 可以通过调用此method 直接与fragment 通信。

【讨论】:

这真的很好,因为您不想在片段中输入onCreateView 方法。真的很有帮助【参考方案13】:

将数据从活动类发送到片段的更好方法是通过 setter 方法。喜欢

FragmentClass fragmentClass = new FragmentClass();
fragmentClass.setMyList(mylist);
fragmentClass.setMyString(myString);
fragmentClass.setMyMap(myMap);

并轻松地从类中获取这些数据。

【讨论】:

如何在片段之间传递数据,如***.com/questions/32953477/pass-data-to-fragment 这不是很方便,当你也传其他的,之前设置的Extra的 如果您需要将该数据设置到片段中的某个字段,并且它尚不可见(已初始化),您将获得 NPE。 developer.android.com/training/basics/fragments/… 我可能在 3 年前就停止使用 Android,这些天来可能已弃用。【参考方案14】:

使用以下接口在activity和fragment之间进行通信

public interface BundleListener 
    void update(Bundle bundle);
    Bundle getBundle();

或者按照这个通用监听器使用接口进行双向通信

 /**
 * Created by Qamar4P on 10/11/2017.
 */
public interface GenericConnector<T,E> 
    T getData();
    void updateData(E data);
    void connect(GenericConnector<T,E> connector);

片段展示方法

public static void show(AppCompatActivity activity) 
        CustomValueDialogFragment dialog = new CustomValueDialogFragment();
        dialog.connector = (GenericConnector) activity;
        dialog.show(activity.getSupportFragmentManager(),"CustomValueDialogFragment");
    

您也可以在onAttach(Context) 中将上下文转换为GenericConnector

在你的活动中

CustomValueDialogFragment.show(this);

在你的片段中

...
@Override
    public void onCreate(@Nullable Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        connector.connect(new GenericConnector() 
            @Override
            public Object getData() 
                return null;
            

            @Override
            public void updateData(Object data) 

            

            @Override
            public void connect(GenericConnector connector) 

            
        );
    
...
    public static void show(AppCompatActivity activity, GenericConnector connector) 
            CustomValueDialogFragment dialog = new CustomValueDialogFragment();
            dialog.connector = connector;
            dialog.show(activity.getSupportFragmentManager(),"CustomValueDialogFragment");
        

注意:千万不要像"".toString().toString().toString();那样使用它。

【讨论】:

【参考方案15】:

只是偶然发现了这个问题,而上面的大多数方法都可以。 我只想补充一点,您可以使用Event Bus Library,尤其是在尚未创建组件(Activity 或 Fragment)的情况下,它适用于各种规模的 android 项目和许多用例。我在 Playstore 上的几个项目中亲自使用过它。

【讨论】:

【参考方案16】:

您可以在片段中创建公共静态方法,您将在其中获取该片段的静态引用,然后将数据传递给该函数,并将该数据设置为同一方法中的参数,并通过片段的 oncreate 方法上的 getArgument 获取数据,并设置数据到局部变量。

【讨论】:

【参考方案17】:

我在使用最新的导航架构组件时遇到了类似的问题。通过将我的调用活动中的捆绑包传递给片段,尝试了上述所有代码。

遵循 Android 最新发展趋势的最佳解决方案是使用 View Model(Android Jetpack 的一部分)。

在父 Activity 中创建并初始化一个 ViewModel 类,请注意这个 ViewModel 必须在 Activity 和 Fragment 之间共享。

现在,在 Fragment 的 onViewCreated() 中,初始化相同的 ViewModel 并设置 Observers 来监听 ViewModel 字段。

如果您需要,这里有一个有用的、深入的教程。

https://medium.com/mindorks/how-to-communicate-between-fragments-and-activity-using-viewmodel-ca733233a51c

【讨论】:

【参考方案18】:

在片段和活动之间传递数据的最聪明的尝试和测试方法是创建一个变量,例如:

class StorageUtil 
  public static ArrayList<Employee> employees;

然后要将数据从片段传递到活动,我们在 onActivityCreated 方法中这样做:

//a field created in the sending fragment
ArrayList<Employee> employees;

@Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) 
        super.onActivityCreated(savedInstanceState);
         employees=new ArrayList();

       //java 7 and above syntax for arraylist else use employees=new ArrayList<Employee>() for java 6 and below

     //Adding first employee
        Employee employee=new Employee("1","Andrew","Sam","1984-04-10","Male","Ghanaian");
        employees.add(employee);

      //Adding second employee
       Employee employee=new Employee("1","Akuah","Morrison","1984-02-04","Female","Ghanaian");
         employees.add(employee);

        StorageUtil.employees=employees;
    

现在您可以从任何地方获取 StorageUtil.employees 的值。 祝你好运!

【讨论】:

【参考方案19】:

我的解决办法是在fragment里面写一个静态方法:

public TheFragment setData(TheData data) 
    TheFragment tf = new TheFragment();
    tf.data = data;
    return tf;

这样,我确信我需要的所有数据都在 Fragment 中,然后再进行任何其他可能需要使用它的操作。 在我看来,它看起来更干净。

【讨论】:

好吧,如果 TheData 不是线程安全的,将其设为静态不一定能保护您免受线程问题的影响。小心点。静态本质上不是线程安全的。【参考方案20】:

您可以在片段中创建一个 setter 方法。然后在 Activity 中,当您引用片段时,您调用 setter 方法并将来自您的 Activity 的数据传递给它

【讨论】:

【参考方案21】:

Kotlin 版本:

Activity:

val bundle = Bundle()
bundle.putBoolean("YourKey1", true)
bundle.putString("YourKey2", "YourString")

val fragment = YourFragment()
fragment.arguments = bundle
val fragmentTransaction = parentFragmentManager.beginTransaction()
fragmentTransaction.replace(R.id.your_container, fragment, fragment.toString())
fragmentTransaction.commit()

FragmentonCreate()中:

var value1 = arguments?.getBoolean("YourKey1", default true/false)
var value2 = arguments?.getString("YourKey2", "Default String")

【讨论】:

【参考方案22】:

在你的活动中声明静态变量

public static HashMap<String,ContactsModal> contactItems=new HashMap<String, ContactsModal>();

然后在你的片段中做如下操作

ActivityName.contactItems.put(Number,contactsModal);

【讨论】:

它会起作用,但不推荐。因为静态成员将保留在内存中,除非从最近的应用程序中强制关闭应用程序。因此,在这种情况下,您应该只在整个应用程序的许多活动中需要静态成员时才使用它。

以上是关于在Android中将数据从活动发送到片段的主要内容,如果未能解决你的问题,请参考以下文章

将数据从活动发送到片段android工作室[重复]

Java Android - 将数据从活动发送到片段

如何在android中将图像从一个活动发送到另一个活动?

ViewPager - 将数据从片段发送到活动时出现 Android 错误 setCurrentItem(int,boolean)

如何在android中将图像从一个活动发送到另一个活动

无法在Android中将变量值从一个活动发送到另一个活动