《Android编程权威指南》-读书笔记(十一) 完善CriminalIntent

《Android编程权威指南》-读书笔记(十一) 完善CriminalIntent

 

在上篇文章中,我们跟随本书作者,使用了单个的Fragment做了最简单的插入操作。本篇文章将跟随作者进行更深入的完善各种功能。

 

本章目标:

  • 使用ListFragment显示列表
  • fragment之间传递数据
  • 使用ViewPager来实现划屏显示
  • 对话框

 

使用ListFragment显示列表

如果是挑战,或者没有UI我会给出草图之类的UI。或手画或Axure原型。每次例子本书都给出了最终UI,所以这步基本都可以省了。本章在例子CriminalIntent中使用ListFragment。以达到如下的效果:

 

 

资源相关

在这个UI中所有的数据都是根据动态数据显示的,因为没有用到任何图片资源或者字符串资源。所以没有修改的地方。(目前的资源默认的为按钮文字,界面上出现的文字等)

 

数据相关

现在需要显示一串数据,书中新增了一个可以容纳多个Crime对象ArrayList类。它定义了2个私有变量

private static CrimeLab sCrimeLab;

private Context mAppContext;

s开头的变量是开发的命名约定。它代表了变量sCrimeLab是一个静态变量。

这个类的代码现阶段如下:

http://git.oschina.net/canglin/CriminalIntent/commit/2f5580a992c804949a14a921dae7535a6097532f

这个类里面定义了个一个get方法用来传入一个Context。而在18行sCrimeLab直接调用了构造函数,构造函数的参数却是getApplicationContext(),这是因为Context可能是一个Activity,也可能是一另一个Context对象,如Service。在应用的整个生命周期里,无法保证只要CrimeLab需要用到Context,Context就一定会存在。所以才使用getApplicationContext()。至于12行public 的构造函数我写错了,我将会在下个版本的Git镜像中做修正。

 

将一些Crime对象保存到CrimeLab中去。增加一个Crime的ArrayList列表,并添加Getter()方法。

然后在增加一个查询操作在CrimeLab中为getCrime(UUID id)。我更倾向于使用getCrimeById()这种命名法。

完成后的CrimeLab最干净版不包含模拟数据代码如下:

http://git.oschina.net/canglin/CriminalIntent/commit/e4a9e6fac36f7eca76e063b406e194338f3fa778

 

逻辑相关

 

下图是CriminalIntent应用的整体规划设计

 

这个应用是在容器视图中显示列表。我们要创建一个ListFragment和一个Activity,还有与ListFragment相匹配的layout。

 

Fragment

创建CrimeListFragment类扩展自ListFragment。HoneyComb系统版本引入了ListFragment类,相应的,支持库也引入了该类。

import android.support.v4.app.ListFragment;

 

ListFragment是通过ListView将列表项展示给用户。而ListView通过adapter来申请视图对象。

Adapter负责:

创建必要的视图对象;

用模型层数据填充视图对象;

将准备好的视图对象返回给ListView。

随意例子中采用了setListAdapter(ListAdapter)来为CrimeListFragment管理内置ListView设置adapter。(详情参看后面的代码链接,现在就可以打开它对比观看)

 

FragmentActivity

由于每一个ActivityFragment都有相似的代码,于是作者创建了一个SingleFragmentActivity抽象类用来减少以后的代码输入。在书中的例子都是在一个FragmentContainer动态添加一个Fragment,所以唯一不同的代码就是在事物添加Fragment之前动态创建的代码。

修改CrimeActivity扩展自SingleFragmentActivity。

创建CrimeListActivity扩展自SingleFramentActivity。

因为这2个类唯一的区别

都只是在23行而已。采用抽象的方法后,每个扩展自SingleFragmentActivity的类都必须@Override掉createFragment()。

 

视图相关

需要在res/layout/list_item_crime.xml中的如下:

代码如下:

http://git.oschina.net/canglin/CriminalIntent/commit/05a1da49e017dbdcef2d795d6da07eca41a3a006

在本章中只关注下面的类和layout资源文件。其他的删除掉都可以,不会影响应用的正常运行。

对象

Crime                列表子元素的对象

CrimeLab                可以创建和获取一个Crime 列表。

 

Activity

CrimeListActivity            扩展自SingleFragmentActivity 创建CrimeListFragment 事物

SingleFragmentActivity        

 

Fragment

CrimeListFragment            根据list_item_crime.xml 生成相应的View

 

Res/layout

Activity_fragment.xml        定义了容易视图

List_item_crime.xml        定义了列表子元素的视图

 

由于默认的Activity 不是CrimeListActivity

<activity android:name=".CrimeListActivity">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

这样设置后默认的将会是CrimeListActivity。

 

在例子初期做的很随意,为后面几个目标做准备,我将源代码做了一些调整,调整后代码如下:

http://git.oschina.net/canglin/CriminalIntent/commit/05a1da49e017dbdcef2d795d6da07eca41a3a006

 

使用fragment argment

我们学习过在Activity中是调用startActivity(Intent)来启动另外一个activity。现在在Fragment中还是调用startActivity(Intent)来启动Activity。

在点击ListFragment将会显示详细的信息。详细信息界面应该是在第8章中完成。

界面设置好后,资源文件的字符串最初是这个样子。因为增加了几个字符串,但是还没有在strings.xml中添加,效果如下图所示:

资源文件填写完成后

代码如下:

http://git.oschina.net/canglin/CriminalIntent/commit/918aed3dd490b12f39f00701e4806cada3ad0b63

 

到现在为止界面界面基本成型,里面所有的事件,以及数据处理,还有业务逻辑我都把它精简到了最低的程度。中间很多地方在本书中是要求加入一些非UI代码的,我都没有加入。至于列表的数据那一块,也只是为了让界面逻辑能够完整。

 

给CrimeFragment填充数据

使用之前学到的方法用Intent传递数据,在CrimeFragment的onCreate()中从Intent读取数据。

首先定义一个id

public static final String EXTRA_CRIME_ID =

"com.example.lijing.criminalintent.crime_id";

…..

然后在onCreate()中

UUID crimeId = (UUID)getActivity().getIntent().getSerializableExtra(EXTRA_CRIME_ID);

mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);

数据在CrimeListFragment中startActivity()之

putExtra(CrimeFragment.EXTRA_CRIME_ID,c.getId());

 

完成后界面是这个样子:

代码发生如下变动:

http://git.oschina.net/canglin/CriminalIntent/commit/657c3d4164e08968dca123c3babec0ee80f97327

 

界面之间传递数据的改进

原先的代码如下

UUID crimeId = (UUID)getActivity().getIntent().getSerializableExtra(EXTRA_CRIME_ID);

在这段代码中crimeId是存储在CrimeActivity中的。现在将它由CrimeActivity的intent内的extra改为arguments bundle。每个fragment实例都可以附带一个Bundle对象。该bundle包含有key-value对,我们可以如同附加extra到Activity的intent中那样使用它们。一个key-value对即一个argument。

UUID crimeId = (UUID)getArguments().getSerializable(EXTRA_CRIME_ID);

 

并给fragment一个newInstance()来创建自己,并在这个静态方法里创建arguments。

 

总的来说就是由以前从Activity中获取参数,改成从自己的存储区里获取参数。而自己的存储区里的参数是在创建该fragment是写入的。

 

这是代码的改动:

http://git.oschina.net/canglin/CriminalIntent/commit/95d7da1d76c2ac316cdb1f1af823df3cb745e133

 

使用ViewPager来实现划屏显示

为了实现这个效果,我们需要创建一个ViewPager的activity,命名为CrimePagerActivity来取代CrimeActivity。本章采用了以代码的方式创建视图它包含以下步骤:

  • 为ViewPager创建资源ID;
  • 创建ViewPager实例并赋值给mViewPager;
  • 赋值资源ID给ViewPager,并对其进行配置;
  • 设置ViewPager为activity的内容视图。

 

创建独立资源ID(res/values/ids.xml)

定义独立资源ID与定义字符串资源ID并没有什么不同:在res/values目录下的XML文件中创建一个项目元素。创建一个名为res/values/ids.xml的Android XML 资源文件。

 

以代码的方式创建内容视图(CrimePagerActivity.java)

mViewPager = new ViewPager(this);

mViewPager.setId(R.id.viewPager);

setContentView(mViewPager);

然后我们使用FragmentStatePagerAdaper为我们的代理,负责管理与ViewPager的对话并协同工作。

代码如下:

http://git.oschina.net/canglin/CriminalIntent/commits/master

到这里在详细信息界面就实现了拖动。

现在程序有一个Bug就是,当我点击一个详细信息的时候,详细信息界面中显示的永远是第一条。现在我们通过设置setCurrnetItem(index)是当前详细页面的信息是选中的选项。

 

有关ViewPager.OnPageChangeListener

在本书的例子中,当页面发生改变的时候将标题设置给CrimePagerActivity。在我的代码中我貌似将显示标题的位置拿掉了。

https://developer.android.com/reference/android/support/v4/view/ViewPager.OnPageChangeListener.html#onPageScrollStateChanged(int)

这个是官方的api地址

这个方法必须重写3个抽象的方法,如果现在不知道写什么可以将3个方法复制进去就可以了。

mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {

public void onPageScrollStateChanged(int state) {}

public void onPageScrolled (int position, float positionOffset, int positionOffsetPixels) {}

public void onPageSelected (int position) {

Crime crime = mCrimes.get(position);

if (crime.getTitle() != null) {

setTitle(crime.getTitle());

}

}

});

就像这个样子,但是这段代码在现在的程序里,是不会有界面改变的。所以在这里特别强调一下。

完成后代码如下:

http://git.oschina.net/canglin/CriminalIntent/commits/master

 

对话框

在我们CrimeFragment对应的详细信息界面里有一个时间设置按钮。现在我们就按照书中的要求完善这个按钮的对话框。

作者采用将AlertDialog封装在DialogFragment的方法来显示对话框,因为有如下优点:

  • 使用FragmentManager管理对话框,可以使用更多配置选项来显示对话框;
  • 发生旋转时封装在fragment中的AlertDialog不会消失

 

在屏幕上显示DialogFragment时,托管activity的FragmentManager会调用onCreateDialog()。在onCreateDialog里我们需要返回一个AlertDialog.Builder。

 

在显示对话框的时候要注意。要将DialogFragment添加给FragmentManager管理并放置到屏幕上,可以调用fragment的show方法。

Public void show (FragmentManager manager, String tag)

Public void show(FragmentTransactiong transaction, String tag)

String参数是用来队列中的DialogFragment。在FragmentManager和FragmentTransaction的选择上,书中选择了FragmentManager因为传入这个参数,事物可以自动创建提交。

最简单的界面效果如下:

现阶段代码如下:

http://git.oschina.net/canglin/CriminalIntent/commit/ddf4b0365937ea287d0f64f7b437c1822f8745b0

 

对话框之间的数据交互,在本章已经简单介绍过了。书中在这里是继续完成了的。本着达到目标最简单的代码原则,这些代码先不提交到Git。

 

在每个小段落最后都会有当前阶段的Git代码地址。

 

小结:

到现在为止,基本上Android的一些最基本的界面,最基本的业务逻辑,数据逻辑都已经完成了。每个阶段的代码都是尽量的删减到最少,为了方便在Git中查看修改的过程,避免不必要的误导。

posted @ 2015-03-30 15:46  苍林  阅读(1717)  评论(0编辑  收藏  举报