如何将一个变量从Activity传递到Fragment,并将其传回?

156

我正在开发一个Android应用程序,我想在活动和片段之间传递日期。 我的活动有一个按钮,可以打开片段: DatePickerFragment。

在我的活动中,我展示了一个日期,我想要用片段修改它。所以我想传递日期给日期选择器,并将其发送回活动。

我尝试了很多解决方案,但没有一个能够工作。最简单的方法是传递参数,但这在片段中无法实现。


我的问题和你的完全一样。我想知道为什么这些示例只将片段本身作为日期选择器的“用户”,而不是启动它们的活动。 - Claudio
9个回答

238

传递信息到一个片段,您在创建片段时设置setArguments,稍后可以在片段的onCreate或onCreateView方法中检索此参数。

在片段的newInstance函数中,添加要发送到它的参数:

/**
 * Create a new instance of DetailsFragment, initialized to
 * show the text at 'index'.
 */
public static DetailsFragment newInstance(int index) {
    DetailsFragment f = new DetailsFragment();
    // Supply index input as an argument.
    Bundle args = new Bundle();
    args.putInt("index", index);
    f.setArguments(args);
    return f;
}

然后在onCreateonCreateView方法的片段内,您可以按如下方式检索参数:

Bundle args = getArguments();
int index = args.getInt("index", 0);
如果你希望从你的片段与你的活动进行交流(发送或不发送数据),你需要使用接口。在通讯片段的文档教程中详细说明了如何做到这一点。由于所有片段都通过活动进行通信,在这个教程中,你可以看到如何将数据从实际片段发送到其活动容器,以便在活动中使用此数据或将其发送到你的活动包含的另一个片段。
文档教程: http://developer.android.com/training/basics/fragments/communicating.html

19
人们反复问这个问题的原因是文档中没有很好地解释清楚。 - Michael Alan Huff
1
我仍然不确定哪种方法是最好的,是你提供的还是由@Elenasys回答下面的那个。 - Yoann Hercouet
有一种新的方式可以使用架构组件(I/O '17)在片段之间共享数据:https://developer.android.com/topic/libraries/architecture/viewmodel.html#sharing_data_between_fragments - jpardogo
那些在 XML 布局中创建的片段怎么样? - ralphgabb
我不想再使用静态方法了。 - Ahamadullah Saikat
显示剩余2条评论

99

Activity发送数据到Fragment

Activity:

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

碎片:

读取碎片中的值。

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

但是如果你想从Fragment向Activity发送值,请阅读jpardogo的答案,你必须需要使用接口,更多信息:与其他片段进行通信


3
如何传递自定义对象?我使用了 Parcelable,但是导致了 class cast exception 异常。 - viper
1
这个解决方案对我不起作用,找不到从哪里导入事务。 - thebeancounter
如果您将Fragment加载到Activity中,可以通过在事务中定义的Bundle发送值。请说明您的情况是什么? - Jorgesys
我们如何将对象作为Bundle传递? - Sulman Rasheed

12

感谢@ρяσѕρєя K和Terry...他们帮了我很多,并且完美地发挥作用

从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) {
    // get arguments
    String strtext = getArguments().getString("edttext");    
    return inflater.inflate(R.layout.fragment, container, false);
}

参考: 如何在Android中从Activity向Fragment发送数据


10

对于所有的 Kotlin 开发者:

这里是 Android Studio 建议的解决方案将数据发送到您的 Fragment(= 当您使用文件 -> 新建 -> Fragment -> Fragment(Blank),并勾选“包含片段工厂方法”时创建一个空白片段)。

将以下代码放入您的 Fragment 中:

class MyFragment: Fragment {

...

    companion object {

            @JvmStatic
            fun newInstance(isMyBoolean: Boolean) = MyFragment().apply {
                arguments = Bundle().apply {
                    putBoolean("REPLACE WITH A STRING CONSTANT", isMyBoolean)
                }
            }
     }
}
.apply方法是在创建对象时设置数据的一种不错的技巧,或者如他们在此处所述的那样

使用当前对象作为接收器调用指定的函数[block],并返回当前对象。

然后在您的Activity或Fragment中执行:

val fragment = MyFragment.newInstance(false)
... // transaction stuff happening here

并且在您的片段中读取参数,例如:

private var isMyBoolean = false

override fun onAttach(context: Context?) {
    super.onAttach(context)
    arguments?.getBoolean("REPLACE WITH A STRING CONSTANT")?.let {
        isMyBoolean = it
    }
}

要将数据“发送”回您的 Activity,请在 Activity 中定义一个函数,并在 Fragment 中执行以下操作:

(activity as? YourActivityClass)?.callYourFunctionLikeThis(date) // your function will not be called if your Activity is null or is a different Class

享受 Kotlin 带来的魔力吧!


5
使用库EventBus来传递可能包含变量的事件。这是一个很好的解决方案,因为它可以保持你的活动和片段之间松散耦合。 https://github.com/greenrobot/EventBus

4
我不能说这不是一个好的解决方案,但需要谨慎。虽然EventBus非常易于使用,但它也存在危险。如果您开始添加太多信号,松散耦合的特性可能会使跟踪谁在调用谁以及不同事件发生的位置变得非常困难。 - Johan Paul

4

将数据从 Activity 发送到与 XML 相关联的 Fragment

如果您使用 Android Studio 中的模板之一创建 Fragment,例如文件> 新建> Fragment> Fragment(列表),则该 Fragment 通过 XML 进行链接。 newInstance 方法在 Fragment 中创建但从未调用过,因此不能用于传递参数。

相反,在 Activity 中重写 onAttachFragment 方法。

@Override
public void onAttachFragment(Fragment fragment) {
    if (fragment instanceof DetailsFragment) {
        Bundle args = new Bundle();
        args.putInt("index", index);
        f.setArguments(args);        
     }
}

然后,根据其他答案,在片段的onCreate方法中阅读参数。

3

现在,回答2021年的问题,Fragment内置了一些静态方法,用于在创建Fragment时传递参数。未来的开发者们可以尝试使用它们。

当你创建一个Fragment时,它已经定义了以下静态方法:

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

在片段中的onCreate方法通过已定义的代码接收参数。

   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       if (getArguments() != null) {
           mParam1 = getArguments().getString(ARG_PARAM1);
           mParam2 = getArguments().getString(ARG_PARAM2);
         //  rr=getArguments().getInt("ky");
           sn1=mParam1;
           sn2=mParam2;
       }
   }

在活动中使用以下代码,其中mostpop是片段类的名称。如果您想要其他数据类型,请修改这些内置行或使用bundle。
Fragment mp=mostpop.newInstance("parameter1","parameter2");

这很好,但你能详细说明一下吗?至少你指的是哪些方法,最好附带简单的代码示例。 - WerWet

0

你可以通过一个 bundle 实例化你的 fragment:

Fragment fragment = Fragment.instantiate(this, RolesTeamsListFragment.class.getName(), bundle);

-2

在类中声明公共变量是最简单的方法:

目标类上:

public class MyFragment extends Fragment {
    public MyCallerFragment caller; // Declare the caller var
...
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
           Bundle savedInstanceState) {
        // Do what you want with the vars
        caller.str = "I changed your value!";
        caller.i = 9999;
        ...
        return inflater.inflate(R.layout.my_fragment, container, false);
    }
...
}

在调用者类中:

public class MyCallerFragment extends Fragment {
    public Integer i; // Declared public var
    public String str; // Declared public var
        ...
            FragmentManager fragmentManager = getParentFragmentManager();
            FragmentTransaction transaction = fragmentManager.beginTransaction();

            myFragment = new MyFragment();
            myFragment.caller = this;
            transaction.replace(R.id.nav_host_fragment, myFragment)
                    .addToBackStack(null).commit();
        ...
}

如果您想要使用主活动,这也很容易:
在主活动类上:
public class MainActivity extends AppCompatActivity {
    public String str; // Declare public var
    public EditText myEditText; // You can declare public elements too.
                                // Taking care that you have it assigned
                                // correctly.
...
}

在被调用的类中:

public class MyFragment extends Fragment {
    private MainActivity main; // Declare the activity var
...
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
           Bundle savedInstanceState) {
        // Assign the main activity var
        main = (MainActivity) getActivity();

        // Do what you want with the vars
        main.str = "I changed your value!";
        main.myEditText.setText("Wow I can modify the EditText too!");
        ...
        return inflater.inflate(R.layout.my_fragment, container, false);
    }
...
}

注意:在使用事件(onClick、onChanged等)时要小心,因为你可能会陷入“争夺”变量的情况。结果将是变量有时不会改变或神奇地返回到上一个值。
更多组合请发挥创意。 :)

1
这将活动和片段紧密耦合在一起,从而限制了片段的可重用性。我们应该找到一种耦合度更低的通信方式。 - Zorayr

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