[译]:Xamarin.Android平台功能——片段演练

标签: Xamarin.Android, 官方教程, 中文翻译

博客分类: 官方教程

返回索引目录
原文链接:

译文链接:Xamarin.Android平台功能——片段演练
练习示例代码:FragmentSample

Fragment演练

Android 3.0中引入了Fragment。Fragment是自包含的模块化组件,它是用于辅助处理运行在不同尺寸屏幕上应用编写的复杂性。本文将介绍如何使用片段来开发Xamarin.Android应用,以及如何让Android 3.0之前的设备支持片段。

概览

文中形状参数(form factors):指电子产品的尺寸、形状、规格

在本文中,我们将演示如何创建一个应用来展示一个莎士比亚戏剧列表以及每个选中剧本的引文。我们的应用将利用片段来让我们可以在一个位置定义我们的UI组件,但是之后可以在不同的形状参数中使用它们。例如,下面的截图展示了应用运行在一个10英寸平板电脑和运行在一个手机上的情况:

本文演示将包含以下主题:

  • 创建片段(Creating Fragments) —— 展示如何创建片段来显示一个莎士比亚戏剧列表,并创建另一个片段来显示每个选中剧本的引文。
  • 支持不同的屏幕尺寸(Supporting different screen sizes) —— 显示如何规划应用来对更大的屏幕尺寸加以利用。
  • 使用Android支持包(Using the Android Support Package) —— 实现Android支持包,然后对应用中的Activity进行一些小改动,以此允许应用在旧版本的Android上运行。

环境要求

本演示内容需要Xamarin.Android 4.0或更高版本。它还需要安装Android支持包,如Fragment文档中所述。

简介

在此示例中,我们将在演练中构建内容,Activity不包含加载列表、响应用户选择或显示所选戏剧引文的逻辑。此逻辑会存在于各个片段。通过将此逻辑置于片段本身中,我们可以拆分应用的工作流程,以便于在无需为每个Activity编写不同逻辑的情况下支持具有一个Activity的大屏幕或者具有多个Activity的小屏幕。在平板电脑上,两个片段将在一个Activity中。在手机上,片段将由多个Activity托管。

此应用包括以下部分:

  • MainActivity —— 显示一个或两个片段,这取决于屏幕的尺寸。这是用来启动的Activity。
  • TitlesFragment —— 用于显示用户可以选择的莎士比亚戏剧列表。
  • DetailsFragment —— 显示所选戏剧的引文。
  • DetailsActivity —— 用于托管和显示DetailsFragment。此Activity由具有小屏幕的设备(如手机)使用。

第一部分 - 操作演练

下面开始演练。第一步是创建一个新的Xamarin.Android的Android项目。

本节中各个小节需交叉查看。

1. 创建项目

创建一个名为FragmentSample的Xamarin.Android项目。项目的最低Android版本应为Android 3.1,如下图所示:

2. 创建MainActivity

下一步,我们需要创建MainActivity类。它是应用的启动Activity。当Xamarin.Android项目创建完成后,启动Activity名为Activity1。分别将文件重命名为MainActivity.cs,类重命名为MainActivity。 —— 新版的项目创建中,已经默认为MainActivity。

根据屏幕的尺寸大小,此Activity将托管一个或两个片段。MainActivity将通过加载与设备适配的布局文件来完成此操作。

[Activity(Label = "FragmentSample", MainLauncher = true, Icon = "@drawable/icon")]
public class MainActivity : Activity
{
    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);

        SetContentView(Resource.Layout.activity_main);
    }
}

注意:Fragment子类必须含有一个公共的默认无参数构造函数。

3. 创建布局文件

两种不同的屏幕尺寸需要两个不同的布局文件。因此我们需要创建一个新的文件夹(Resources/Layout-Large),同时创建一个名为activity_main.axml的新布局。我们还需要将默认布局文件重命名为Resources/Layout/activity_main.axml。修改完成之后,布局文件夹应当如下图所示:

所有的设备将会加载使用Resources/Layout中的布局文件。下面是一个非常简单的布局,仅仅用于显示一个TitlesFragment:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
<fragment class="FragmentSample.TitlesFragment"
        android:id="@+id/titles_fragment"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />
</LinearLayout>

对于大屏幕设备,Android将会加载Resources/Layout-Large中的布局文件。平板电脑的布局内容如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
<fragment class="FragmentSample.TitlesFragment"
        android:id="@+id/titles_fragment"
        android:layout_weight="1"
        android:layout_width="0px"
        android:layout_height="match_parent" />
<FrameLayout android:id="@+id/details"
            android:layout_weight="1"
            android:layout_width="0px"
            android:layout_height="match_parent" />
</LinearLayout>

比较大的屏幕布局文件略有不同。不仅仅是TitlesFragment显示在布局文件中,同时,片段(fragment)之后还添加了一个FrameLayout。在较大屏幕中,当用户选择戏剧时,会以编程方式向MainActivity中添加DetailsFragment。稍后,我们将更详细地说明这是如何完成的。

Android 3.2中引入了一种新的方式来指定屏幕布局。那些新的限定符指定了你的布局所需的空间大小,而不是屏幕大小。如果此应用仅用于Android 3.2及更高版上,我们可以为布局文件activity_main.axml创建一个Resources/Layout-sw600dp文件夹(而不是Resources/Layout-Large文件夹)。该资源文件将会在所有屏幕宽度大于600(与像素无关)的设备上被加载。然而,由于此应用设置目标为Android 3.1及更高版本,故它使用旧的资源限定符。

4. 创建TitlesFragment

TitlesFragment将显示各类戏剧的标题,所以我们需要向项目中添加一个新的片段,命名为TitlesFragment:

在添加TitlesFragment之后,我们需要修改类 —— 让其继承自Android.App.ListFragment。ListFragment是一种专用的片段类型,它含有列表的功能。TitlesFragment中也将重写OnActivityCreated(另一个片段的生命周期方法),并提供一个适配器,ListFragment会使用此适配器来填充列表:

关于Shakespeare类,此处不做详述,其内容为示例数据源

#region 临时存储变量 —— 用于方法见内容传递
private int _currentPlayId;
private bool _isDualPane;
#endregion

public override void OnActivityCreated(Bundle savedInstanceState)
{
    base.OnActivityCreated(savedInstanceState);

    var adapter = new ArrayAdapter<String>(Activity, Android.Resource.Layout.SimpleListItemChecked, Shakespeare.Titles);
    ListAdapter = adapter;
    if (savedInstanceState != null)
    {
        _currentPlayId = savedInstanceState.GetInt("current_play_id", 0);
    }
    var detailsFrame = Activity.FindViewById<View>(Resource.Id.details);
    _isDualPane = detailsFrame != null && detailsFrame.Visibility == ViewStates.Visible;
    if (_isDualPane)
    {
        ListView.ChoiceMode = ChoiceMode.Single;
        ShowDetails(_currentPlayId);
    }
}

如前所述,我们的应用有两个针对MainActivity的布局。OnActivityCreated方法中的代码检测查找存在的FrameLayout,并决定加载哪个布局文件。如果布局中存在FrameLayout,则isDualPane标志设为true。isDualPane标志会在Activity中的其他地方使用,确切的说,它会被ShowDetails方法使用。ShowDetails方法将在后面详细介绍。

TitlesFragment是一个列表,并且它需要响应用户在列表中的选择。为了实现这个功能,TitlesFragment将重写OnListItemClick方法。在OnListItemClick方法中,将会以编程方式创建一个新的DetailsFragment,并将其显示到FrameLayout中。TitlesFragment中的相关代码如下:

public override void OnListItemClick(ListView l, View v, int position, long id)
{
    ShowDetails(position);
}

private void ShowDetails(int playId)
{
    _currentPlayId = playId;
    if (_isDualPane)
    {
        ListView.SetItemChecked(playId, true);

        var details = FragmentManager.FindFragmentById(Resource.Id.details) as DetailsFragment;
        if (details == null || details.ShownPlayId != playId)
        {
            details = DetailsFragment.NewInstance(playId);
            var ft = FragmentManager.BeginTransaction();
            ft.Replace(Resource.Id.details, details);
            ft.SetTransition(FragmentTransit.FragmentFade);//此处原为有误
            ft.Commit();
        }
    }
    else
    {
        var intent = new Intent();
        intent.SetClass(Activity, typeof(DetailsActivity));
        intent.PutExtra("current_play_id", playId);
        StartActivity(intent);
    }
}

此代码通过设备来决定如何安排和显示选中戏剧的引文。如果是平板电脑,isDualPane表示将设置为true,从而因此引文将显示在TitlesFragment后面。如果所选戏剧id尚未显示,则创建一个新的DetailsFragment,然后将其加载到Activity的FrameLayout中。对于其他不具有大屏幕显示的设备(如,手机),isDualPane将设置为false,因此将会启动一个新的DetailsActivity。

5. 创建DetailsActivity

DetailsActivity为小型设备显示DetailsFragment。由此,我们首先将向项目中添加一个名为DetailsActivity的新Activity。DetailsActivity是一个非常简单的Activity。它将创建并托管一个新的DetailsFragment来传送戏剧的id:

[Activity(Label = "DetailsActivity")]
public class DetailsActivity : Activity
{
    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);
        var index = Intent.Extras.GetInt("current_play_id", 0);

        // 工厂方法创建 片段 实例
        var details = DetailsFragment.NewInstance(index);
        var fragmentTransaction = FragmentManager.BeginTransaction();
        fragmentTransaction.Add(Android.Resource.Id.Content, details);
        fragmentTransaction.Commit();
    }
}

请注意,没有针对DetailsActivity加载的布局文件。取而代之的是DetailsFragment会被加载到Activity的根视图中。此根视图有个特殊的ID —— Android.Resource.Id.Content。一个新的DetailsFragment将会被创建并添加到由Activity的FragmentManager所创建的FragmentTransaction的根视图中。

6. 创建DetailsFragment

现在,让我们向应用中添加另一个片段DetailsFragment。此片段将显示所选戏剧的引文。下列代码展示了完整的DetailsFragment:

public class DetailsFragment : Fragment
{
    public static DetailsFragment NewInstance(int playId)
    {
        var detailsFrag = new DetailsFragment { Arguments = new Bundle() };
        detailsFrag.Arguments.PutInt("current_play_id", playId);
        return detailsFrag;
    }

    public int ShownPlayId
    {
        get { return Arguments.GetInt("current_play_id", 0); }
    }

    public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        if (container == null)
        {
                            return null;
        }

        // 后台代码建立前台视图内容
        var scroller = new ScrollView(Activity);
        var text = new TextView(Activity);
        var padding = Convert.ToInt32(TypedValue.ApplyDimension(ComplexUnitType.Dip, 4, Activity.Resources.DisplayMetrics));
        text.SetPadding(padding, padding, padding, padding);
        text.TextSize = 24;
        text.Text = Shakespeare.Dialogue[ShownPlayId];
        scroller.AddView(text);

        return scroller;
    }
}

为了让DetailsFragment可以正常工作,它需要有TitlesFragment中选中戏剧的索引。有很多方法可以将此值提供给DetailsFragment;在本示例中,戏剧Id被置于Bundle中,并且此Bundle存储在一个DetailsFragment实例的Anguments属性中。为了方便起见,这里提供了ShownPlayId属性 —— 它将被DetailsFragment实例用于从Bundle中获取检索值。

在OnCreateView调用期间,片段将绘制用户界面,并返回一个Android.Views.View对象。在大多数情况下,这是用已有布局文件填充一个View。在上述示例中,片段将以编程方式创建将要用于显示的视图。

恭喜,你现在已经创建了一个应用,它使用片段来简化形状参数的开发。

在下面一节中,我们将扩展此应用,以此让它可以在Android 3.0之前的设备上工作。

第二部分 - 使用Android支持包处理前面示例

Android支持包是由将一些新的API(如片段fragment)转化为旧版Android库组成。因此,通过添加Android支持包,我们可以在Android 2.3设备上运行我们的应用,如下屏幕所示:

添加支持包

要将支持包加入到Xamarin.Android应用中,需要将Android Support Library v4组件添加到Xamarin.Android项目中,正如下面截图所示:

在完成添加组件后,将目标框架修改为Android 2.2或更高版本:

另外,确保最低目标Android版本与API级别相同:

将MainActivity修改为派生自FragmentActivity

任何使用片段的Activity必须派生自Xamarin.Support.V4.App.FragmentActivity。无论是什么Android版本,此类(FragmentActivity)是支持包的必要部分,因为它允许片段由activity托管。MainActivity需要做个小改变 —— 它现在需要继承自Android.Support.V4.App.FragmentActivity:

[Activity(Label = "FragmentSample", MainLauncher = true, Icon = "@drawable/icon")]
public class MainActivity : Android.Support.V4.App.FragmentActivity
{
    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);

        SetContentView(Resource.Layout.activity_main);
    }
}

将DetailsActivity修改为派生自FragmentActivity

DetailsActivity也需要将Activity修改为FragmentActivity。由于FragmentManager与Android的Honeycomb之前的版本不兼容,Android支持包中引入了一个封装的类SupportFragmentManager,以此提供向后兼容性。每个FragmentActivity都有一个SupportFragmentManager属性,同时修改DetailsActivity,将FragmentManager替换为SupportFragmentManager:

[Activity(Label = "DetailsActivity")]
public class DetailsActivity : Android.Support.V4.App.FragmentActivity
{
    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);
        var index = Intent.Extras.GetInt("current_play_id", 0);

        // 工厂方法创建 片段 实例
        var details = DetailsFragment.NewInstance(index);
        var fragmentTransaction = FragmentManager.BeginTransaction();
        fragmentTransaction.Add(Android.Resource.Id.Content, details);
        fragmentTransaction.Commit();
    }
}

在完成这些更改后,我们现在就有一个可以运行在Android 1.6及更高版本的应用了,同时可以使用片段来调整我们的UI,以此适应我们的目标设备的尺寸。

总结

本文介绍了如何在Xamarin.Android应用中使用片段。它展示了如何修改一个应用,以此让它可以使用如平板电脑的大型屏幕尺寸,同时在小的形状参数中保留功能。FragmentManager用于展示如何查找片段或向Activity中添加片段。最后,利用 Android支持包修改应用,以此使其在向后兼容的情况下,可以在较旧Android版本的设备上运行。


译:奇葩史

没有评论