第 12 章 对话框

请参考教材,全面理解和完成本章节内容... ...

复制工程ch11,将工程目录改名为ch12。

对话框既能引起用户的注意也可接收用户的输入。在提示重要信息或提供用户选项方面,它都非常有用。本章,我们将CriminalIntent(陋习手记)应用添加一个对话框,以供用户改变crime记录日期。点击CrimeFragment上的日期按钮,即可弹出对话框,如图12-1所示。

第 12 章 对话框_第1张图片

12-1 可供选择crime日期的对话框

图12-1所示的AlertDialog视图封装在DialogFragment(Fragment的子类)实例中。不使用DialogFragment,也可显示AlertDialog视图,但Android开发原则不推荐这种做法。使用FragmentManager管理对话框,可使用更多配置选项来显示对话框。

就CriminalIntent应用来说,我们首先会创建一个名为DatePickeFragment的DialogFragment子类。然后,在DatePickeFragment中,创建并配置一个显示DatePicker组件的AlertDialog实例。DatePickeFragment也将交给CrimePagerActivity来托管。

图12-2展示了以上各对象间的关系。

第 12 章 对话框_第2张图片

图12-2 两个由CrimePagerActivity托管的fragment对象图解

总结下来,要实现对话框显示,首先应完成以下任务:

  • 创建DatePickeFragment类;
  • 创建AlertDialog;
  • 通过FragmentManager在屏幕上显示对话框。

在本章的后面,我们将配置使用DatePicker,并实现CrimeFragment和DatePickerFragment之间必要数据的传递。

继续学习之前,请参照代码清单12-1添加所需的字符串资源。

代码清单12-1 为对话框标题添加字符串资源(values/strings.xml)

第 12 章 对话框_第3张图片

12.1 创建 DialogFragment

创建一个名为DatePickerFragment的新类,并设置其DialogFragment超类为支持库中的android.support.v4.app.DialogFragment类。

DialogFragment类有如下方法:

public Dialog onCreateDialog(Bundle savedInstanceState)

在屏幕上显示DialogFragment时,托管activity的FragmentManager会调用onCreateDialog方法。

在DatePickerFragment.java中,添加onCreateDialog( )方法的实现代码,创建一个带标题栏和OK按钮的AlertDialog,如代码清单12-2所示。(DatePicker组件稍后会添加)

注意AlertDialog需要import android.support.v7.app.AlertDialog;

代码清单12-2 创建DialogFragment(DatePickerFragment.java)

第 12 章 对话框_第4张图片

如代码清单12-2所示,使用AlertDialog.Builder类,以流接口(fluent interface)的方式创建了一个AlertDialog实例。

12.1.1 显示DialogFragment

和其他fragment一样,DialogFragment实例也是由托管activity的FragmentManager管理的。

CrimeFragment中,为DatePickerFragment添加一个tag常量。然后,在onCreateView( )方法中,删除禁用日期按钮的代码。为实现用户点击日期按钮展现DatePickerFragment界面,实现mDateButton按钮的OnClickListener监听器接口,如代码清单12-3所示。

代码清单12-3 显示DialogFragment(CrimeFragment.java)

第 12 章 对话框_第5张图片

运行CriminalIntent应用。点击日期按钮弹出对话框,单击OK按钮消除对话框,如图12-3所示。

第 12 章 对话框_第6张图片

12-3 带有标题和OK按钮的AlertDialog

12.1.2 设置对话框的显示内容

接下来,使用下列AlertDialog.BuildersetView(...)方法,添加DatePicker组件到AlertDialog对话框:

public AlertDialog.Builder setView(View view)

该方法配置对话框,实现在标题栏与按钮之间显示传入的View对象。

layout目录下,创建名为dialog_date.xml的布局文件(Layout XML File),注意,将其根元素改为DatePicker。该布局仅包含一个View对象,即我们生成并传给setView(...)方法的DatePicker视图。

参照图12-4,配置好DatePicker的XML布局文件。
 
   

12-4 DatePicker布局(layout/dialog_date.xml

DatePickerFragment.onCreateDialog( )方法中,生成DatePicker视图并添加到对话框中,如代码清单12-4所示。

代码清单12-4 添加DatePickerAlertDialog(DatePickerFragment.java)

第 12 章 对话框_第7张图片 

运行应用, 点击日期按钮,确认对话框上是否出现了DatePicker视图,如图12-5所示。

第 12 章 对话框_第8张图片

12-5 显示DatePickerAlertDialog

至此,将对话框显示在屏幕上的工作就完成了。下一节,我们会将DatePickerCrime的日期关联起来,并支持用户对其进行修改。

12.2 fragment 间的数据传递

前面,我们已经实现了activity之间以及基于fragment的activity之间的数据传递。现在需实现由同一activity托管的两个fragment,即CrimeFragmentDatePickerFragment间的数据传递,如图12-6。

第 12 章 对话框_第9张图片

12-6 CrimeFragmentDatePickerFragment间的对话

要传递crime的记录日期给DatePickerFragment,需实现一个newInstance(Date)方法,然后将Date作为argument附加给fragment。

为返回新日期给CrimeFragment,并实现模型层以及对应视图的更新,需将日期打包为extra并附加到Intent上,然后调用CrimeFragment.onActivityResult( )方法,并传入准备好的Intent参数,如图12-7所示。

第 12 章 对话框_第10张图片

12-7 CrimeFragmentDatePickerFragment间的事件流

但接下来,我们并没有选择调用托管activity的Activity.onActivityResult( )方法,而是调用了Fragment.onActivityResult(...)方法,这似乎有点奇怪?事实上,通过调用onActivityResult( )方法将数据从一个fragment返还给另一个fragment,这种做法不仅行得通,而且可以更灵活地展现对话框fragment。­

12.2.1 传递数据给DatePickerFragment

要传递crime记录日期给DatePickerFragment,需将记录日期保存在DatePickerFragment的argument bundle中,这样,DatePickerFragment便可直接获取到它。

回顾第10章的内容,我们知道,替代fragment的构造方法,创建和设置fragment argument通常是在一个newInstance()方法中完成的。在DatePickerFragment.java中,添加newInstance(Date)方法,如代码清单12-5所示。

第 12 章 对话框_第11张图片

代码清单12-5 添加newInstance(Date)方法(DatePickerFragment.java)

第 12 章 对话框_第12张图片

然后,在CrimeFragment中,用DatePickerFragment.newInstance(Date)方法替换掉DatePickerFragment的构造方法,如代码清单12-6所示。

代码清单12-6 添加newInstance()方法(CrimeFragment.java)

第 12 章 对话框_第13张图片

DatePickerFragment需使用Date中的信息来初始化DatePicker对象。然而,DatePicker对象的初始化需整数形式的月、日、年。Date就是个时间戳,它无法直接提供整数形式的月、日、年。

要想获得所需的整数数值,必须首先创建一个Calendar对象,然后用Date对象对其进行配置,即可从Calendar对象中取回所需信息。

onCreateDialog(...)方法内,从argument中获取Date对象,然后使用它和Calendar对象完成DatePicker的初始化工作,如代码清单12-7所示。

代码清单12-7 获取Date对象并初始化DatePicker(DatePickerFragment.java)

第 12 章 对话框_第14张图片

如代码所示,初始化DatePicker对象时,同时也在该对象上设置了OnDateChangedListener监听器。这样,用户改变DatePicker内的日期后,Date对象即可得到同步更新。下一节,我们将把该Date对象回传给CrimeFragment

为防止设备旋转时发生Date数据的丢失,在onDateChanged( )方法的尾部,我们将Date对象回写保存到了fragment argument中。如发生设备旋转,而DatePickerFragment正显示在屏幕上,那么FragmentManager会销毁当前实例并产生一个新的实例。新实例创建后,FragmentManager会调用它的onCreateDialog( )方法,这样新实例便可从argument中获得保存的日期数据。相比以前使用onSaveInstanceState( )方法保存状态,在fragment argument中保存数据应对设备旋转显然更简单。

(如经常使用fragment,可能会困惑为何不直接保存DatePickerFragment?使用保留的fragment处理设备旋转问题(详见第14章)确实是个好办法。但不幸的是,目前DialogFragment类有个bug,会导致保存的实例行为异常,因此,尝试保存DatePickerFragment现在还不是个好的选择。)

现在,CrimeFragment可成功将要显示的日期传递给DatePickerFragment。运行CriminalIntent应用,查看最终效果。

12.2.2 返回数据给 CrimeFragment

为使CrimeFragment接收到DatePickerFragment返回的日期,需以某种方式追踪记录二者间的关系。

对于activity的数据回传,我们调用startActivityForResult( )方法,ActivityManager负责跟踪记录父activity与activity间的关系。当activity回传数据后被销毁了,ActivityManager知道接收返回数据的应为哪一个activity。

设置目标fragment

类似于activity间的关联,可将CrimeFragment设置成DatePickerFragment目标 fragment。要建立这种关联,可调用以下Fragment方法:

public void setTargetFragment(Fragment fragment, int requestCode)

该方法接受目标fragment以及一个类似于传入startActivityForResult( )方法的请求代码作为参数。随后,目标fragment可使用该请求代码通知是哪一个fragment在返回数据信息。

目标fragment以及请求代码由FragmentManager负责跟踪记录,我们可调用fragment(设置目标fragment的fragment)的getTargetFragment()getTargetRequestCode()方法获取它们。

在CrimeFragment.java中,创建一个请求代码常量,然后将CrimeFragment设为DatePickerFragment实例的目标fragment,如代码清单12-8所示。

代码清单12-8 设置目标fragment(CrimeFragment.java)

第 12 章 对话框_第15张图片

传递数据给目标fragment

建立CrimeFragmentDatePickerFragment间的联系后,需将数据返还给CrimeFragment。返回的日期数据将作为extra附加给Intent

DatePickerFragment类中,新建一个sendResult()私有方法。通过该方法,创建一个intent,将日期数据作为extra附加到intent上。最后调用CrimeFragment.onActivityResult()方法。在onCreateDialog()方法中,取代setPositiveButton()null参数,实现一个DialogInterface.OnClickListener监听器接口。然后在监听器接口的onClick()方法中,调用新建的sendResult()私有方法并传入结果代码,如代码清单12-9所示。

代码清单12-9 回调目标fragment(DatePickerFragment.java)

第 12 章 对话框_第16张图片

在CrimeFragment中,覆盖onActivityResult(...)方法,从extra中获取日期数据,设置对应Crime的记录日期,然后刷新日期按钮的显示,如代码清单12-10所示。

代码清单12-10 响应DatePicker对话框(CrimeFragment.java)

第 12 章 对话框_第17张图片

onCreateView()onActivityResult()方法中,设置按钮上显示信息的代码完全一样。因此,为避免代码冗余,将其封装到一个公有的updateDate()方法中,然后分别在两个方法中调用它,如代码清单12-11所示。

代码清单12-11 使用公共的updateDate()方法(CrimeFragment.java)

第 12 章 对话框_第18张图片

日期数据的双向传递完成了。运行CriminalIntent应用,确保可以控制日期的传递与显示。修改某项Crime的日期,确认CrimeFragment视图显示了新的日期。然后返回crime列表项界面,查看对应Crime的日期,确认模型层数据已得到更新。

还有一个问题, 日期显示的格式是不好看,改成“标准”格式比较耐看,参考代码清单12-12修改updateDate()函数。

代码清单12-12 修改updateDate()方法,显示北京时间(CrimeFragment.java)

第 12 章 对话框_第19张图片

同样,ListView 的日期显示的格式也是“不好看“,参考代码清单12-13改成“标准”格式

代码清单12-13 ListView 的日期,显示北京时间(CrimeFragment.java)

第 12 章 对话框_第20张图片

转载于:https://www.cnblogs.com/jlxuqiang/p/4756027.html

你可能感兴趣的:(第 12 章 对话框)