Xamarin.Android之Fragment Walkthrough

利用Fragment设计能够兼容不同屏幕的应用

这里我们先围观下最后的成果图,给读者打打气:

普通手机上显示的结果:

在平板上显示的结果:

笔者要郑重声明下,虽然看似是两种不同的显示效果,但是同一个应用,而下面笔者将逐步教会大家如何利用Fragment制作出能够兼容不同屏幕的应用。

准备工作

创建一个项目是必不可少的,并且Android SDK的版本要在3.0以上,建议是4.0因为笔者设定的就是4.0,新建完成之后项目会自动帮我们创建好MainActivity,当然靠这一个还不足够,我们还要新建一个Activity,并命名为DetailsActivity,另外还有两个Fragment分为命名为DetailsFragmentTitlesFragment,最后的目录结构应该如下图所示:

创建兼容视图

现在我们先把呈现部分的功能完成,我们打开Resources\Layout下的Main.axml将下面的xml代码写入:

 1 <?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:orientation="horizontal"
4 android:layout_width="fill_parent"
5 android:layout_height="fill_parent">
6 <fragment
7 class="fragmentwalkthrough.TitlesFragment"
8 android:id="@+id/titles_fragment"
9 android:layout_width="fill_parent"
10 android:layout_height="fill_parent" />
11 </LinearLayout>

这里我们就利用了fragment作为占位符,从而显示TitlesFragment,笔者还要注意class的完整路径,要按照自己实际项目的名称来,一般都是解决方案的名字的小写加上点然后就是对应的类名了。

光有这个视图只能应付小屏幕的显示,我们还需要为大屏幕设计一个视图。但是我们不能在layout下继续新建,那样我们就要用代码负责控制了,其实Android本身就已经提供了这些功能,我们只要在Resources下新建一个文件夹并且命名为layout-large,然后在该文件夹下新建一个Main.axml,这里的视图文件命名必须要和layout下的一致,然后将下面的内容写入其中:

 1 <?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:orientation="horizontal"
4 android:layout_width="fill_parent"
5 android:layout_height="fill_parent">
6 <fragment
7 class="fragmentwalkthrough.TitlesFragment"
8 android:id="@+id/titles_fragment"
9 android:layout_weight="1"
10 android:layout_width="0px"
11 android:layout_height="match_parent" />
12 <FrameLayout
13 android:id="@+id/details"
14 android:layout_weight="3"
15 android:layout_width="0px"
16 android:layout_height="match_parent" />
17 </LinearLayout>

这里多了一个FrameLayout这个就是作为内容的容器,最后我们可以看到在我们选择不同的项的时候,都会在这个占位符中切换碎片(Fragment),这里提示下我们还要把MainActivity.cs中的自动生成的代码删除,最后只要剩下以下的内容即可:

1         protected override void OnCreate(Bundle bundle)
2 {
3 base.OnCreate(bundle);
4 SetContentView(Resource.Layout.Main);
5 }

完成了上面的内容,我们下面就开始从下而上来开始。

完善DetailsFragment

唯一需要学习的就是OnCreateView方法,这个方法就是来用指定碎片的视图的,最后显示的是返回的视图,具体的代码如下所示:

 1     public class DetailsFragment : Fragment
2 {
3 public static DetailsFragment NewInstance(int playId)
4 {
5 var detailsFrag = new DetailsFragment
6 {
7 Arguments = new Bundle()
8 };
9 detailsFrag.Arguments.PutInt("current_play_id", playId);
10 return detailsFrag;
11 }
12
13 public int ShowPlayId
14 {
15 get
16 {
17 return Arguments.GetInt("current_play_id", 0);
18 }
19 }
20
21 public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
22 {
23 if (container == null)
24 {
25 return null;
26 }
27 var scroller = new ScrollView(Activity);
28 var text = new TextView(Activity);
29 text.SetPadding(4, 4, 4, 4);
30 text.TextSize = 24;
31 text.Text = "you select " + ShowPlayId;
32 scroller.AddView(text);
33 return scroller;
34 }
35 }

我们仅仅只是对选择的项的id保存了,提供还提供了一个快捷方法NewInstance用来实例化这个碎片,最后的内容仅仅只是通过TextView呈现的,笔者后面也可以设计一个视图,然后利用inflater参数实例化并返回。

完善DetailsActivity

这个活动纯粹只是为了兼容小屏幕的,因为它只是一个躯壳,负责将发送给它的参数在转发给DetailsFragment,并显示DetailFragment。具体的代码如下所示:

 1     [Activity(Label = "DetailsActivity")]
2 public class DetailsActivity : Activity
3 {
4 protected override void OnCreate(Bundle bundle)
5 {
6 base.OnCreate(bundle);
7 var index = Intent.Extras.GetInt("current_play_id", 0);
8 var details = DetailsFragment.NewInstance(index);
9 var ft = FragmentManager.BeginTransaction();
10 ft.Add(Android.Resource.Id.Content, details);
11 ft.Commit();
12 }
13 }

我们注意到了FragmentManger这个类,它对于我们今后使用碎片都是非常重要的,只要在活动里面切换碎片,删除碎片等都要通过它。这一过程还必须要使用BeginTransaction先开启事务,完成操作后还要通过Commit提交,否则是没有效果的。

完善TitlesFragment

这里我们看到了列表显示的数据,而这些数据都是定义在Strings.xml中的,所以我们先要定义这些资源,代码如下所示:

 1 <?xml version="1.0" encoding="utf-8"?>
2 <resources>
3 <string name="Hello">Hello World, Click Me!</string>
4 <string name="ApplicationName">FragmentWalkthrough</string>
5 <string-array name="TitleList">
6 <item>First</item>
7 <item>Second</item>
8 <item>Third</item>
9 <item>Fourth</item>
10 <item>Fifth</item>
11 <item>Sixth</item>
12 </string-array>
13 </resources>

完成上面的操作后,我们就要重点学习TitlesFragment中的功能,首先我们删除里面默认重写的方法,然后将继承的类改成ListFragment,并重写OnActivityCreated,因为我们继承了这个类,就跟ListActivity一样,所以不需要在设置界面。

为了能够兼容不同的屏幕,所以我们需要一个bool类型的变量去保存当前的屏幕是属于大还是小,从而决定相关的功能,并且还要有一个int类型的变量保存当前所选的数据的id,然后我们就可以完善OnActivityCreated方法了:

 1         protected int _currentPlayId;
2 protected bool _isDualPanel;
3
4 public override void OnActivityCreated(Bundle savedInstanceState)
5 {
6 base.OnActivityCreated(savedInstanceState);
7 //从Resources将资源取出
8 string[] strarray = Resources.GetStringArray(Resource.Array.TitleList);
9
10 //实例化一个适配器并将适配器赋给ListAdapter
11 var adapter = new ArrayAdapter<string>(Activity, Android.Resource.Layout.SimpleListItemChecked, strarray);
12 ListAdapter = adapter;
13
14 //判断是否存在上一次会话的数据
15 if (savedInstanceState != null)
16 {
17 _currentPlayId = savedInstanceState.GetInt("current_play_id", 0);
18 }
19
20 //获取用于碎片的占位符
21 var detailsFrame = Activity.FindViewById<View>(Resource.Id.details);
22
23 //根据该占位符是否存在以及是否可见,从而决定是否为大屏幕
24 _isDualPanel = detailsFrame != null && detailsFrame.Visibility == ViewStates.Visible;
25
26 //当前屏幕为大屏幕时操作
27 if (_isDualPanel)
28 {
29 ListView.ChoiceMode = ChoiceMode.Single;
30 ShowDetails(_currentPlayId);
31 }
32 }

这里只是初始化了列表并判断了当前属于那种情况,下面我们就要介绍重要的ShowDetails方法,该方法将负责用户点击某项后采用那种方式呈现,下面是该代码:

 1         public void ShowDetails(int playid)
2 {
3 _currentPlayId = playid;
4 //判断当前屏幕显示的方案
5 if (_isDualPanel)
6 {
7 //为大屏幕时显示的方案
8
9 ListView.SetItemChecked(playid, true);
10 //通过碎片管理器查找对应的碎片 如果是第一次显示则返回的是null
11 var details = FragmentManager.FindFragmentById(Resource.Id.details) as DetailsFragment;
12
13 //判断是否存在该碎片的实例化对象或该对象显示的内容是否跟当前选择的内容一致
14 if (details == null || details.ShowPlayId != playid)
15 {
16 //实例化碎片
17 details = DetailsFragment.NewInstance(playid);
18 var ft = FragmentManager.BeginTransaction();
19 //将FrameLayout占位符替换成details碎片
20 ft.Replace(Resource.Id.details, details);
21 ft.Commit();
22 }
23 }
24 else
25 {
26 //为小屏幕时显示的方案
27 var intent = new Intent();
28 intent.SetClass(Activity, typeof(DetailsActivity));
29 intent.PutExtra("current_play_id", playid);
30 StartActivity(intent);
31 }
32 }

这面的代码我们通过其中的注释就可以清楚的明白了,当然我们还要重写ListFragment的事件,代码如下所示:

1         //监听选择事件,在每次选择后重新显示详细内容
2 public override void OnListItemClick(ListView l, View v, int position, long id)
3 {
4 ShowDetails(position);
5 }

至此我们就实现了能够在不同屏幕下显示不同界面的方式,这在很大的程度上可以复用代码,而不需要非要单独去定制专门的应用。

Xamarin.Android开发实践(十六)的更多相关文章

  1. Xamarin.Android开发实践(六)

    Xamarin.Android通知详解 一.发送通知的机制 在日常的app应用中经常需要使用通知,因为服务.广播后台活动如果有事件需要通知用户,则需要通过通知栏显示,而在Xamarin.Android ...

  2. Xamarin.Android开发实践(五)

    原文:Xamarin.Android开发实践(五) 一.服务的生命周期 服务与活动一样,在它的整个生命周期中存在着一些事件,下图可以很好解释整个过程以及涉及到的方法: 在真实的使用中,Service来 ...

  3. Xamarin.Android开发实践(四)

    原文:Xamarin.Android开发实践(四) Xamarin.Android下获取与解析JSON 一.新建项目 1.新建一个Android项目,并命名为为NetJsonList 2.右击引用,选 ...

  4. Xamarin.Android开发实践(三)

    原文:Xamarin.Android开发实践(三) 一.前言 用过Android手机的人一定会发现一种现象,当你把一个应用置于后台后,一段时间之后在打开就会发现应用重新打开了,但是之前的相关的数据却没 ...

  5. Xamarin.Android开发实践(二)

    原文:Xamarin.Android开发实践(二) 一.准备 开始学习本教程前必须先完成该教程http://www.cnblogs.com/yaozhenfa/p/xamarin_android_qu ...

  6. Xamarin.Android开发实践(一)

    原文:Xamarin.Android开发实践(一) 一.准备工作 1.创建一个空的解决方案,并命名为Phoneword 2.右击解决方案 新建->新建项目 并命名为Phoneword_Droid ...

  7. Xamarin.Android开发实践(十五)

    Xamarin.Android学习之应用程序首选项 一.前言 任何App都会存在设置界面,如果开发者利用普通控件并绑定监听事件保存设置,这 一过程会非常的枯燥,而且耗时.我们可以看到Android系统 ...

  8. Xamarin.Android开发实践(十四)

    Xamarin.Android之ListView和Adapter 一.前言 如今不管任何应用都能够看到列表的存在,而本章我们将学习如何使用Xamarin去实现它,以及如何使用适配器和自定义适配器(本文 ...

  9. Xamarin.Android开发实践(十八)

    Xamarin.Android之SlidingMenu 一.前言 有位网友在评论中希望能够出个在Xamarin.Android下实现SlidingMenu效果的随笔,刚好昨天在观看官网示例项目的时候也 ...

随机推荐

  1. MyBatis查询传一个参数时报错:There is no getter for property named 'sleevetype' in 'class java.lang.Integer

    用MyBatis进行查询,传入参数只有一个时(非Map)如int,报错 There is no getter for property named 'sleevetype' in 'class jav ...

  2. 取消chrome浏览器下input和textarea的默认样式

    最近一个细节引起了我的注意,chrome浏览器下的input和textarea在聚焦的时候都有一个黄色的边框,而且textarea还可以任意拖动放大,这是不能容忍的,影响美观不说,有时候拖动texta ...

  3. CSS创建一个遮罩层

    .layer{ width: 100%; position: absolute; left:; right:; top:; bottom:; -moz-opacity:; filter: alpha( ...

  4. TCP,IP,HTTP,SOCKET区别和联系

    物理层-- 数据链路层-- 传输层--                       TCP协议 会话层-- 我 们在传输数据时,可以只使用(传输层)TCP/IP协议,但是那样的话,如 果没有应用层,便 ...

  5. C++ 四种强制类型转换

    来自csdn:http://blog.csdn.net/hgl868/article/details/46619399 C风格的强制类转换(Type Cast)很简单,不管什么类型的转换统统是: TY ...

  6. 记录一次centos6.4版本的VSFTP本地用户登陆的配置

    其实vsftp是一个非常常用而且简单的服务,但是假如服务不是你配置的前者没有留下参考档案,的确是件头疼的事儿,特此记录下. 首先是vsftp的安装当然安装有源码的编译和yum等 这里我选择rpm包的y ...

  7. jQuery.snowflake雪花飘落插件

    一.前言 前言:最近圣诞节来临,需要在页面上应用一个雪花飘落的效果,做之前产品经理给了我网络上的一个demo,地址是http://demo.lanrenzhijia.com/demo/1225/sd/ ...

  8. JSP 内置对象(request response session application out pageContext)

    request对象  javax.servlet.http.HttpServletRequest接口的实例 request.setCharacterEncoding("utf-8" ...

  9. Android Activity模拟dialog

    Android项目中很多地方,都会弹出一个弹出框.类似于自己定义的alertDialog,比如微信的退出提示,但由于Dialog的限制,可能不能很完美的实现你的想要的功能,所有研究发现他们这种实现其实 ...

  10. [Effective JavaScript 笔记]第41条:将原型视为实现细节

    对象原型链 一个对象给其使用者提供了轻量.简单.强大的操作集.使用者与一个对象最基本的交互是获取其属性值和调用其方法.这些操作不是特别在意属性存储在原型继承结构的哪个位置.随着时间推移,实现对象时可能 ...