zoukankan      html  css  js  c++  java
  • 第 12 章 对话框

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

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

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

    image

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

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

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

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

    image

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

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

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

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

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

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

    image

    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)

    image

    如代码清单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)

    image

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

    image

    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布局文件。
    image

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

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

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

    image 

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

    image

    12-5 显示DatePickerAlertDialog

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

    12.2 fragment 间的数据传递

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

    image

    12-6 CrimeFragmentDatePickerFragment间的对话

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

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

    image

    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所示。

    image

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

    image

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

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

    image

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

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

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

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

    image

    如代码所示,初始化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)

    image

    传递数据给目标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)

    image

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

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

    image

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

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

    image

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

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

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

    image

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

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

    image

  • 相关阅读:
    spring的@Transactional注解详细用法
    解决:No qualifying bean of type [org.springframework.jdbc.core.JdbcTemplate] found for dependency
    SpringBoot2 启动报错 Failed to auto-configure a DataSource
    SpringBoot2 全局异常处理
    Intellij IDEA 将工程转换成maven工程 详解
    js性能优化
    JDK故障处理工具箱
    编写高性能的jquery代码
    maven工程解决jar包冲突依赖问题
    spring aop中xml配置文件中标签和属性对应的类
  • 原文地址:https://www.cnblogs.com/jlxuqiang/p/4756027.html
Copyright © 2011-2022 走看看