第 10 章 使用 fragment argument

请参考教材,全面理解和完成本章节内容... ...java

复制工程ch9,将工程目录更名为ch10。安全

本章,咱们将实现CriminalIntent「陋习手记」应用的列表与明细部分的关联。用户点击某个「陋习」crime列表项时,会生成一个负责托管CrimeFragment的CrimeActivity,并显示出某特定Crime实例的明细信息。如图10-1所示。编码

image

图10-1 从CrimeListActivity中启动CrimeActivityspa

10.1 从 fragment 中启动 activity

从fragment中启动activity的实现方式,基本等同于从activity中启动另外一activity的实现方式。咱们调用Fragment.startActivity(Intent)方法,该方法在后台会调用对应的Activity方法。操作系统

CrimeListFragmentonListItemClick(...)实现方法里,用启动CrimeActivity实例的代码,替换日志记录crime标题的代码,如代码清单10-1所示。(暂时忽略Crime变量未使用的提示信息,下一节会使用它)3d

代码清单10-1 启动CrimeActivity(CrimeListFragment.java)日志

image

以上代码中,指定要启动的activity为CrimeActivityCrimeListFragment建立了一个显式intent。在CrimeListFragment中使用getActivity()方法传入它的托管activity,此activityIntent构造方法须要的Context对象。code

运行CriminalIntent应用。点击任意列表项,屏幕上会出现一个托管CrimeFragmentCrimeActivity,如图10-2所示。对象

image

图10-2 空白的CrimeFragmentblog

因为不知道该显示哪一个Crime对象的信息,CrimeFragment也就没有显示出特定Crime对象的数据信息。

10.1.1 附加extra信息

经过将mCrimeId值附加到Intent的extra上,咱们能够告知CrimeFragment应显示的Crime。在onListItemClick(...)方法中,将用户所选CrimemCrimeId值附加到用来启动CrimeActivity的intent上。输入代码清单10-2所示代码,IDE会报告一个错误信息,这是由于CrimeFragment.EXTRA_CRIME_ID的key值尚未建立。暂时忽略该条错误信息,稍后咱们会建立它。

代码清单10-2 启动附加extra的CrimeActivity(CrimeListFragment.java)

image

建立了显式intent后,调用putExtra( )方法,传入匹配mCrimeId的字符串key与key值,完成extra信息的准备。这里,因为UUIDSerializable对象,咱们调用了可接受Serializable对象的putExtra( )方法,即putExtra(String, Serializable)方法。

10.1.2 获取extra信息

简单获取extra的方法是,返回至CrimeFragment类,为extra添加key。而后,在onCreate(Bundle)方法中,获得CrimeActivity的intent内的extra信息后,再使用它获取Crime对象,如代码清单10-3所示。

代码清单10-3 获取extra信息并取得Crime对象(CrimeFragment.java)

image

在代码清单10-3中,除了getActivity()方法的调用,获取extra数据的实现代码与activity里获取extra数据的代码同样。getIntent()方法返回用来启动CrimeActivityIntent。而后调用IntentgetSerializableExtra(String)方法获取UUID并存入变量中。

取得Crime的ID后,利用该ID从CrimeLab单例中调取Crime对象。使用CrimeLab.get(...)方法须要Context对象,所以CrimeFragment传入了CrimeActivity

10.1.3 使用Crime数据更新CrimeFragment视图

既然CrimeFragment获取了Crime对象,它的视图即可显示该Crime对象的数据。参照代码清单10-4,更新onCreateView(...)方法,显示Crime对象的标题及解决状态。(显示日期的代码早已就绪)

代码清单10-4 更新视图对象(CrimeFragment.java)

image
运行应用。选中Crime #4,查看显示了正确crime数据信息的CrimeFragment实例,如图10-3所示。CriminalIntent
image

图10-3 Crime #4列表项的明细内容

10.1.4 直接获取extra信息方式的缺点

只需几行简单的代码,就可实现让fragment直接获取托管activity的intent。然而,这种方式是以牺牲fragment的封装性为代价的。CrimeFragment再也不是可复用的构建单元,由于它老是须要由某个具体activity托管着,该activity的Intent又定义了名为EXTRA_CRIME_ID的extra。

CrimeFragment类来讲,这看起来合情合理。但这也意味着,按照当前的编码实现,CrimeFragment便再也没法用于任何其余的activity了。

一个比较好的作法是,将mCrimeId存储(Stash)在CrimeFragment的某个地方,而不是将它保存在CrimeActivity的私有空间里。这样,无需依赖于CrimeActivity的intent内指定extra的存在,CrimeFragment就能(本身)获取本身所需的extra数据信息。fragment的“某个地方”实际就是它的arguments bundle

10.2  fragment argument

每一个fragment实例均可附带一个Bundle对象。该bundle可含有多个key-value对,咱们能够如同附加extra到Activity的intent中那样使用它们。一个key-value对即一个argument。

要建立fragment argument,首先需建立Bundle对象。而后,使用Bundle限定类型的“put”方法(相似于Intent的方法),将argument添加到bundle中(如如下代码所示)。

Bundle args = new Bundle();

args.putSerializable(EXTRA_MY_OBJECT, myObject);

args.putInt(EXTRA_MY_INT, myInt);

args.putCharSequence(EXTRA_MY_STRING, myString);

10.2.1 附加argumentfragment

附加argument bundle给fragment,需调用Fragment.setArguments(Bundle)方法。注意,该任务必须在fragment建立后、添加给activity前完成。

为知足以上苛刻的要求,Android开发者遵循的习惯作法是:添加名为newInstance()的静态方法给Fragment类。使用该方法,完成fragment实例及bundle对象的建立,而后将argument放入bundle中,最后再附加给fragment。

托管activity须要fragment实例时,需调用newInstance()方法,而非直接调用其构造方法。并且,为知足fragment建立argument的要求,activity可传入任何须要的参数给newInstance()方法。

如代码清单10-5所示,在CrimeFragment类中,编写能够接受UUID参数的newInstance(UUID)方法,经过该方法,完成arguments bundle以及fragment实例的建立,最后附加argument给fragment。

代码清单10-5 编写newInstance(UUID)方法(CrimeFragment.java)

image

如今,当CrimeActivity建立CrimeFragment时,应调用CrimeFragment.newInstance(UUID)方法,并传入从它的extra中获取的UUID参数值。回到CrimeActivity类中,在createFragment()方法里,从CrimeActivity的intent中获取extra数据信息,并将之传入CrimeFragment.newInstance(UUID)方法,如代码清单10-6所示。

代码清单10-6 使用newInstance(UUID)方法(CrimeActivity.java)

image

注意,交互的activity和fragment不须要也没法同时保持通用独立性。CrimeActivity必须了解CrimeFragment的内部细节,好比知晓它内部有一个newInstance(UUID)方法。这很正常。托管activity就应该知道有关托管fragment方法的细节,但fragment则没必要知道其托管activity的细节问题。至少在须要保持fragment通用独立性的时候是如此。

10.2.2 获取argument

fragment在须要获取它的argument时,会先调用Fragment类的getArguments()方法,接着再调用Bundle的限定类型的“get”方法,如getSerializable(...)方法。

如今回到CrimeFragment.onCreate(...)方法中,调整代码,改成从fragment的argument中获取UUID,如代码清单10-7所示。

代码清单10-7 从argument中获取crime ID(CrimeFragment.java)

image

运行CriminalIntent应用。虽然运行结果仍与以前一致,但咱们应该感到由衷地高兴。由于咱们不只保持了CrimeFragment类的独立性,又为下一章实现CriminalIntent应用更为复杂的列表项导航打下了良好基础。

10.3 从新加载显示列表项

运行CriminalIntent应用,点击某个列表项,而后修改对应的Crime明细信息。这些修改的数据被保存至模型层,但返回列表后,列表视图并无发生改变。下面咱们来处理这个问题。

如模型层保存的数据发生改变(或可能发生改变),应通知列表视图的adapter,以便其及时获取最新数据并从新加载显示列表项。在适当的时点,与系统的ActivityManager回退栈协同运做,能够完成列表项的刷新。

CrimeListFragment启动CrimeActivity实例后,CrimeActivity被置于回退栈顶。这致使原先处于栈顶的CrimeListActivity实例被暂停并中止。

用户点击后退键返回到列表项界面,CrimeActivity随即被弹出栈外并被销毁。CrimeListActivity继而被从新启动并恢复运行。应用的回退栈如图10-4所示。

image

图10-4 CriminalIntent应用的回退栈

CrimeListActivity恢复运行状态后,操做系统会向它发出调用onResume()生命周期方法的指令。CrimeListActivity接到指令后,它的FragmentManager会调用当前被activity托管的fragment的onResume()方法。这里,CrimeListFragment即惟一的目标fragment。

在CrimeListFragment中,覆盖onResume()方法刷新显示列表项,如代码清单10-8所示。

代码清单10-8 在onResume()方法中刷新列表项(CrimeListFragment.java)

image

为何选择覆盖onResume()方法来刷新列表项显示,而非onStart()方法呢?当一个activity位于咱们的activity以前时,咱们没法保证本身的activity是否会被中止。如前面的activity是透明的,则咱们的activity可能只会被暂停。对于此场景下暂停的activity,onStart()方法中的更新代码是不会起做用的。通常来讲,要保证fragment视图获得刷新,在onResume()方法内更新代码是最安全的选择。

运行CriminalIntent应用。选择某个crime项并修改其明细内容。而后返回到列表项界面,如预期那样,列表项当即刷新反映了更改的内容。

通过前两章的开发,CriminalIntent应用已得到大幅更新。如今,咱们来看看更新后的应用对象图解,如图10-5所示。

image

图10-5 应用对象图解更新版

相关文章
相关标签/搜索