首页 » Android程序设计:第2版 » Android程序设计:第2版全文在线阅读

《Android程序设计:第2版》创建Fragment

关灯直达底部

跟其他视图对象一样,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