跟其他视图对象一样,fragment既可以在XML布局中定义,也可以通过代码直接添加到视图中。在如下布局中添加fragment:
<LinearLayout xmlns:android=/"http://schemas.android.com/apk/res/android/" android:orientation=/"horizontal/" android:layout_ android:layout_><fragment android:id=/"@+id/date_time/" android:layout_ android:layout_ /></LinearLayout>
Activity可以通过如下方式使用该布局:
@Overridepublic void onCreate(Bundle state) { super.onCreate(state); setContentView(R.layout.main);}
这段代码现在看起来应该是相当熟悉了。唯一的区别在于main.xml文件中的fragment标签。该标签使用class属性指定了实现fragment的类的完整名称。实现Fragment的类中包含一些约束条件,在这个例子中,实现Fragment的类是com.oreilly.demo.android.ch085.contactviewer.DateTime,其约束条件包括:
·指定的类必须存在,并且对于应用是可见的。
·该类必须是Fragment类的子类。
虽然静态验证这些约束是可行的,但是,当前的Android工具并不支持。需要手工检查这些约束条件。
当布局inflate时,Android框架会创建该类的新实例。其实现可能很让人意外。首先,它表示该类必须包含没有参数的构造函数,即Java默认提供的构造函数。实际上,Android开发者文档强烈建议不要在Fragment的子类中定义任何构造函数,因为如果定义构造函数,则新创建的Fragment对象在创建时可能会不一致。开发者文档建议推迟对fragment的初始化,待进入fragment的生命周期之后再执行。
无论你在应用的其他地方如何使用fragment,如果你在布局中使用它,inflation过程必须能够创建它,而且不需要提供任何初始化参数。因此,以这种方式创建的fragment即使没有执行初始化,也需要执行一些操作。例如,显示从URL接收的内容的fragment必须处理URL及其内容为空的情况。
以下是一个非常简单的fragment:
public class DateTime extends Fragment { private String time; public void onCreate(Bundle state) { super.onCreate(state); if (null == time) { time = new SimpleDateFormat(/"d MMM yyyy HH:mm:ss/") .format(new Date); } } @Override public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle b) { View view = inflater.inflate( R.layout.date_time, container, false); //!!! this is important ((TextView) view.findViewById(R.id.last_view_time)) .setText(time); return view; }}
这段代码有几个要点。首先,onCreate生命周期方法的实现应该使我们能够想到Activity类及其具有生命周期的方法。虽然Fragment的生命周期和Activity的生命周期不完全相同,但它们包含很多相同的方法。对于Activity,fragment的onCreate方法是在fragment初始化时调用的。这是执行fragment初始化的非常好的时机。在这个例子中,很好地确保了变量time的值及fragment要显示的都能够正确初始化。
Fragment包含一些额外的有生命周期的方法,包括本例中所使用的onCreateView。当fragment的视图执行初始化时,会调用onCreateView方法(和onCreate方法不同,onCreateView方法是在fragment本身执行初始化时调用的)。注意,fragment通过传递LayoutInflater对视图碎片(view shard)R.layout.date_time执行实例化来创建视图。简单的视图碎片,仅仅是在RelativeLayout中的一组TextView,是在自己的文件中定义的,layout/date_time.xml(这里没有给出),和之前显示的主要布局很相似。
还要注意的是(这块有点复杂),对inflate的调用的第三个参数是布尔型的false。这一点很重要。Inflater必须能够访问container,即最终会新创建的视图碎片的父亲。它需要成为父视图才能够正确地处理布局。例如,举个例子,container是RelativeLayout,它通过layout_toRightOf指令创建新的碎片的位置。
另一方面,fragment框架拥有并管理着onCreateView方法所返回的视图。在onCreateView方法中,不应该把视图碎片附着到container中,因为在inflation阶段通常会执行这个操作。第三个参数是个标志,它表明fragment框架会控制操作,不应该把视图碎片关联到容器上。
一旦创建了fragment视图,就可以使用视图的findViewById方法找到其他嵌套的widget。这个例子使用findViewById方法找到了要显示时间的TextView,并把其值设置成在onCreate方法中初始化的变量time的值。运行时的情况如图7-1所示。
图7-1:一个简单的fragment