热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

二十八、片段

二十八、片段2011年Android开发者面临的最大变化可能是Android3.0引

二十八、片段

2011 年 Android 开发者面临的最大变化可能是 Android 3.0 引入了片段系统,以及最近 Android 4.0 冰淇淋三明治将片段系统合并到主代码库。片段是一个可选层,可以放在活动和小部件之间,旨在帮助您重新配置活动,以支持大屏幕(例如平板电脑)和小屏幕(例如手机)。然而,片段系统也增加了额外的复杂性,这将需要 Android 开发者社区一些时间来适应。因此,使用片段的公共评论、博客帖子和示例应用就更少了,因为片段是在 Android 之后很久才被引入的。

本章涵盖了片段的基本用途,包括在 Android 3.0 之前的设备上支持片段。

引入片段

片段不是小部件,像ButtonEditText。片段不是容器,像LinearLayoutRelativeLayout。片段不是活动。

相反,片段集合了小部件和容器。然后可以将片段放入活动中——有时一个活动有几个片段,有时每个活动有一个片段。原因是 Android 屏幕尺寸的变化。

片段解决的问题

平板电脑的屏幕比手机大。电视的屏幕比平板电脑大。利用额外的屏幕空间是有意义的,正如第 25 章中所描述的那样,该章解释了如何处理多种屏幕尺寸。在那一章中,我们分析了一个EU4You示例应用,最终以一个活动结束,该活动将在一个不同的布局中为更大尺寸的屏幕加载,该布局具有一个嵌入的WebView小部件。该活动将检测小部件的存在,并使用它来加载与选定国家相关的网页内容,而不是启动一个单独的浏览器活动或只包含一个WebView的活动。

然而,第 25 章中概述的场景相当琐碎。想象一下,我们有一个包含 28 个小部件的TableLayout,而不是一个WebView。在更大尺寸的屏幕上,我们希望TableLayout和相邻的ListView在同一活动中;在较小的屏幕上,我们希望TableLayout在一个单独的活动中,因为没有足够的空间。为了使用早期的 Android 技术做到这一点,我们需要复制两个活动中所有的TableLayout处理逻辑,创建一个活动基类并希望两个活动都可以继承它,或者将TableLayout和它的内容变成一个定制的ViewGroup…或者做其他事情。这仅仅是针对一个这样的场景——在一个更大的应用中乘以许多活动,复杂性就会增加。

片段溶液

片段减少了复杂性,但没有消除。

对于片段,可以在多个活动中使用的用户界面的每个离散块(基于屏幕大小)都放在一个片段中。根据屏幕大小,正在讨论的活动决定了谁得到片段。

EU4You的例子中,我们有两个片段。一个片段代表国家列表。另一个片段表示该国家的详细信息(在我们的例子中,是一个WebView)。在大屏幕设备上,我们希望两个片段都在一个活动中,而在小屏幕设备上,我们将这些片段放在两个独立的活动中。这为大屏幕用户提供了与上一个版本的EU4You相同的好处:用更少的点击获得更多信息。然而,我们用片段演示的技术将更具可伸缩性,能够处理比简单的EU4YouWebView或【非】场景更复杂的 UI 模式。

在这种情况下,我们的整个 UI 都在片段中。那是不必要的。片段是一种选择加入的技术——你只需要它们用于你的 UI 中可能出现在不同场景的不同活动中的部分。事实上,您的活动根本不会改变(比如说,一个帮助屏幕)可能不会使用任何片段。

片段还为我们提供了其他一些额外的功能,包括:


  • 基于用户交互动态添加片段的能力:例如,Gmail 应用最初显示用户邮件文件夹的ListFragment。轻按一个文件夹会在屏幕上添加第二个ListFragment,显示该文件夹中的对话。点击一个对话会在屏幕上添加第三个Fragment,显示该对话中的信息。

  • 动态片段在屏幕上来回移动的动画功能:例如,当用户在 Gmail 中点击一个对话时,文件夹ListFragment从屏幕上滑向左边,对话ListFragment向左滑动并缩小以占据更少的空间,而消息Fragment从右边滑入。

  • 动态片段的自动后退按钮管理:例如,当用户在查看消息Fragment时按下后退,则Fragment滑向右侧,对话ListFragment向右滑动并扩展以填充更多屏幕,文件夹ListFragment从左侧滑回。所有这些都不必由开发人员来管理——只需通过FragmentTransaction添加动态片段,就可以让 Android 自动处理后退按钮,包括反转所有动画。

  • 向选项菜单添加选项的能力,因此也向动作栏添加选项:调用片段的onCreate()中的setHasOptionsMenu()来注册对此感兴趣,然后像在活动中一样覆盖片段中的onCreateOptionsMenu()onOptionsItemSelected()。片段还可以注册小部件来拥有上下文菜单,并像处理活动一样处理这些上下文菜单。

  • 向动作栏添加标签的能力:动作栏可以有标签,取代了TabHost,其中每个标签的内容都是一个片段。类似地,动作栏可以有一个导航模式,用一个Spinner在模式之间切换,其中每个模式由一个片段表示。

如果你可以使用任何运行 Honeycomb 或 Ice Cream Sandwich 的最新设备,启动 Gmail 应用来查看所有片段的功能。

Android 兼容性库

如果片段只适用于 Android 3.0 和更高版本,我们将回到起点,因为今天并不是所有的 Android 设备都运行 Android 3.0 和更高版本。

幸运的是,情况并非如此,因为 Google 已经发布了 Android 兼容性库(ACL),它可以通过 Android SDK 和 AVD 管理器获得(在这里,您可以安装其他 SDK 支持文件,创建和启动您的仿真器 AVD,等等)。ACL 允许您访问从 Android 1.6 开始的 Android 版本上的片段系统。因为绝大多数 Android 设备运行的是 1.6 或更高版本,这允许您在保持向后兼容性的同时开始使用片段。随着时间的推移,对于希望使用该库的应用,该库可能会添加其他特性来帮助实现向后兼容性。

本章中的材料着重于在使用分段时使用 ACL。一般来说,对片段使用 ACL 几乎等同于直接使用原生 Android 3.0 片段类。

由于 ACL 仅支持 Android 1.6 的版本,Android 1.5 设备将无法使用基于片段的应用。这在目前的 Android 设备中只占很小的一部分——在撰写本文时大约是 1%。

创建片段类

建立基于片段的应用的第一步是为每个片段创建片段类。正如您从Activity(或子类)继承活动一样,您从Fragment(或子类)继承片段。

在这里,我们将检查Fragments/EU4You_6示例项目和它定义的片段。

注:本书的约定将是使用“片段”作为通用名词,Fragment指代实际的Fragment类。

一般片段

除了从Fragment继承之外,片段唯一需要的就是覆盖onCreateView()。这将作为把片段放在屏幕上的一部分被调用。您需要返回一个代表片段主体的View。最有可能的是,您将通过一个 XML 布局文件来创建您的片段的 UI,并且onCreateView()将扩展该片段布局文件。

例如,下面是来自EU4You_6DetailsFragment,它将围绕我们的WebView显示给定国家的网页内容:

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

public class DetailsFragment extends Fragment {
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
                           Bundle savedInstanceState) {
    return(inflater.inflate(R.layout.details_fragment, container, false));
  }

public void loadUrl(String url) {
    ((WebView)(getView().findViewById(R.id.browser))).loadUrl(url);
  }
}`

注意,我们不是从android.app.Fragment继承,而是从android.support.v4.app.Fragment继承。后者是来自 ACL 的Fragment实现,因此它可以跨 Android 版本使用。

onCreateView()实现扩展了一个布局,碰巧其中有一个WebView:


xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/browser"
  android:layout_
  android:layout_
/>

它还公开了一个loadUrl()方法,宿主活动使用它来告诉片段是时候显示一些 web 内容了,并提供 URL 来做同样的事情。DetailsFragmentloadUrl()的实现使用getView()检索onCreateView()中创建的View,找到其中的WebView,将loadUrl()调用委托给WebView

Fragment上有无数其他可用的生命周期方法。更重要的包括活动的标准onCreate()onStart()onResume()onPause()onStop()onDestroy()方法的镜子。由于片段是带有小部件的片段,它将实现更多以前可能驻留在这些方法的活动中的业务逻辑。例如,在onPause()onStop()中,由于用户可能不会返回到您的应用,您可能希望将任何未保存的编辑保存到某个临时存储中。在DetailsFragment的例子中,这里没有真正合格的东西,所以那些生命周期方法被单独留下。

列表片段

一个肯定会流行的Fragment子类是ListFragment。这将一个ListView封装在一个Fragment中,旨在简化国家、邮件文件夹、邮件对话等列表的设置。类似于一个ListActivity,你所需要做的就是用你选择和配置的ListAdapter调用setListAdapter(),并且当用户点击列表中的一行时覆盖onListItemClick()来响应。

EU4You_6中,我们有一个代表可用国家列表的CountriesFragment。它初始化onActivityCreated()中的ListAdapter,这个函数在onCreate()结束保存片段的活动后被调用:

`@Override
public void onActivityCreated(Bundle state) {
  super
.onActivityCreated(state);

setListAdapter(new CountryAdapter());

if (state!=null) {
    int position=state.getInt(STATE_CHECKED, -1);

if (position>-1) {
      getListView().setItemChecked(position, true);
    }
  }
}`

处理提供给onCreate()Bundle的代码将在本章稍后解释。

CountryAdapter与之前的EU4You样本几乎相同,除了在Fragment上没有getLayoutInflater()方法,所以我们必须在LayoutInflater上使用静态from()方法,并通过getActivity()提供我们的活动:

`class CountryAdapter extends ArrayAdapter {
  CountryAdapter() {
    super(getActivity(), R.layout.row, R.id.name, EU);
  }

@Override
  public View getView(int position, View convertView,
                     ViewGroup parent) {
    CountryWrapper wrapper=null;

if (cOnvertView==null) {
      cOnvertView=LayoutInflater
                    .from(getActivity())
                    .inflate(R.layout.row, null);
      wrapper=new CountryWrapper(convertView);
      convertView.setTag(wrapper);
    }
    else {
      wrapper=(CountryWrapper)convertView.getTag();
    }

wrapper.populateFrom(getItem(position));

return(convertView);
  }
}`

同样,CountryWrapper与之前的EU4You样品没有任何不同:

`static class CountryWrapper {
  private TextView name=null;
  private ImageView flag=null;
  private View row=null;

CountryWrapper(View row) {
    this.row=row;
    name=(TextView)row.findViewById(R.id.name);
    flag=(ImageView)row.findViewById(R.id.flag);
  }

TextView getName() {
    return(name);
  }

ImageView getFlag() {
    return(flag);
  }

void populateFrom(Country nation) {
    getName().setText(nation.name);
    getFlag().setImageResource(nation.flag);  }
}`

国家列表也是一样的:

static {
  EU**.add**(new **Country**(R.string.austria, R.drawable.austria,
                    R.string.austria_url));
  EU**.add**(new **Country**(R.string.belgium, R.drawable.belgium,
                    R.string.belgium_url));
  EU**.add**(new **Country**(R.string.bulgaria, R.drawable.bulgaria,
                    R.string.bulgaria_url));
  EU0**.add**(new **Country**(R.string.cyprus, R.drawable.cyprus,
                    R.string.cyprus_url));
  EU**.add**(new **Country**(R.string.czech_republic,
                    R.drawable.czech_republic,
                    R.string.czech_republic_url));
  EU**.add**(new **Country**(R.string.denmark, R.drawable.denmark,
                    R.string.denmark_url));
  EU**.add**(new **Country**(R.string.estonia, R.drawable.estonia,
                    R.string.estonia_url));
  EU**.add**(new **Country**(R.string.finland, R.drawable.finland,
                    R.string.finland_url));
  EU**.add**(new **Country**(R.string.france, R.drawable.france,
                    R.string.france_url));
  EU**.add**(new **Country**(R.string.germany, R.drawable.germany,
                    R.string.germany_url));
  EU**.add**(new **Country**(R.string.greece, R.drawable.greece,
                    R.string.greece_url));
  EU**.add**(new **Country**(R.string.hungary, R.drawable.hungary,
                    R.string.hungary_url));
  EU**.add**(new **Country**(R.string.ireland, R.drawable.ireland,
                    R.string.ireland_url));
  EU**.add**(new **Country**(R.string.italy, R.drawable.italy,
                    R.string.italy_url));
  EU**.add**(new **Country**(R.string.latvia, R.drawable.latvia,
                    R.string.latvia_url));
  EU**.add**(new **Country**(R.string.lithuania, R.drawable.lithuania,
                    R.string.lithuania_url));
  EU**.add**(new **Country**(R.string.luxembourg, R.drawable.luxembourg,
                    R.string.luxembourg_url));
  EU**.add**(new **Country**(R.string.malta, R.drawable.malta,
                    R.string.malta_url));
  EU**.add**(new **Country**(R.string.netherlands, R.drawable.netherlands,
                    R.string.netherlands_url));
  EU**.add**(new **Country**(R.string.poland, R.drawable.poland,
                    R.string.poland_url));
  EU**.add**(new **Country**(R.string.portugal, R.drawable.portugal,
                    R.string.portugal_url));
  EU**.add**(new **Country**(R.string.romania, R.drawable.romania,
                    R.string.romania_url));
  EU**.add**(new **Country**(R.string.slovakia, R.drawable.slovakia,
                    R.string.slovakia_url));
  EU**.add**(new **Country**(R.string.slovenia, R.drawable.slovenia,
                    R.string.slovenia_url));
  EU**.add**(new **Country**(R.string.spain, R.drawable.spain,
                    R.string.spain_url));
EU**.add**(new **Country**(R.string.sweden, R.drawable.sweden,
                    R.string.sweden_url));
  EU**.add**(new **Country**(R.string.united_kingdom,
                    R.drawable.united_kingdom,
                    R.string.united_kingdom_url));
}

…正如Country的定义一样,来自一个单独的公共类:

`public class Country {
  int name;
  int flag;
  int url;

Country(int name, int flag, int url) {
    this.name=name;
    this.flag=flag;
    this.url=url;
  }
}`

持续突出显示

当你使用像 Gmail 这样基于片段的应用时,有一件事会让你眼前一亮。当您点击列表中的一行,并且在同一活动中显示(或更新)另一个片段时,您点击的行会保持高亮显示。这与传统的使用ListView背道而驰,在传统的使用中,列表选择器只有在使用 D-pad、轨迹球或类似的定点设备时才会出现。目的是向用户显示相邻片段的上下文。

实际的实现与您预期的不同。这些ListView小部件实际上实现了CHOICE_MODE_SINGLE,通常使用行右侧的RadioButton来呈现。然而,在ListFragment中,单选ListFragment的典型样式是通过一个“激活的”背景。

EU4You_6中,这是通过我们的**Country**Adapter使用的行布局(res/layout/row.xml)来处理的:

`
  android:layout_
  android:layout_
  android:padding="2dip"
  android:min
  

      android:layout_gravity="center_vertical|right"
    android:textSize="5mm"
  />
`

注意style属性,它指向一个activated样式。这被EU4You_6定义为本地风格,而不是操作系统提供的风格。事实上,它必须有两个样式的实现,因为“激活”的概念是 Android 3.0 的新功能,不能在以前的 Android 版本中使用。

因此,EU4You_6有一个向后兼容的空样式的res/values/styles.xml:

`
  
  

`

它还有res/values-v11/styles.xml-v11资源集后缀意味着这将只在 API Level 11 (Android 3.0)及更高版本上使用。这里,风格继承了标准的 Android 全息主题,并使用标准的激活背景色:

`
  
    ?android:attr/activatedBackgroundIndicator
  

`

CountriesFragment中,活动将通过enablePersistentSelection()方法让我们知道CountriesFragment是否出现在DetailsFragment旁边——因此需要单选模式:

public void **enablePersistentSelection**() {
  **getListView**()**.setChoiceMode**(ListView.CHOICE_MODE_SINGLE);
}

同样,在onListItemClick()CountriesFragment“检查”用户点击的行,从而启用持久高亮:

`@Override
public void onListItemClick(ListView l, View v, int position,
                           long id) {
  l.setItemChecked(position, true);

if (listener!=null) {
    listener.onCountrySelected(EU.get(position));
  }
}`

listener对象和对on**Country**Selected()的调用将在本章后面解释。

其他片段基类

ACL 还有另外一个子类Fragment : DialogFragment。这用于帮助协调模态Dialog和基于片段的 UI。

Android 3.0 本身还有两个子类Fragment,在本文撰写之时,ACL 中还没有:


  • PreferenceFragment:用于新型蜂巢式PreferenceActivity(包含在第 31 章中)

  • WebViewFragment:一个Fragment缠绕着一个WebView


片段、布局、活动和多种屏幕尺寸

拥有一些片段类及其伴随的布局当然很好,但是我们需要将它们与活动联系起来,并让它们出现在屏幕上。在这个过程中,我们必须考虑如何处理多种屏幕尺寸,就像我们对先前版本的EU4You示例采用的WebViewor-browser 方法一样。

在 Android 3.0 及更高版本中,任何活动都可以托管一个片段。但是,对于 ACL,需要从FragmentActivity继承才能使用片段。ACL 的这种限制肯定会带来挑战,特别是如果您打算将地图放入片段中,这是我们将在本书后面讨论的主题。其他活动基类造成的问题更少——例如,ListActivity将由ListFragment代替。

片段可以通过两种方式添加到活动中:


  • 您可以通过活动布局中的元素来定义它们。这些片段是固定的,并且将在该活动实例的生存期内一直存在。

  • 您可以通过FragmentManagerFragmentTransaction即时添加它们。这为您提供了更多的灵活性,但也增加了复杂性。这种技术不在本书讨论范围之内。

处理多种屏幕尺寸的一个很大的限制是,对于任何配置更改,布局都需要有相同的起始片段。因此,活动的小屏幕版本和大屏幕版本可以有不同的片段组合,但是相同屏幕大小的纵向布局和横向布局必须定义相同的片段。否则,当屏幕旋转时,Android 就会出现问题,例如,试图使用不存在的片段。

我们还需要解决片段和活动之间的通信。活动定义了它们持有的片段,因此它们通常知道哪些类实现了这些片段,并可以直接调用这些片段上的方法。然而,这些片段只知道它们是由某个活动托管的,而这个活动可能因情况而异。因此,典型的模式是使用接口进行片段到活动的通信:


  • 定义一个方法的接口,该片段将在它的活动(或者由该活动提供的一些其他对象)上调用这些方法。

  • 当创建片段时,活动通过片段上的一些 setter 方法提供该接口的实现。

  • 片段根据需要使用该接口实现。

当我们完成EU4You_6活动和它们相应的布局时,我们会看到所有这些。

在早期版本的EU4You项目中,我们只有一个活动,也叫做EU4You。在EU4You_6,我们有两项活动:


  • EU4You:在所有屏幕尺寸下显示CountriesFragment的手柄,以及在更大屏幕上显示DetailsFragment

  • DetailsActivity:在较小的屏幕上显示DetailsFragment

虽然我们可以让EU4You在更小的屏幕上启动浏览器活动,而不是让DetailsActivity托管一个只有WebViewDetailsFragment,但后一种方法对于更多基于片段的应用来说更现实。

优优

首先,我们来看看EU4You活动的各个部分。

布局

对于普通屏幕设备,我们只想显示CountriesFragment。这是通过具有适当的元素的res/layout/main.xml来实现的:


  class="com.commonsware.android.eu4you.CountriesFragment"
  android:id="@+id/countries"
  android:layout_
  android:layout_
/>

属性表明哪个 Java 类实现了这个片段。否则,这种布局是不起眼的。

请注意,片段不会像活动那样在清单文件中列出。

另一种布局

对于大屏幕设备,在横向模式下,我们希望将CountriesFragmentDetailsFragment并排显示。这样,用户可以点击一个国家并查看详细信息,而无需在活动之间来回切换。这也使我们能够更好地利用屏幕空间。

然而,有一个问题。如果我们想在我们的布局文件中预定义这两个片段,我们必须对使用相同的片段对横向和纵向模式——尽管我们不想在纵向模式下使用EU4You中的DetailsFragment(将列表垂直堆叠在WebView上看起来很奇怪,最好的情况也是如此)。作为一种变通方法,我们将对两个方向使用相同的布局文件,然后在 Java 代码中进行调整。另一个解决问题的方法是让布局文件只有CountriesFragment并使用FragmentManager和一个FragmentTransaction来添加到DetailsFragment中。不过,在这里,我们将使用其他技巧。

因此,在res/layout-large/(不是res/layout-large-land/)中,我们有这样的布局:


  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="horizontal"
  android:layout_
  android:layout_>
      android:id="@+id/countries"
    android:layout_weight="30"
    android:layout_
    android:layout_
  />
      android:id="@+id/details"
    android:layout_weight="70"
    android:layout_
    android:layout_
  />

注意,我们负责片段的定位,所以这里我们使用一个水平的LinearLayout来包围两个元素。

监听器接口

当用户在CountriesFragment中选择一个国家时,我们希望让我们的包含活动知道这一点。在这种情况下,碰巧唯一会主办CountriesFragment的活动是EU4You。然而,也许将来不会是这样。因此,我们应该通过一个监听器接口将从CountriesFragment到它的主机活动的通信抽象出来。

因此,EU4You_6项目有一个**Country**Listener接口:

`package com.commonsware.android.eu4you;

public interface Country Listener {
  void onCountrySelected(Country c);
}`

CountriesFragment持有由托管活动提供的**Country**Listener的实例:

public void **setCountryListener**(CountryListener listener) {
  this.listener=listener;
}

并且,当用户点击一个国家并触发onListItemClick()时,CountriesFragment调用接口上的on**Country**Selected()方法:

`@Override
public void onListItemClick(ListView l, View v, int position,
                           long id) {
  l.setItemChecked(position, true);

if (listener!=null) {
    listener.onCountrySelected(EU.get(position));
  }
}`

活动

这个EU4You活动时间不长,虽然有点棘手:

`package com.commonsware.android.eu4you;

import android.content.Intent;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.view.View;

public class EU4You extends FragmentActivity implements Country Listener {
  private boolean detailsInline=false;

@Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

CountriesFragment countries
      =(CountriesFragment)getSupportFragmentManager()
                           .findFragmentById(R.id.countries);

countries.setCountryListener(this);

Fragment f=getSupportFragmentManager().findFragmentById(R.id.details);

detailsInline=(f!=null &&
                    (getResources().getConfiguration().orientation==
                     Configuration.ORIENTATION_LANDSCAPE));

if (detailsInline) {
      countries.enablePersistentSelection();    }
    else if (f!=null) {
      f.getView().setVisibility(View.GONE);
    }
  }

@Override
  public void onCountrySelected(Country c) {
    String url=getString(c.url);

if (detailsInline) {
      ((DetailsFragment)getSupportFragmentManager()
                           .findFragmentById(R.id.details))
                           .loadUrl(url);

}
    else {
      Intent i=new Intent(this, DetailsActivity.class);

i.putExtra(DetailsActivity.EXTRA_URL, url);
      startActivity(i);
    }
  }
}`

我们在onCreate()的任务是连接我们的片段。片段本身是由我们对setContentView()的调用创建的,膨胀了我们的布局和其中定义的片段。然而,除此之外,EU4You还做了以下事情:


  • 找到CountriesFragment并将自己注册为**Country**Listener,因为EU4You实现了那个接口。

  • 如果存在,查找DetailsFragment。如果它存在,并且我们处于横向模式,我们告诉CountriesFragment启用持续高亮,以提醒用户右侧正在加载什么细节。如果它存在,并且我们处于纵向模式,我们实际上不想要DetailsFragment,但是需要它与布局模式一致,所以我们将片段的内容标记为GONE。如果DetailsFragment不存在,我们不必做任何特别的事情。

findFragmentById()这样的呼叫的FragmentManager是通过getFragmentManager()完成的。然而,ACL 定义了一个单独的getSupportFragmentManager(),以确保您正在使用 ACL 的FragmentManager实现,并在更广泛的 Android 版本上工作。

另外,由于EU4You实现了**Country**Listener接口,所以它必须实现on**Country**Selected()。在这里,EU4You指出我们是否应该路由到DetailsFragment的内嵌版本。如果应该,那么on**Country**Selected()**Country**传递给DetailsFragment,因此它加载那个国家的网页。否则,我们启动DetailsActivity,额外提供 URL。

详细活动

DetailsActivity将用于DetailsFragment未在EU4You活动中显示的情况,包括以下情况:


  • 当设备具有正常屏幕尺寸,因此在布局中没有DetailsFragment

  • 当设备具有纵向尺寸的大屏幕,因此EU4You隐藏了它自己的DetailsFragment


布局

布局中只有我们的元素,因为没有其他东西可以显示:


  class="com.commonsware.android.eu4you.DetailsFragment"
  android:id="@+id/details"
  android:layout_
  android:layout_
/>

活动

DetailsActivity简单地将来自Intent extra 的 URL 传递给DetailsFragment,告诉它要显示什么网页内容:

`package com.commonsware.android.eu4you;

import android.support.v4.app.FragmentActivity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;

public class DetailsActivity extends FragmentActivity {
  public static final String EXTRA_URL="com.commonsware.android.eu4you.EXTRA_URL";

@Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.details);

DetailsFragment details
      =(DetailsFragment)getSupportFragmentManager()
                           .findFragmentById(R.id.details);

details.loadUrl(getIntent().getStringExtra(EXTRA_URL));
  }
}`

片段和配置变化

在第 19 章中,我们回顾了活动如何处理配置变化,比如屏幕旋转。这如何转化为一个片段的世界?

和往常一样,有好消息,也有其他消息。

好消息是片段有可以覆盖的onSaveInstanceState()方法,行为很像它们的活动对应物。然后Bundle在很多地方都可以买到,比如onCreate()onActivityCreated(),尽管没有专门的onRestoreInstanceState()

另一个消息是,不仅片段缺少onRetainNonConfigurationInstance(),而且 ACL 的FragmentActivity不允许您扩展onRetainNonConfigurationInstance(),因为那是内部使用的。使用片段的直接 Android 实现的应用不会遇到这个问题。这个限制是很大的,开发人员社区仍然在共同寻找克服这个限制的方法。

为片段设计

片段的总体设计方法倾向于在片段中包含业务逻辑,活动充当片段间导航的编排层,以及片段所不具备的功能(例如,onRetainNonConfigurationInstance())。例如,Gmail 应用最初可能在每个活动(例如,文件夹的活动、对话列表的活动、单个对话的活动)中实现其大部分业务逻辑。如今,该应用可能是围绕将业务逻辑委托给片段而构建的,活动只是根据可用的屏幕大小来选择显示哪些片段。

自从 fragments 在 2011 年初首次亮相以来,这已经导致了现有应用的大量重组。例如,ListActivity可能已经从onListItemClick()发起了另一个活动。第一次重构会让片段的onListItemClick()发起一个活动。但是,该片段不知道用户请求的内容是否应该在另一个活动中显示——它可能会转到当前活动中的另一个片段。因此,该片段不应该盲目地调用startActivity(),而是应该调用其容器活动上的方法(或者,更有可能是由该活动实现的侦听器接口),告诉它点击事件,并让它决定正确的操作过程。


推荐阅读
author-avatar
小老虎颖儿
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有