Skip to content

Files

Latest commit

94f6a4e · Jan 8, 2022

History

History
228 lines (163 loc) · 12.6 KB

06.md

File metadata and controls

228 lines (163 loc) · 12.6 KB

六、片段

一个片段是一个活动的封装部分。片段允许您将 UI 组件和行为配置到一个可重用的实体中,然后将该实体嵌入到几个不同的活动中。这为安卓应用打开了各种布局和导航的机会。你可以把它们看作是一个活动的“片段”,然后以不同的方式组合起来形成一个完整的活动。下图向您展示了开发安卓应用程序时可以使用的不同片段。

图 57:在智能手机和平板电脑布局中重用主片段和细节片段

片段最常见的用例之一是实现主-细节 UI 模式。例如,电子邮件应用程序包含邮件列表(主界面)和查看邮件正文的单独位置(详细界面)。通过将一个片段用于消息列表,另一个片段用于消息正文,您可以以不同的方式组合片段,从而轻松支持多个屏幕维度。在平板电脑上,您可能会在单个活动中显示这两个片段,以充分利用屏幕空间,而在智能手机上,您可能会希望在占用整个屏幕的专用活动中显示它们。因为主片段和细节片段是可重用的,所以您所要做的就是根据屏幕尺寸以不同的方式组合它们——它们所有的用户界面组件和行为都是可重用的。

还可以无缝地在活动中交换片段。这使您可以在单个活动中显示不同类型的界面和行为,这支持两种常见的导航模式:滑动视图和选项卡式导航。

图 58:片段之间的滑动和跳转

滑动视图旨在让用户在主-细节应用程序中的兄弟细节项目之间导航。例如,在电子邮件应用程序中,用户可以滑动屏幕来前后移动他们的消息,而不必被迫导航回主列表。也可以使用滑动手势在标签之间导航,我们将在本章后面讨论。

标签导航允许用户使用屏幕顶部的标签在片段之间切换。选项卡用于在应用程序的顶层部分之间切换,因此在任何给定时间显示的选项卡不应超过三四个。例如,电子邮件应用程序可能有选项卡,用于在收件箱、您的星号消息和您的电子邮件设置之间切换。

本章提供了片段的基本介绍。我们将学习如何创建它们,将它们嵌入到活动中,并使用它们来实现滑动视图和选项卡式导航。您应该理解如何将功能封装到片段中,并在各种活动中重用该功能。本书示例代码中包含的片段项目演示了我们将要讨论的所有内容。

创造片段

片段由两部分组成:一个定义片段外观的 XML 布局文件,以及一个加载该布局并定义其行为的类。请注意,这些是活动所需的完全相同的组件。

片段示例项目包括两个片段:一个HomeFragment和一个ArticlesFragment。它们都显示一个带有名称和彩色背景的文本字段,这样一旦我们将它们加载到活动中,您就可以看到它们的尺寸。每个片段的 XML 看起来与活动的完全一样。例如,home_view.xml的定义如下:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFDDFFDD">

    <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:textSize="32sp"
    android:textColor="#FF009900"
    android:text="Home Fragment" />

    </RelativeLayout>

接下来,我们需要定义片段的子类来加载这个布局。Fragment类只在 Android 3.0 (API 11)中添加。如果不需要支持 API 11 以下的东西,可以从android.app.Fragment导入Fragment类,但是可以通过android.support.v4.app.Fragment从支持库中导入 Fragment 支持回安卓 1.6。本章的示例代码使用后一种方法。

HomeFragment.java类定义了主片段的行为。onCreateView()必须在Fragment子类中覆盖的唯一方法,因为它返回代表片段的根View。在这种情况下,我们需要做的就是膨胀 XML 布局:

    import android.os.Bundle;
    import android.support.v4.app.Fragment;
    import android.view.LayoutInflater;
    import android.view.ViewGroup;
    import android.view.View;

    public class HomeFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
    Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.home_view, container, false);
    }
    }

片段遵循与活动相同的生命周期模式。您可以在Fragment子类中定义自定义onCreate()onPause()onResume()以及我们在本书前面讨论的所有其他活动生命周期回调方法。当一个生命周期方法在一个宿主活动上被调用时,它会将这个生命周期方法传递给它包含的任何片段。这是片段如此模块化的部分原因——它们的行为就像活动一样,但完全是独立的。

在活动中嵌入片段

片段必须由活动主持。不可能单独显示片段,尽管它可能是活动中的唯一元素。如果您将activity_main.xml更改为以下内容,您将看到如何在活动中嵌入片段:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <fragment android:name="com.example.fragments.HomeFragment"
    android:id="@+id/homeFragment"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1" />

    <fragment android:name="com.example.fragments.ArticlesFragment"
    android:id="@+id/articlesFragment"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1" />

    </LinearLayout>

这包括主活动中的HomeFragmentArticlesFragment,结果如下截图所示。如你所见,片段嵌入了<fragment>标签。最重要的属性是android:name,它应该是定义片段的类(包括应用程序包)的绝对路径。

请记住,片段的目标是成为一个可重用的用户界面。这意味着它应该能够拉伸或收缩,以匹配主机活动定义的任何大小。这就是片段的根元素使用match_parent作为其android:layout_widthandroid:layout_height的原因。上面的代码使每个片段占据了屏幕的一半:

图 59:在一个活动中显示两个片段

现在,片段可能看起来只是布局文件(它们是)的抽象,但是它们支持各种其他导航选项。由于它们是可重用的,我们可以在同一个活动中显示它们(就像我们在这里所做的那样),在单独的活动中显示它们,用滑动视图对它们进行分页,或者让用户通过选项卡访问它们。

滑动视图

片段可以使用滑动视图进行分页,这是通过视图分页类实现的。建议仅针对少量屏幕使用滑动视图,并且这些屏幕中的每一个都应该是独立的。如果您让用户浏览一组数据项,您还应该提供一个主列表作为导航的替代方式,并且您不应该使用滑动视图来为长文章分页。

要向主活动添加滑动视图,请用以下内容替换activity_main.xml中的<fragment>元素:

    <android.support.v4.view.ViewPager
    android:id="@+id/fragmentPager"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

这增加了一个视图页面,支持回到安卓 1.6。将使用适配器在MainActivity.java中填充ViewPager,就像填充ListViewGridView一样。首先,它需要定义fragmentpage adapter的一个子类,在下面的代码中称为SimplePagerAdapter。其getItem()方法返回与页面关联的片段。在这种情况下,第一页返回HomeFragment,第二页返回ArticlesFragment。当然,如果你试图浏览一个集合,可以从一个数据集动态生成这些页面。要填充ViewPager,我们需要做的就是将其adapter属性设置为SimplePagerAdapter

    import android.os.Bundle;
    import android.support.v4.app.Fragment;
    import android.support.v4.app.FragmentActivity;
    import android.support.v4.app.FragmentManager;
    import android.support.v4.app.FragmentPagerAdapter;
    import android.support.v4.view.ViewPager;
    import android.view.Menu;

    public class MainActivity extends FragmentActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    SimplePagerAdapter adapter = new SimplePagerAdapter(getSupportFragmentManager());
    ViewPager pager = (ViewPager) findViewById(R.id.fragmentPager);
    pager.setAdapter(adapter);
    }

    public static class SimplePagerAdapter extends FragmentPagerAdapter {
    public SimplePagerAdapter(FragmentManager fragmentManager) {
    super(fragmentManager);
    }

    @Override
    public int getCount() {
    return 2;
    }

    @Override
    public Fragment getItem(int position) {
    switch (position) {
    case 0:
    return new HomeFragment();
    case 1:
    return new ArticlesFragment();
    default:
    return null;
    }
    }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
    }

    }

每个片段现在应该占据整个屏幕,您应该能够在它们之间滑动。注意MainActivity.java扩展了片段活动而不是Activity。如果你想支持安卓 3.0 之前的任何东西,这是必要的。如果你不需要这样做,你只需要扩展Activity

添加制表符

选项卡与动作栏协同工作,这是我们到目前为止使用的所有示例顶部的栏。您可以通过使用newTab()方法从ActionBar请求一个标签来创建新的标签,设置它的标题,给它一个监听器来响应事件,然后使用addTab()方法将其添加到ActionBar中。这都在下面的片段中得到了演示,应该会用到MainActivity.javaonCreate()方法:

    final ActionBar actionBar = getActionBar();
    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    TabListener tabListener = new TabListener() {
    public void onTabSelected(Tab tab,
    android.app.FragmentTransaction ft) {
    pager.setCurrentItem(tab.getPosition());
    }
    public void onTabReselected(Tab arg0,
                                    android.app.FragmentTransaction arg1) {
    // TODO Auto-generated method stub
    }
    public void onTabUnselected(Tab arg0,
                                    android.app.FragmentTransaction arg1) {
    // TODO Auto-generated method stub
    }
    };
    actionBar.addTab(actionBar.newTab().setText("Home").setTabListener(tabListener)); actionBar.addTab(actionBar.newTab().setText("Articles").setTabListener(tabListener));

每当选择一个选项卡时,就会调用onTabSelected()回调方法。要显示相关的片段,我们需要做的就是告诉ViewPager改变它的currentItem属性。您现在应该能够选择标签来更改片段,但是当您在它们之间滑动时,标签不会更新。为了解决这个问题,我们需要监听页面变化,并在用户滑动时更新选项卡,如下所示:

    pager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
    public void onPageSelected(int position) {
    actionBar.setSelectedNavigationItem(position);
    }
    });

标签和滑动现在应该可以正常工作了。

图 60:使用标签在片段之间导航

总结

在本章中,我们学习了如何用Fragment子类封装用户界面的可重用部分,并使用滑动视图和选项卡显示它们。这是片段的常见用例,但它们也支持其他导航模式,如主-细节模式、灵活的用户界面和复杂的多面板布局。

下一章将远离安卓的用户界面框架,并解释如何保存和加载应用程序收集的数据。