第十二章 对话框
有关AlertDialog
AlertDialog是Dialog的子类,可以在当前的界面上显示一个对话框,这个对话框是置顶于所有界面元素之上的,能够屏蔽掉其他控件的交互能力,因此AlertDialog一般是用于提示一些非常重要的内容或者警告信息。
AlertDialog.Builder
AlertDialog.Builder(getActivity()).setView(v).setTitle(R.string.date_picker_title).setPositiveButton(android.R.string.ok, null).create();
这个类用流式接口的方式创建AlertDialog实例,在public Dialog onCreateDialog(@Nullable Bundle savedInstanceState)中调用。等价于
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setView(v);
builder.setTitle(R.string.date_picker_title);
builder.setPositiveButton(android.R.string.ok, null);
builder.create();
还有一些其它可以用到的方法:
- setTitle:设置对话框的标题,比如“提示”、“警告”等;
- setMessage:设置对话框要传达的具体信息;
- setIcon: 设置对话框的图标;
- setCancelable: 点击对话框以外的区域是否让对话框消失,默认为true;
- setPositiveButton:设置正面按钮,表示“积极”、“确认”的意思,第一个参数为按钮上显示的文字,下同;
- setNegativeButton:设置反面按钮,表示“消极”、“否认”、“取消”的意思;
- setNeutralButton:设置中立按钮;
- setOnShowListener:对话框显示时触发的事件;
- setOnCancelListener:对话框消失时触发的事件。
DialogFragment.show()
创建出的AlertDialog实例需要通过DialogFragment来显示。有两种方法将DialogFragment添加给FragmentManager来管理:
public static show(FragmentManager manager,String tag)
public static show(FragmentTransaction Transaction ,String tag)
这里的tag适用于唯一标识FragmentManager队列中的DialogFragment,相当于建立一个键值对(不是)。区别在于第一种是自动帮你提交事务的。
有关DatePicker
DatePicker是一个可以选择日期的布局类型,可以设置初始的日期时间,也可以从这个布局上获得当前选择的日期。
//初始化
mDatePicker.init(year,month,day,null);
//获取
int year=mDatePicker.getYear();
int month=mDatePicker.getMonth();
int day=mDatePicker.getDayOfMonth();
还有其它的方法可以参照这里。
有关同一Activity下的Fragment之间的数据传递
发送数据
和之前没什么区别,就是利用Fragment的Arguments。例如我在DatePickerFragment中定义一个newInstance,在CrimeFragment中调用之并且附加信息。
返回数据
方式和父子Activity之间、Activity到Fragment之间返回数据一样使用startActivityForResult和onActivityResult方法。只不过这两个Fragment之间不存在父子关系,因此需要多做一步设置目标TargetFragment。
public void setTargetFragment(Fragment fragment,int requestCode)
第一个参数是目标Fragment,第二个参数是请求代码。这两个东西可以分别用getTargetFragment()和getTargetRequestCode()获取。
将数据传回时,需要调用的是目标Fragment的onActivityResult方法,我觉得这个是很显然的东西,不知道书上为什么要说这很难以置信。
重写目标Fragment的onActivityResult方法,使对应的RequestCode下处理返回结果信息,并在源Fragment中调用onActivityResult方法即可。
关于挑战练习 更多对话框
就是照着DatePicker的方式在写一个Fragment和布局文件,只不过用的是TimePicker而已。
有一些小细节需要处理:
TimePicker没有初始化方法,但是可以直接设置其hour和minute。
mTimePicker.setCurrentHour(hour);
mTimePicker.setCurrentMinute(mimute);
用GregorianCalendar()来实例化一个Date,必须要加上年月日,在此处年月日是之前传进来的Date,因此之前的Calender要final一下。
final Calendar calendar=Calendar.getInstance();
calendar.setTime(date);
int hour=calendar.get(Calendar.HOUR);
int mimute=calendar.get(Calendar.MINUTE);
...
...new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int year=calendar.get(Calendar.YEAR);
int month=calendar.get(Calendar.MONTH);
int day=calendar.get(Calendar.DAY_OF_MONTH);
int hour=mTimePicker.getCurrentHour();
int minute=mTimePicker.getCurrentMinute();
Date date=new GregorianCalendar(year,month,day,hour,minute).getTime();
sendResult(Activity.RESULT_OK,date);
}
TimePacker有一个监听器setOnTimeChangedListener,但是我们不需要,因为我们并不关心时间变化的过程;我们只需要关注点下按钮那一刻的时间即可。
关于挑战练习 实现响应式DialogFragment
一开始用@Override没找到DialogFragment的onCreateView(),我以为这个方法被隐藏了,使得我又去重新写了一个Fragment。但是刚刚又发现可以找到了,还要只需要重新移动过来就好了,改动不大。
DatePickerActivity
继承SingleFragmentActivity方法,并且写一个newInstance方法方便父类的Fragment(CrimeFragment)创建其实例。
CrimeFragment
在原来创建DialogFragment的地方改写成startActivityForResult方法:
mDataButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
/*
FragmentManager manager =getFragmentManager();
DatePickerFragment dialog=DatePickerFragment.newInstance(mCrime.getDate());
dialog.setTargetFragment(CrimeFragment.this,REQUEST_DATE);
dialog.show(manager,DIALOG_DATE);
*/
Intent intent=DatePickerActivity.newIntent(getActivity(),mCrime.getDate());
startActivityForResult(intent,REQUEST_DATE);
}
});
DatePickerFragment
在DatePickerFragment中只需要重写onCreateView方法,下面两个方法是可以直接用的,不需要改。
public static DatePickerFragment newInstance(Date date){}
...
private void sendResult(int resultCode,Date date){}
而在onCreateView方法中,大部分代码都一样,只是两者的按钮监听器内容写法有差别,需要调用托管它的Activity方法的setResult来完成将消息传递回父Activity的Fragment(CrimeFragment)。
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View v=inflater.inflate(R.layout.dialog_date_new,container,false);
Date date=(Date) getArguments().getSerializable(ARG_DATE);
Calendar calendar=Calendar.getInstance();
calendar.setTime(date);
int year=calendar.get(Calendar.YEAR);
int month=calendar.get(Calendar.MONTH);
int day=calendar.get(Calendar.DAY_OF_MONTH);
mButton=(Button)v.findViewById(R.id.fragment_date_picker_ok);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int year=mDatePicker.getYear();
int month=mDatePicker.getMonth();
int day=mDatePicker.getDayOfMonth();
Date date=new GregorianCalendar(year,month,day).getTime();
Intent intent=new Intent();
intent.putExtra(EXTRA_DATE,date);
//Toast.makeText(getActivity(),getActivity().getLocalClassName().toString(),Toast.LENGTH_SHORT).show();
getActivity().setResult(Activity.RESULT_OK,intent);
getActivity().finish();
}
});
mDatePicker=(DatePicker) v.findViewById(R.id.dialog_date_picker);
mDatePicker.init(year,month,day,null);
return v;
}
有一个地方要注意的是,点了ok按钮之后最好是需要立刻结束这个Fragment的,所以直接调用托管Activity的finish()方法即可。