为了重用fragment ui 组件,在设计中你应该通过定义每一个fragemnt自己的layout和行为,让fragment的自包含和模块化。一旦你定义了这些可重用的fragment,你可以通过activity将它们关联起来并通过程序的逻辑代码将他们连接起来以实现整体组合的ui。
你会经常想要一个fragment与另一个fragment通信,例如基于用户事件改变fragment中的内容。所有fragment质检单额通信都是通过activity关联起来的。两个fragment之间不应该直接进行通信。
定义接口
为了允许fragment和它的activity通信,你应该fragment类中定义一个接口并在activity中实现它。fragment在onattach()回调函数中获取接口的具体实现的对象。后面,fragment就可以调用接口中的方法实现与activity的通信。
下面是一个fragment一activity通信的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// 用来存放fragment的activtiy必须实现这个接口 public interface onheadlineselectedlistener { public void onarticleselected( int position); } @override public void onattach(activity activity) { super .onattach(activity); // 这是为了保证activity容器实现了用以回调的接口。如果没有,它会抛出一个异常。 try { mcallback = (onheadlineselectedlistener) activity; } catch (classcastexception e) { throw new classcastexception(activity.tostring() + " must implement onheadlineselectedlistener" ); } } |
现在fragment可以通过使用实现了onheadlineselectedlistener接口的mcallback实例调用onarticleselected()方法(或其他在接口中的方法)将消息传递给activity。
例如下面在fragment中的方法将会在用户点击一个list item的时候被调用。这个fragment使用回调接口将时间传递给父activtity。
1
2
3
4
5
|
@override public void onlistitemclick(listview l, view v, int position, long id) { // 发送时间给activity宿主 mcallback.onarticleselected(position); } |
实现接口
为了接收从fragment传递过来的事件回调,宿主activity必须实现定义在fragment类中的接口。
例如,下面的activity实现了上个例子中的接口。
1
2
3
4
5
6
7
8
9
|
public static class mainactivity extends activity implements headlinesfragment.onheadlineselectedlistener{ ... public void onarticleselected( int position) { // 用户选择了headlinesfragment中的头标题后 // 做一些业务操作 } } |
将消息传递给fragment
宿主activity可以通过调用findfragmentbyid()方法获取fragment实例,然后直接调用fragment的公有方法,将消息传递给fragment。
例如,想象一下,之前讲过的activity可能包含有其他的fragment,而这些fragment通过上面的回调函数返回的接口动态地显示一些内容。这种情况下,activity可以将回调函数中接收到信息传递给需要的fragment。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
public static class mainactivity extends activity implements headlinesfragment.onheadlineselectedlistener{ ... public void onarticleselected( int position) { // 用户选中headlinesfragment中的头标题后 // 做一些必要的业务操作 articlefragment articlefrag = (articlefragment) getsupportfragmentmanager().findfragmentbyid(r.id.article_fragment); if (articlefrag != null ) { // 如果 article frag 不为空,那么我们在同时显示两个fragmnet的布局中... // 调用articlefragment中的方法去更新它的内容 articlefrag.updatearticleview(position); } else { // 否则,我们就是在仅包含一个fragment的布局中并需要交换fragment... // 创建fragment并给他一个跟选中的文章有关的参数 articlefragment newfragment = new articlefragment(); bundle args = new bundle(); args.putint(articlefragment.arg_position, position); newfragment.setarguments(args); fragmenttransaction transaction = getsupportfragmentmanager().begintransaction(); // 用这个fragment替换任何在fragment_container中的东西 // 并添加事务到back stack中以便用户可以回退到之前的状态 transaction.replace(r.id.fragment_container, newfragment); transaction.addtobackstack( null ); // 提交事务 transaction.commit(); } } } |
动态加载的fragment通信
fragment之间永远不能通信都是通过他们所依附的activity来通信的,通过接口回调的方式来通信
activity--->fragment:在activity中创建bundle数据包,并调用fragment的setarguments(bundle bundle)方法
fragment--->activity:在fragment类中定义一个接口,并在他所属的activity中实现该接口,fragment在他的onattach()方法执行期间捕获该接口的实现,然后就可以调用该接口方法,以便跟activity通信。
下面的代码实现了在一个activity下有两个fragment,在一个fragment中输入值在另一个fragment显示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public class mainactivity extends activity implements mylistener { @override protected void oncreate(bundle savedinstancestate) { super .oncreate(savedinstancestate); setcontentview(r.layout.activity_main); fragmentmanager manager = getfragmentmanager(); fragmenttransaction fragmenttransaction = manager.begintransaction(); fragment1 fragment1 = new fragment1(); fragmenttransaction.add(r.id.layout1, fragment1); fragmenttransaction.commit(); } //在mainactivity中实现mylistener接口并实现它未实现的方法,在该方法中传递数据 @override public void send(string info) { toast.maketext( this , "activity成功接收" + info, 0 ).show(); fragment2 fragment2 = new fragment2(); bundle bundle = new bundle(); bundle.putstring( "name" , info); fragment2.setarguments(bundle); fragmentmanager manager = getfragmentmanager(); fragmenttransaction fragmenttransaction = manager.begintransaction(); //替换该fragment fragmenttransaction.replace(r.id.layout2, fragment2, "fragment2" ); fragmenttransaction.commit(); } } |
上面的mainactivity布局就是添加两个linearlayout,下面是创建两个fragment,fragment1的布局就是一个edittext一个button,fragment2的布局就是一个textview。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
public class fragment1 extends fragment { edittext et; button enter; private string info; @override public void onattach(activity activity) { listener = (mylistener) activity; super .onattach(activity); } @override public view oncreateview(layoutinflater inflater, viewgroup container, bundle savedinstancestate) { view view = inflater.inflate(r.layout.fragment1_lay,container, false ); et = (edittext) view.findviewbyid(r.id.et); enter = (button) view.findviewbyid(r.id.enter); enter.setonclicklistener( new onclicklistener() { @override public void onclick(view v) { info = et.gettext().tostring(); listener.send(info); } }); return view; } public mylistener listener; //定义一个接口 public interface mylistener{ public void send(string info); } } public class fragment2 extends fragment { @override public view oncreateview(layoutinflater inflater, viewgroup container, bundle savedinstancestate) { view view = inflater.inflate(r.layout.fragment2_lay,container, false ); textview tv = (textview) view.findviewbyid(r.id.tv); tv.settext(getarguments().get( "name" )+ "" ); //获取传递的值 toast.maketext(getactivity(), "fragment" +getarguments().get( "name" )+ "" , 0 ).show(); return view; } } |
静态加载的fragment通信
fragment可以调用getactivity()方法获取他所在的activity
activity可以调用fragmentmanager的findfragmentbyid()或findfragmentbytag()方法获取fragment
如果是静态加载的fragmentn那么向activity传递数据就很简单了,在mainactivity的oncreate方法中:
1
2
3
4
|
fragmentmanager fragmentmanager = getfragmentmanager(); fragment fragment = fragmentmanager.findfragmentbyid(r.id.frag); //静态加载在activity布局中设置的fragment的id myfragment frag = (myfragment) fragment; frag.settext1( "fragment静态传值" ); |
在fragment类中设置它的get、set方法,
1
2
3
4
5
6
7
|
private string text1; public string gettext1() { return text1; } public void settext1(string text1) { this .text1 = text1; } |
下面直接调用就可以了:
1
|
string value = gettext1(); |