在片段及其容器活动之间传递数据


176

如何在片段及其容器活动之间传递数据?是否有类似于通过意图在活动之间传递数据的东西?

我读了这篇文章,但并没有太大帮助:http :
//developer.android.com/guide/topics/fundamentals/fragments.html#CommunicatingWithActivity


那还不够吗?您可以发送任何您想要的东西,也许您可​​以解释更多,您想要完成什么?
Marcin Milejski '02

从文档中,我不了解如何将活动中的值或字符串传递给片段,反之亦然。谢谢
tyb 2012年

2
处理来自片段的异步调用并返回它们的数据的最佳方法是在两侧都实现BroadcastReceivers。无论如何,如果要处理非特定数量的片段,都应该使其异步。
Marek Sebera

@punisher_malade不,对我有用
Vadim Kotov

Answers:


210

您可以在片段中致电getActivity()

这将使您能够访问创建该片段的活动。显然,您可以从那里调用活动中的任何访问器方法。

例如,getResult()针对您的Activity 调用的方法:

((MyActivity) getActivity()).getResult();

85
由于您正在访问您的活动(而不是父Android活动)中的函数,因此您需要转换getActivity()调用:(((MyActivity)getActivity())。getResult();
尼克

3
从片段到活动的后退方法将如何?
Vasil Valchev

6
@VasilValchev,您可以创建一个接口并强制活动实现该接口,然后从您的片段中调用方法以传递数据。使用onAttach方法检查活动是否实现了接口。
伊凡·尼科洛夫

3
@IvanNikolov的出色回答。您可以在Fragments Training Link
bogdan

8
这既不是一种好习惯,也不是一种模式。接口的答案是正确的。
moictab '18年

303

尝试使用接口。

任何应将数据传递回其包含活动的片段都应声明一个接口来处理和传递数据。然后,确保您的包含活动实现了这些接口。例如:

爪哇

在您的片段中,声明接口...

public interface OnDataPass {
    public void onDataPass(String data);
}

然后,通过onAttach方法将包含类的接口实现连接到片段,如下所示:

OnDataPass dataPasser;

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    dataPasser = (OnDataPass) context;
}

在片段中,当您需要处理数据传递时,只需在dataPasser对象上调用它即可:

public void passData(String data) {
    dataPasser.onDataPass(data);
}

最后,在您实施 的包含活动OnDataPass ...

@Override
public void onDataPass(String data) {
    Log.d("LOG","hello " + data);
}

科特琳

步骤1.创建界面

interface OnDataPass {
    fun onDataPass(data: String)
}

步骤2.然后,将接口的包含类的实现连接到onAttach方法(YourFragment)中的片段,如下所示:

lateinit var dataPasser: OnDataPass

override fun onAttach(context: Context) {
    super.onAttach(context)
    dataPasser = context as OnDataPass
}

步骤3.在片段中,当您需要处理数据传递时,只需在dataPasser对象上调用它即可:

fun passData(data: String){
    dataPasser.onDataPass(data)
}

步骤4.最后,在您的活动中实现OnDataPass

class MyActivity : AppCompatActivity(), OnDataPass {}

override fun onDataPass(data: String) {
    Log.d("LOG","hello " + data)
}

1
到目前为止,谢谢您的好回答。这在这里也很好解释。我没有意识到您可以实现多个接口,我已经实现了一个接口,ActionBar.TabListener并且不得不添加一个额外的接口。
尤金·范德默

5
这绝对是必经之路,我认为这是唯一的必经之路。并提供了Activity和Fragment之间的两种交互方式。当您在一个活动中有多个片段时,也可以通过选项卡或多片段布局在片段之间进行通信。
Christopher

1
我想知道的是...这是官方的答案,并且在官方开发人员网站上,他们说这是传播frag-act-frag的正确方法,但是为什么甚至可以通过强制转换getActivity( )?
2014年

3
为什么有人需要一个额外的接口来做到这一点?为什么您认为以下解决方案不好或您的解决方案更好?(((MyActivity)getActivity).myMethod(...)
spacifici 2014年

3
不推荐使用onAttach(活动活动),请改用onAttach(Context上下文)。
克里斯·C

23

最简单的方法,但不推荐

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

活动:

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

5
这会增加代码的耦合性,是一种不好的做法。现在,您不能在MyActivity
AmeyaB

为什么这种方式被认为是不好的做法,而接口只是这个问题的解决方案?
androidXP

AmeyaB,如果所有活动都从BaseActivity扩展,或者我们将其像BaseActivity一样投下activity =(BaseActivity)getActivity(); 然后它将适用于所有活动
Zar E Ahmer '17

21
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    Bundle b = getActivity().getIntent().getExtras();
            wid = b.getString("wid");
            rid = b.getString("rid");
            View view = inflater.inflate(R.layout.categoryfragment, container, false);
    return view;
 }

14
这是在Fragment中获取它的方法,但是如果您在XML布局文件中包含了片段,则如何在调用该片段的类中进行设置。
Zapnologica

17

在片段及其容器活动之间传递数据

活动:

        Bundle bundle = new Bundle();
        bundle.putString("message", "Alo Elena!");
        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");
        ...
        ...
        ...
        }

你好@Elenasys 如何将捆绑包数据从活动发送回片段。我从片段转到活动,我需要回到已创建的片段(仅调用onStart,onResume等),并且需要我能够在活动中获得的数据。有什么方法可以做的还是我做错了什么?
Michal Moravik,

7

我不知道这是否是最好的方法,我一直在google上搜索了很长时间,发现如何将Bundle从片段传递到其容器活动,但是我发现的全部是从活动向片段发送数据(因为我是新手,这让我有些困惑)。

后来我尝试了自己想要的东西,完全适合我的需求。所以我将其张贴在这里,以防像我这样的人在寻找相同的东西。

//从Fragment传递数据。

Bundle gameData = new Bundle();
        gameData.putStringArrayList(Constant.KEY_PLAYERS_ARR,players);
        gameData.putString(Constant.KEY_TEAM_NAME,custom_team_name);
        gameData.putInt(Constant.KEY_REQUESTED_OVER,requestedOver);

        Intent intent = getActivity().getIntent();
        intent.putExtras(gameData);

//从包的容器活动中获取数据。

Bundle gameData = getIntent().getExtras();
        if (gameData != null)
        {
            int over = gameData.getInt(Constant.KEY_REQUESTED_OVER);
            ArrayList<String> players = gameData.getStringArrayList(Constant.KEY_PLAYERS_ARR);
            String team = gameData.getString(Constant.KEY_TEAM_NAME);

        }
        else if (gameData == null)
        {
            Toast.makeText(this, "Bundle is null", Toast.LENGTH_SHORT).show();
        }

6

接口是最好的解决方案之一:

胶水界面:

public interface DataProviderFromActivity {

    public String getName();
    public String getId);

}  

我的活动:

public class MyActivity implements DataProviderFromActivity{

    String name = "Makarov";
    String id = "sys533";

    ... ... ... ... ... .... .... 
    ... ... ... ... ... .... .... 

    public String getName(){
        return name;
    };
    public String getId(){
        return id;
    };
}

MyFragment:

public class MyFragment extends Fragment{

    String fragName = "";
    String fragId = "";

    ... ... ... ... ... .... .... 
    ... ... ... ... ... .... .... 

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        DataProviderFromActivity myActivity= (DataProviderFromActivity) getActivity();
        fragName = myActivity.getName();
        fragId = myActivity.getId();

        ... ... ... ... ... .... .... 
        ... ... ... ... ... .... .... 

        updateFragmentView();
    }
}

嗨@HassanMakarov我喜欢您的界面,简洁明了。我确实有一个问题。我不知道它是否特定于我的viewpager,但一次最多只能得到2个片段中的数据?创建活动时,字符串“ Makarov”和“ sys533”出现在片段1和2中,但是在选择片段2或3之前,字符串不出现在片段3中吗?有任何想法吗?
JC23 '17

@ JC23我猜问题出在片段交易上。启动MainActivity时会设置哪个片段?
哈桑·塔雷克

@ JC23我的工作:我使用工具栏和NavigationView代替Tab / ViewPager。在onNavigationItemSelected()中,我执行片段事务以相应地设置片段。
哈桑·塔雷克

2

我使用了实现日期侦听器的AppCompatActivity。片段是必需的,因为我需要编写日期范围选择器的代码。我还需要容器接收选定的日期,以将其返回到父活动。

对于容器活动,这是类声明:

public class AppCompatDateRange extends AppCompatActivity implements
    DateIniRangeFragment.OnDateIniSelectedListener, DateFimRangeFragment.OnDateFimSelectedListener

以及回调的接口:

@Override
public void onDateIniSelected(String dataIni) {
    Log.i("data inicial:", dataIni);
}

@Override
public void onDateFimSelected(String dataFim) {
    Log.i("data final:", dataFim);
}

回调是字符串,因为日期是查询选择中的参数。

片段的代码(基于初始日期片段):

public class DateIniRangeFragment extends Fragment {
OnDateIniSelectedListener callbackIni;

private DatePicker startDatePicker;

public DateIniRangeFragment() {
    ///required empty constructor
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

///through this interface the fragment sends data to the container activity
public interface OnDateIniSelectedListener {
    void onDateIniSelected(String dataIni);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    ///layout for the fragment
    View v = inflater.inflate(R.layout.date_ini_fragment, container, false);

    ///initial date for the picker, in this case, current date
    startDatePicker = (DatePicker) v.findViewById(R.id.start_date_picker_appcompat);
    Calendar c = Calendar.getInstance();
    int ano = c.get(Calendar.YEAR);
    int mes = c.get(Calendar.MONTH);
    int dia = c.get(Calendar.DAY_OF_MONTH);
    startDatePicker.setSpinnersShown(false);
    startDatePicker.init(ano, mes, dia, dateSetListener);

    return v;
}

///listener that receives the selected date
private DatePicker.OnDateChangedListener dateSetListener = new DatePicker.OnDateChangedListener() {
    public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
        if (view.isShown()) { ///if the datepicker is on the screen
            String sDataIni = year + "-" + (monthOfYear + 1) + "-" + dayOfMonth;
            callbackIni.onDateIniSelected(sDataIni); //apply date to callback, string format
        }
    }
};

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);

    /*
    * this function guarantees that the container activity implemented the callback interface
    * */
    try {
        callbackIni = (OnDateIniSelectedListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString() + " deve implementar OnDateIniSelectedListener");
    }
}

}

为了组成容器+片段,我使用了带有自定义类的ViewPager(AppCompat),该类扩展了FragmentPagerAdapter。没有对话。


2

您只需使用EventBus即可轻松实现出色的工作

3步EventBus

  1. 定义事件:

    public static class MessageEvent { /* Additional fields if needed */ }

  2. 准备订阅者:声明并注释您的订阅方法,可以选择指定线程模式:

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(MessageEvent event) {/* Do something */};

注册和取消注册您的订户。例如,在Android上,活动和片段通常应根据其生命周期进行注册:

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

 @Override
 public void onStop() {
     super.onStop();
     EventBus.getDefault().unregister(this);
 }
  1. 发布事件:

    EventBus.getDefault().post(new MessageEvent());


1

我知道这可能会晚了。但是我也总是在这个问题上迷路。我正在分享此链接...,因为这可能是我在网上找到的最佳解释。这解决了“ 碎片到活动”和“ 碎片到碎片”的问题

解决和解释得很好


0

这对我有用。

在活动中添加此方法

    public void GetData(String data)
     {
        // do something with your data        
     }

并在片段中添加此行

((YourActivity)getContext).GetData("your data here");

0
public class Fragmentdemo extends Fragment {

  public interface onDemoEventListener {
    public void demoEvent(String s);
  }

  onDemoEventListener demoEventListener;

  @Override
  public void onAttach(Activity activity) {
    super.onAttach(activity);
        try {
          demoEventListener = (onDemoEventListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement onDemoEventListener");
        }
  }

  final String LOG_TAG = "TAG";

  public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.fragmentdemo, null);

    Button button = (Button) v.findViewById(R.id.button);
    button.setOnClickListener(new OnClickListener() {
      public void onClick(View v) {
        demoEventListener.someEvent("Test text to Fragment1");
      }
    });
    enter code here
    return v;
  }
}

-12

从另一个活动传递的另一种简单的获取数据的方式是在容器活动的片段中:例如:

Activity_A => Activity_B(片段)

在Activity_A中,您将创建一个意图,就像您将数据(此处为String)发送到另一个活动:

Intent intent = new Intent(getBaseContext(),Activity_B.class);
intent.putExtra("NAME", "Value");
startActivity(intent);

在片段中,包含在您的Activity_B中:

String data = getActivity().getIntent().getExtras();

getBaseContext()给我以下错误:The method getBaseContext() is undefined for the type new View.OnClickListener(){}
Si8 2013年
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.