如何在片段之间传递数据


Answers:


22

您为什么不使用捆绑软件。从您的第一个片段开始,以下是设置步骤:

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

然后在第二个片段中,使用以下方法检索数据:

Bundle bundle = this.getArguments();
int myInt = bundle.getInt(key, defaultValue);

Bundle为许多数据类型放置了方法。请参阅http://developer.android.com/reference/android/os/Bundle.html


21

如果您使用Roboguice,则可以使用Roboguice中的EventManager传递数据,而无需使用Activity作为界面。这是非常干净的IMO。

如果您不使用Roboguice,也可以将Otto用作事件总线:http ://square.github.com/otto/

更新20150909:您现在也可以使用Green Robot Event Bus甚至RxJava。取决于您的用例。


3
我全心全意地拒绝唐恩在这里所说的。使用总线解开所有这些内容,并停止手动编写侦听器和回调。奥托FTW。
查理·柯林斯

如果您不想使用RoboGuice,则可以使用Square的Otto。我现在经常使用Otto及其令人敬畏的功能。github.com/square/otto
Donn Felker,

9
如果您喜欢Otto,还可能会喜欢EventBus,它还提供线程处理。github.com/greenrobot/EventBus
Markus Junginger,

4
如果两个片段同时可见,我可以看到事件总线策略有效,但是如果片段A(假设它是一个ListFragment)占据了整个屏幕,并且选择一个List项目启动了Fragment B,那么它将如何工作?整个屏幕。AFAIK,事件总线模式在onResume / onPause中都有每个片段的注册/注销,所以我不确定如何应用它。
Matt

@Matthew如果是这种情况,则需要在活动之间捆绑分发它们,然后可以在运行时将数据注入它们。
Donn Felker

17

Fragment 文档中

通常,您会希望一个Fragment与另一个Fragment进行通信,例如,根据用户事件更改内容。所有片段到片段的通信都是通过关联的活动完成的。两个片段永远不要直接通信。

因此,我建议您查看文档中的基本片段训练文档。它们非常详尽,并提供了示例和演练指南。


12

因此,假设您有活动AB控制片段A和片段B。在片段A内,您需要活动AB可以实现的接口。在示例android代码中,它们具有:

private Callbacks mCallbacks = sDummyCallbacks;

/ *一个回调接口,所有包含此片段的活动都必须实现。此机制允许将项目选择通知活动。* /

public interface Callbacks {
/*Callback for when an item has been selected. */    
      public void onItemSelected(String id);
}

/*A dummy implementation of the {@link Callbacks} interface that does nothing. Used only when this fragment is not attached to an activity. */    
private static Callbacks sDummyCallbacks = new Callbacks() {
    @Override
    public void onItemSelected(String id) {
    }
};

回调接口放置在您的一个片段中(例如,片段A)。我认为此Callbacks接口的目的就像Frag A中的嵌套类,任何Activity都可以实现。因此,如果片段A是电视,则回调是电视遥控器(接口),允许活动AB使用片段A。我可能在细节上是错的,因为我是菜鸟,但是我确实让我的程序在所有屏幕尺寸上都能正常工作,这就是我使用的方法。

因此,在Fragment A中,我们有:(我从Android的Sample程序中获取了此信息)

@Override
public void onListItemClick(ListView listView, View view, int position, long id) {
super.onListItemClick(listView, view, position, id);
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
mCallbacks.onItemSelected(DummyContent.ITEMS.get(position).id);
//mCallbacks.onItemSelected( PUT YOUR SHIT HERE. int, String, etc.);
//mCallbacks.onItemSelected (Object);
}

在Activity AB中,我们重写了onItemSelected方法:

public class AB extends FragmentActivity implements ItemListFragment.Callbacks {
//...
@Override
//public void onItemSelected (CATCH YOUR SHIT HERE) {
//public void onItemSelected (Object obj) {
    public void onItemSelected(String id) {
    //Pass Data to Fragment B. For example:
    Bundle arguments = new Bundle();
    arguments.putString(“FragmentB_package”, id);
    FragmentB fragment = new FragmentB();
    fragment.setArguments(arguments);
    getSupportFragmentManager().beginTransaction().replace(R.id.item_detail_container, fragment).commit();
    }

因此,在Activity AB中,您基本上将所有内容都放入了Bundle并将其传递给B。如果您不确定如何使用Bundle,请查找类。

我基本上是按照Android提供的示例代码进行操作。一个带有DummyContent的东西。制作新的Android应用程序包时,它就是名为MasterDetailFlow的程序包。


8

1-第一种方法是定义一个接口

public interface OnMessage{
    void sendMessage(int fragmentId, String message);
}

public interface OnReceive{
    void onReceive(String message);
}

2-在您的活动中实现OnMessage界面

public class MyActivity implements OnMessage {
   ...
   @Override
   public void sendMessage(int fragmentId, String message){
       Fragment fragment = getSupportFragmentManager().findFragmentById(fragmentId);
       ((OnReceive) fragment).sendMessage();
   }
}

3-在您的片段中实现OnReceive接口

public class MyFragment implements OnReceive{
    ...
    @Override
    public void onReceive(String message){
        myTextView.setText("Received message:" + message);
    }
}

这是处理片段之间传递消息的样板版本。

处理片段之间的数据通道的另一种方法是使用事件总线。

1-注册/注销到事件总线

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);
    super.onStop();
}

2-定义一个事件类

public class Message{
    public final String message;

    public Message(String message){
        this.message = message;
    }
}

3-在您的应用程序中的任何位置发布此事件

EventBus.getDefault().post(new Message("hello world"));

4-订阅该事件以在您的片段中接收它

@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(Message event){
    mytextview.setText(event.message);
}

有关更多详细信息,用例和有关事件总线模式的示例项目


不要对多个问题发表相同的答案。发表一个好答案,然后投票/标记以将其他问题重复作为一个重复。如果问题不是重复的,请定制对问题的答案
马丁·彼得斯

3

在我的情况下,我不得不从FragmentB-> FragmentA向后发送数据,因此Intents不是一个选项,因为该片段已经被初始化。尽管上述所有答案听起来都不错,但要实现它需要很多样板代码,所以我有很多去简单的方法使用的LocalBroadcastManager,它究竟上面说的,但没有alll讨厌的样板代码。下面共享一个示例。

在发送片段(片段B)中

public class FragmentB {

    private void sendMessage() {
      Intent intent = new Intent("custom-event-name");
      intent.putExtra("message", "your message");
      LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
    }
 }

并在要接收的消息片段中(片段A)

  public class FragmentA {
    @Override
    public void onCreate(Bundle savedInstanceState) {

      ...

      // Register receiver
      LocalBroadcastManager.getInstance(this).registerReceiver(receiver,
          new IntentFilter("custom-event-name"));
    }

//    This will be called whenever an Intent with an action named "custom-event-name" is broadcasted.
    private BroadcastReceiver receiver = new BroadcastReceiver() {
      @Override
      public void onReceive(Context context, Intent intent) {
        String message = intent.getStringExtra("message");
      }
    };
}

希望对别人有帮助


所以我用了这段代码,似乎onReceive被调用两次,两次都不知道为什么。
SolidSnake

永远不要在片段中注册registerReciver,因为某些原因它会被注册两次
SolidSnake

2

这取决于片段的结构。如果您可以在Fragment Class B上拥有一些静态方法,而在目标TextView对象上拥有静态方法,则可以在Fragment Class A上直接调用该方法。这比侦听器更好,因为该方法是即时执行的,我们不这样做无需执行其他任务即可在整个活动中执行监听。请参见下面的示例:

Fragment_class_B.setmyText(String yourstring);

在片段B上,您可以将方法定义为:

public static void setmyText(final String string) {
myTextView.setText(string);
}

只是不要忘记在片段B上将myTextView设置为静态,并在片段A上正确导入Fragment B类。

最近在我的项目上执行了该程序,它就起作用了。希望能有所帮助。


2
这会起作用(好吧,直到碎片被销毁并重新创建,再考虑设备旋转),但是您正在紧密耦合碎片;从属片段(我们称其为“ X”)在代码中对另一个片段(“ Y”)具有硬连接依赖性。进行父活动非常整洁,因为您可以将其留给活动来决定从何处获取数据。例如,在电话上,您的活动可能会使用X和Y片段,但在平板电脑上,您可能会决定使用X和Z。最好让该活动(知道存在哪些片段)充当中间人并连接到Y或Z适当。
Phil Haigh 2013年

如何从该方法调用Asyntask
UnderGround 2013年

1

您可以阅读此文档。此概念已在http://developer.android.com/training/basics/fragments/communicating.html中很好地解释了


如果您仅引用其他URL,请在问题上使用注释,而不是发布答案
Jens Wirth 2014年

尽管此链接可以回答问题,但最好在此处包括答案的基本部分,并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会无效。
2014年

这不能为问题提供答案。要批评或要求作者澄清,请在其帖子下方留下评论-您可以随时对自己的帖子发表评论,一旦拥有足够的声誉,您就可以在任何帖子中发表评论
bartektartanus

@梅兹我同意你的话。下次再试。
Nikhil

@jewirth我不知道你为什么对我有偏见,因为以上提供的所有上述答案都有指向其他页面的链接。我没有那么多的回购信息,以便我可以评论其他帖子。否则我只会那样做。
Nikhil

1

我正在一个类似的项目上,我想我的代码可能会在上述情况下有所帮助

这是我在做什么的概述

我的项目有两个片段,分别称为“ FragmentA ”和“ FragmentB

- FragmentA包含一个列表视图,当您在单击某个项目FragmentA它的索引被传递到FragmentB使用Communicator界面

  • 设计模式完全基于Java接口的概念,即“接口引用变量可以引用子类对象”
  • MainActivity实现fragmentA提供的接口(否则我们不能使接口引用变量指向MainActivity)
  • 在下面的代码通信对象由指 MainActivity的通过使用“对象setCommunicator(Communicatot C) ”存在于方法fragmentA
  • 我正在使用MainActivity的引用从FrgamentA触发 接口的response ()方法。

    接口通信器在fragmentA中定义,这是为了最大程度地减少对通信器接口的访问权限。

以下是我完整的工作代码

FragmentA.java

public class FragmentA extends Fragment implements OnItemClickListener {

ListView list;
Communicator communicater;


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    return inflater.inflate(R.layout.fragmenta, container,false);
}

public void setCommunicator(Communicator c){
    communicater=c;
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    super.onActivityCreated(savedInstanceState);
    communicater=(Communicator) getActivity();
    list = (ListView) getActivity().findViewById(R.id.lvModularListView);
    ArrayAdapter<?> adapter = ArrayAdapter.createFromResource(getActivity(),
            R.array.items, android.R.layout.simple_list_item_1);
    list.setAdapter(adapter);
    list.setOnItemClickListener(this);

}

@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int index, long arg3) {
communicater.respond(index);

}

public interface Communicator{
    public void respond(int index);
}

}

fragmentB.java

public class FragmentA extends Fragment implements OnItemClickListener {

ListView list;
Communicator communicater;


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    return inflater.inflate(R.layout.fragmenta, container,false);
}

public void setCommunicator(Communicator c){
    communicater=c;
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    super.onActivityCreated(savedInstanceState);
    communicater=(Communicator) getActivity();
    list = (ListView) getActivity().findViewById(R.id.lvModularListView);
    ArrayAdapter<?> adapter = ArrayAdapter.createFromResource(getActivity(),
            R.array.items, android.R.layout.simple_list_item_1);
    list.setAdapter(adapter);
    list.setOnItemClickListener(this);

}

@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int index, long arg3) {
communicater.respond(index);

}

public interface Communicator{
    public void respond(int index);
}

}

MainActivity.java

public class MainActivity extends Activity implements FragmentA.Communicator {
FragmentManager manager=getFragmentManager();
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    FragmentA fragA=(FragmentA) manager.findFragmentById(R.id.fragmenta);
    fragA.setCommunicator(this);


}

@Override
public void respond(int i) {
    // TODO Auto-generated method stub

FragmentB FragB=(FragmentB) manager.findFragmentById(R.id.fragmentb);
FragB.changetext(i);
}



}

发布的FragmentB代码与FragmentA相同。请纠正。谢谢。
Marka 2014年

1

基本上实现接口以在Activity和fragment之间进行通信。

1)主要活动

public class MainActivity extends Activity implements SendFragment.StartCommunication
{

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

@Override
public void setComm(String msg) {
// TODO Auto-generated method stub
DisplayFragment mDisplayFragment = (DisplayFragment)getFragmentManager().findFragmentById(R.id.fragment2);
if(mDisplayFragment != null && mDisplayFragment.isInLayout())
{
mDisplayFragment.setText(msg);
}
else
{
Toast.makeText(this, "Error Sending Message", Toast.LENGTH_SHORT).show();
}
}
}

2)发送方片段(片段到活动)

public class SendFragment extends Fragment
{
StartCommunication mStartCommunicationListner;
String msg = "hi";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View mView = (View) inflater.inflate(R.layout.send_fragment, container);
final EditText mEditText = (EditText)mView.findViewById(R.id.editText1);
Button mButton = (Button) mView.findViewById(R.id.button1);
mButton.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
msg = mEditText.getText().toString();
sendMessage();
}
});
return mView;
}

interface StartCommunication
{
public void setComm(String msg);
}

@Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
if(activity instanceof StartCommunication)
{
mStartCommunicationListner = (StartCommunication)activity;
}
else
throw new ClassCastException();

}

public void sendMessage()
{
mStartCommunicationListner.setComm(msg);
}

}

3)接收方片段(活动到片段)

    public class DisplayFragment extends Fragment
{
View mView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
mView = (View) inflater.inflate(R.layout.display_frgmt_layout, container);
return mView;
}

void setText(String msg)
{
TextView mTextView = (TextView) mView.findViewById(R.id.textView1);
mTextView.setText(msg);
}

}

我将此链接用于相同的解决方案,希望有人会发现它有用。非常简单和基本的示例。

http://infobloggall.com/2014/06/22/communication-between-activity-and-fragments/


尽管此链接可以回答问题,但最好在此处包括答案的基本部分,并提供链接以供参考。如果链接的页面发生更改,仅链接的答案可能会失效
abarisone 2015年

0

片段类

public class CountryListFragment extends ListFragment{

    /** List of countries to be displayed in the ListFragment */

    ListFragmentItemClickListener ifaceItemClickListener;   

    /** An interface for defining the callback method */
    public interface ListFragmentItemClickListener {
    /** This method will be invoked when an item in the ListFragment is clicked */
    void onListFragmentItemClick(int position);
}   

/** A callback function, executed when this fragment is attached to an activity */  
@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);

    try{
        /** This statement ensures that the hosting activity implements ListFragmentItemClickListener */
        ifaceItemClickListener = (ListFragmentItemClickListener) activity;          
    }catch(Exception e){
        Toast.makeText(activity.getBaseContext(), "Exception",Toast.LENGTH_SHORT).show();
    }
}

B类片段

public class CountryDetailsFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    /** Inflating the layout country_details_fragment_layout to the view object v */
    View v = inflater.inflate(R.layout.country_details_fragment_layout, null);

    /** Getting the textview object of the layout to set the details */ 
    TextView tv = (TextView) v.findViewById(R.id.country_details);      

    /** Getting the bundle object passed from MainActivity ( in Landscape   mode )  or from 
     *  CountryDetailsActivity ( in Portrait Mode )  
     * */
    Bundle b = getArguments();

    /** Getting the clicked item's position and setting corresponding  details in the textview of the detailed fragment */
    tv.setText("Details of " + Country.name[b.getInt("position")]);     

    return v;
    }

}

用于在片段之间传递数据的Main Activity类

public class MainActivity extends Activity implements ListFragmentItemClickListener {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
}


/** This method will be executed when the user clicks on an item in the listview */
@Override
public void onListFragmentItemClick(int position) {

    /** Getting the orientation ( Landscape or Portrait ) of the screen */
    int orientation = getResources().getConfiguration().orientation;


    /** Landscape Mode */
    if(orientation == Configuration.ORIENTATION_LANDSCAPE ){
        /** Getting the fragment manager for fragment related operations */
        FragmentManager fragmentManager = getFragmentManager();

        /** Getting the fragmenttransaction object, which can be used to add, remove or replace a fragment */
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

        /** Getting the existing detailed fragment object, if it already exists. 
         *  The fragment object is retrieved by its tag name  *
         */
Fragment prevFrag = fragmentManager.findFragmentByTag("in.wptrafficanalyzer.country.details");

        /** Remove the existing detailed fragment object if it exists */
        if(prevFrag!=null)
    fragmentTransaction.remove(prevFrag);           

        /** Instantiating the fragment CountryDetailsFragment */
  CountryDetailsFragment fragment = new CountryDetailsFragment();

        /** Creating a bundle object to pass the data(the clicked item's   position) from the activity to the fragment */ 
        Bundle b = new Bundle();

        /** Setting the data to the bundle object */
        b.putInt("position", position);

        /** Setting the bundle object to the fragment */
        fragment.setArguments(b);           

        /** Adding the fragment to the fragment transaction */
        fragmentTransaction.add(R.id.detail_fragment_container,   fragment,"in.wptrafficanalyzer.country.details");

        /** Adding this transaction to backstack */
        fragmentTransaction.addToBackStack(null);

        /** Making this transaction in effect */
        fragmentTransaction.commit();

    }else{          /** Portrait Mode or Square mode */
        /** Creating an intent object to start the CountryDetailsActivity */
        Intent intent = new Intent("in.wptrafficanalyzer.CountryDetailsActivity");

        /** Setting data ( the clicked item's position ) to this intent */
        intent.putExtra("position", position);

        /** Starting the activity by passing the implicit intent */
        startActivity(intent);          
      }
    }
 }

Detailde活动类

public class CountryDetailsActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    /** Setting the layout for this activity */
    setContentView(R.layout.country_details_activity_layout);

    /** Getting the fragment manager for fragment related operations */
    FragmentManager fragmentManager = getFragmentManager();

    /** Getting the fragmenttransaction object, which can be used to add, remove or replace a fragment */
    FragmentTransaction fragmentTransacton = fragmentManager.beginTransaction();

    /** Instantiating the fragment CountryDetailsFragment */
    CountryDetailsFragment detailsFragment = new CountryDetailsFragment();

    /** Creating a bundle object to pass the data(the clicked item's position) from the activity to the fragment */
    Bundle b = new Bundle();

    /** Setting the data to the bundle object from the Intent*/
    b.putInt("position", getIntent().getIntExtra("position", 0));

    /** Setting the bundle object to the fragment */
    detailsFragment.setArguments(b);

    /** Adding the fragment to the fragment transaction */
    fragmentTransacton.add(R.id.country_details_fragment_container, detailsFragment);       

    /** Making this transaction in effect */
    fragmentTransacton.commit();

    }
}

争辩数组

public class Country {

/** Array of countries used to display in CountryListFragment */
static String name[] = new String[] {
        "India",
        "Pakistan",
        "Sri Lanka",
        "China",
        "Bangladesh",
        "Nepal",
        "Afghanistan",
        "North Korea",
        "South Korea",
        "Japan",
        "Bhutan"
};
}

有关更多详细信息,请访问此链接[ http://wptrafficanalyzer.in/blog/itemclick-handler-for-listfragment-in-android/]。有完整的例子..


0

getParentFragmentManager().setFragmentResultListener是2020年的方法。您唯一的限制是使用捆绑包传递数据。查看文档以获取更多信息和示例。

其他方式

  • 调用getActivity()并将其转换为片段之间的共享活动,然后将其用作传递数据的桥梁。强烈建议不要使用此解决方案,因为它需要在活动和片段之间进行繁琐的修饰,但是在KitKat时代,它曾经是最受欢迎的解决方案...
  • 使用回调。任何事件机制都可以。这将是Java原始解决方案。好处FragmentManager是它不仅限于捆绑软件。但是,不利的一面是,您可能会遇到一些极端情况的错误,在这些错误中,您会弄乱活动的生命周期并获得异常,例如IllegalStateException片段管理器处于保存状态或活动被破坏。另外,它不支持跨处理通信。

-2

基本上,我们在这里处理片段之间的通信。片段之间的通信永远不可能直接实现。它涉及创建两个片段的上下文中的活动。

您需要在发送片段中创建一个接口,并在活动中实现该接口,该活动将重新挂起消息并传输到接收片段。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.