转载自http://blog.youkuaiyun.com/harvic880925/article/details/44966913
前言:依然没有前言……
相关文章:
1、《Fragment详解之一——概述》
2、《Fragment详解之二——基本使用方法》
3、《Fragment详解之三——管理Fragment(1)》
4、《Fragment详解之四——管理Fragment(2)》
5、《Fragment详解之五——Fragment间参数传递》
6、《Fragment详解之六——如何监听fragment中的回退事件与怎样保存fragment状态》
在关Fragment间参数的传递,有两种情况:
- 第一种情况:同一个container中不同fragment间的参数传递。这种情况一般发生在fragment跳转时,上一个Fragment将参数传递给下一个Fragment。
- 第二种情况:是同一个Activity中,不个container间Fragment的参数传递。
有关第一种情况,以前写过一篇文章,详细说明了上一个Fragment将参数传递给下一个Fragment,及数据回传的方法。详细参见:
《Fragment跳转时传递参数及结果回传的方法》
下面详细看看这两种参数传递方法。
一、同一个container间的参数传递。
有关这个问题,请大家移步以前写的一篇文章:《Fragment跳转时传递参数及结果回传的方法》,在这篇文章中有关Fragment跳转时的参数传递和结果回传已经讲的很清楚了,这里就没必要重新再写一遍,下面是这篇文章的效果图:
- 1、在界面开始时,点击“加载第二个Fragment”按钮
- 2、调起Fragment2,并向其传递一个参数“从Fragment1传来的参数”,显示在Fragment2中
- 3、点击Fragment2中的四个小动物中的一个,会向Fragment1回传用户点击的是哪个动物,在Fragment1中显示出来。
看起来挺有意思?那就移步到这篇文章里看看吧

二、同一个Activity,不同container间的参数传递
这里到了这篇文章的重点内容了哦,这可并不是说上一部分不重要哈,其实上一部分要比这部分重要!同一个Container中不同Fragment间的参数传递一般的工程都会用到的,所以大家一定要看。而我这里不讲,是因为以前有讲过,这里就没必要再重复一遍了,好了,废话说了好多……开整吧。
先看看效果图:
1、在这个Actiivty中有两个Fragment;
2、Fragment1中有一个listView,当我们点击ListView的Item的时候,把Item上的内容更新到Fragment2上

这里有多种实现方法,最可取的是方法三。我们由简到易慢慢讲。
我们想使两个fragment实例要能通信,那如果我们都能通过findViewById()找到所有的控件,直接操控的话,岂不就实现了。而通过findViewById()能找到所有控件实例的地方就是在Activity中了,所以这就有了方法一。
方法一:直接在Activity中操作
在Activity中找到对应的控件实例,然后直接操控即可。
先看看MainActivity的布局:activity_main.xml
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/main_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal"
- android:baselineAligned="false" >
-
- <fragment
- android:id="@+id/fragment1"
- android:name="com.harvic.com.harvicblog5_1.Fragment1"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:layout_weight="1" />
-
- <fragment
- android:id="@+id/fragment2"
- android:name="com.harvic.com.harvicblog5_1.Fragment2"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:layout_weight="1" />
- </LinearLayout>
在这个布局中,横向放两个fragment,由于这里的fragment是静态添加的,所以每个fragment都是有id值的,所以这时候如果我们要获取某个fragment的实例,就可以通过FragmentManager::findFragmentById()来找到了。
然后是这两个fragment的布局。
fragment1.xml:
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#ff00ff"
- android:orientation="vertical" >
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="This is fragment 1"
- android:textColor="#000000"
- android:textSize="25sp" />
-
- <ListView
- android:id="@+id/list"
- android:layout_width="match_parent"
- android:layout_height="match_parent"></ListView>
- </LinearLayout>
可以看到fragment1的布局中,除了一个标识当前Fragment的TextView,就是一个listview;
fragment2.xml:
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#ffff00"
- android:orientation="vertical" >
-
- <TextView
- android:id="@+id/fragment2_tv"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="This is fragment 2"
- android:textColor="#000000"
- android:textSize="25sp" />
-
- </LinearLayout>
可以看到在fragment2中非常干净,只有一个TextView来显示当前用户在fragment1中的点击结果。
下面看看在MainActivity中是如何实现的吧
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
-
- ArrayAdapter arrayAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,mStrings);
- ListView listView = (ListView)findViewById(R.id.list);
- listView.setAdapter(arrayAdapter);
-
- mFragment2_tv = (TextView)findViewById(R.id.fragment2_tv);
-
- listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- mFragment2_tv.setText(mStrings[position]);
- }
- });
- }
其中:
- private String[] mStrings = {"Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
- "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", "Aisy Cendre",
- "Allgauer Emmentaler", "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
- "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", "Aisy Cendre",
- "Allgauer Emmentaler"};
难度不大,通过(ListView)findViewById(R.id.list);找到fragment1中的listview,通过(TextView)findViewById(R.id.fragment2_tv);找到fragment2中的textView,然后直接对他们进行操作。即下面的代码:
- listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- mFragment2_tv.setText(mStrings[position]);
- }
- });
当用户点击listView的一个item时,将值setText到fragment2的textView中。
源码在文章底部给出
可见,直接在activity中操作各个fragment的控件就可以实现消息互传。但,这样真的好吗?如果每个fragment中的控件都在Activity中操作,那还要fragment干嘛!最最起码,应该每个fragment负责自己的控件操作才对嘛!
所以,我们对这种方法进行改进,将点击Item的赋值操作放到fragment1中去。所以,这就有方法二;
方法二:直接在fragment中操作
在这里我们会把所有方法写在Fragment1中,这里涉及到两方面的内容:
第一:在Fragment中如何获得自己控件的引用,比较这里Fragment1里的listview控件。
第二:在Fragment中如何获得其它Fragment页面中控件的引用,比如这里Fragment2里的TextView控件。
首先,获取自己控件引用的方法:
方法一:在onCreateView()中获取。
就比如这里获取自己的listView控件的引用,代码如下:
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View rootView = inflater.inflate(R.layout.fragment1, container, false);
- listView = (ListView)rootView.findViewById(R.id.list);
- return rootView;
- }
由于在onCreateView()中,还没有创建视图,所以在这里如果使用getView()方法将返回空。所以如果要获取其实图中指定控件的引用,只用用inflater.inflate()返回的rootView;在这个rootView()中用findViewById来查找。
方法二:在onActivityCreated()函数中获取。
从
《Fragment详解之一——概述》
的流程图中可以看到,onActivityCreated()回调会在Activity的OnCreate()执行完成后再执行,也就是说,onActivityCreated()会在Activity的OnCreate()工作完成以后才会执行。所以当执行到onActivityCreated()的时候Activity已经创建完成,它其中的各个fragment也视图等等的也都已经创建完成。所在可以在这里获取跟Activity相关的各种资源。第二个问题中的获取其它Fragment页面中控件的引用也是在onActivityCreated()中来做的。先看看在onActivityCreated()中如何获得自己视图中控件的引用吧
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- listView = (ListView) getView().findViewById(R.id.list);
- }
然后,获得其它Fragment页面中控件的引用的方法
在上面已经说了,要获取Activity中的资源,就必须等Acitivity创建完成以后,所以必须放在onActivityCreated()回调函数中。
其获取方法为:
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- mFragment2_tv = (TextView) getActivity().findViewById(R.id.fragment2_tv);
-
- }
上面讲了一堆之后,下面就看看在Fragment1中如何实现的吧。
由上面的讲述可知,无论是获取自己视图中控件的引用还是获取其它fragment中控件的引用都可以放在onActivityCreated()函数中,所以我们就把它们全部放在onActivityCreated()中来实现了。
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- mFragment2_tv = (TextView) getActivity().findViewById(R.id.fragment2_tv);
- listView = (ListView) getView().findViewById(R.id.list);
-
- ArrayAdapter arrayAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, mStrings);
- listView.setAdapter(arrayAdapter);
- listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- String str = mStrings[position];
- mFragment2_tv.setText(str);
- }
- });
- }
难度不大,也是获取到了每个控件以后,直接对他们进行操作。
源码在文章底部给出
我们这里直接在fragment1中操作了fragment2的控件,这样就违背了模块分离的思想,我们应该让他们各自处理各自的代码才好。所以,考虑到将他们分离,我们这里就出现了方法三。
方法三:在各自的fragment中操作
好,我们先想想要怎么解决这个问题,首先,我们把各自的事件放在各自的fragment中处理,即在fragment1中能得到当前用户点击的ITEM的String字符串,在fragment2中可以设置textview的值。那问题来了,各自获得了各自的东东,那通过什么让他们交互呢?
答案显然是activity。
很显然,在activity中可以直接通过FragmentManager::findFragmentById()来获取fragment2的实例。进而调用fragment2中的任意方法,这就实现了与fragment2的通信。
那fragment1又怎么把结果回传给Activity呢?大家如果在看到这里之前先看了《Fragment跳转时传递参数及结果回传的方法》就很容易想到,用回调!请记住。在不同页面中回传结果,回调是万能的解决方案。
1、Fragment2设置textView函数:
先看个简单的,fragment2中的处理代码:
- public class Fragment2 extends Fragment {
- private TextView mTv;
- …………
- public void setText(String text) {
- mTv.setText(text);
- }
- }
2、Fragment1中的处理方式:
(1)、定义接口及变量
由于是用回调,所以要先定义一个接口及对应的变量:
- private titleSelectInterface mSelectInterface;
-
- public interface titleSelectInterface{
- public void onTitleSelect(String title);
- }
(2)、接口变量赋值
接口是给activity用的,所以要在activity中给这里的接口变量赋值,可以有很多方法,当然可以选择写一个setXXX()函数来赋值,但如果用户忘了怎么办?所以我们要强制用户赋值。所以采用强转的方式,在fragment与activity相关联时,进行强转赋值:
- public void onAttach(Activity activity) {
- super.onAttach(activity);
-
- try {
- mSelectInterface = (titleSelectInterface) activity;
- } catch (Exception e) {
- throw new ClassCastException(activity.toString() + "must implement OnArticleSelectedListener");
- }
- }
采用强转的方式的问题在于,如果用户的activity没有implements titleSelectInterface,就会抛出错误,所以在调试过程中就会发现。
(3)、调用接口变量
下一步就是在fragment1中在用户点击listView的item的时候,将结果回传给Activity了,代码如下:
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- listView = (ListView) getView().findViewById(R.id.list);
- ArrayAdapter arrayAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, mStrings);
- listView.setAdapter(arrayAdapter);
- listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- String str = mStrings[position];
- mSelectInterface.onTitleSelect(str);
- }
- });
- }
(4)、在Activity中实现titleSelectInterface接口
首先是MainActivity必须实现titleSelectInterface接口,然后结果会在onTitleSelect(String title)中返回,在结果返回后利用fragment2.setText()操作textView;代码如下:
- public class MainActivity extends FragmentActivity implements Fragment1.titleSelectInterface {
-
- ……
-
- @Override
- public void onTitleSelect(String title) {
- FragmentManager manager = getSupportFragmentManager();
- Fragment2 fragment2 = (Fragment2)manager.findFragmentById(R.id.fragment2);
- fragment2.setText(title);
- }
- }
在上面代码中可以看出,在结果返回后,通过findFragmentById()来获得fragment2的实例,这里首次出现了findFragmentById()函数的用法,这个函数主要用来静态添加的fragment中,通过fragment的ID值来获取它的实例。在获得fragment2的实例以后,通过调用我们写好了setText()方法来将结果显示在textView中。
- Fragment2 fragment2 = (Fragment2)manager.findFragmentById(R.id.fragment2);
OK啦,到这里这篇文章就结束啦,源码过来啦。
参考文章:
1、《android Fragments详解五:与activity通讯》
2、《Inter-Fragment Communication》