CriminalIntent项目总结(二)
为CriminalIntent应用添加对话框
对话框既能引起用户的注意也可以接收用户的输入,为其添加对话框,以便用户修改crime记录日期,用户点击CrimeFragment中的日期按钮时,应用会弹出如下图所示的对话框。
可选择crime日期的对话框
创建DialogFragment
由CrimePagerActivity托管的两个fragment对象总结下来,要实现对话框显示,首先应完成以下任务:
(1)创建DatePickeFragment类;
(2)创建AlertDialog;
(3)通过FragmentManager在屏幕上显示对话框。
重要代码
1、为对话框标题添加字符串资源(values/strings.xml)
<resources>
<string name="crime_solve_label">Solved</string>
<string name="date_picker_title">Date of crime:</string>
</resources>
2、创建DialogFragment(DialogFragment.java)
public class DatePickerFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.date_picker_title)
.setPositiveButton(android.R.string.ok,null)
.create();
}
}
其中DialogFragment超类为支持库中android.support.v4.app.DialogFragment类
而AlertDialog需要import android.support.v7.app.AlertDialog;
3、显示DialogFragment(CrimeFragment.java)
public class CrimeFragment extends Fragment {
private static final String ARG_CRIME_ID = "crime_id";
private static final String DIALOG_DATE = "DialogDate";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
mDateButton = (Button) v.findViewById(R.id.crime_date);
mDateButton.setOnClickListener(new View.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);
}
});
mSolvedCheckbox = (CheckBox) v.findViewById(R.id.crime_solved);
...
return v;
}
}
4、DatePicker布局(layout/dialog_date.xml)
<?xml version="1.0" encoding="utf-8"?>
<DatePicker
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dialog_date_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:calendarViewShown="false">
</DatePicker>
5、给AlertDialog添加DatePicker(DatePickerFragment.java)
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
View v = LayoutInflater.from(getActivity()).inflate(R.layout.dialog_date,null);
return new AlertDialog.Builder(getActivity())
.setView(v)
.setTitle(R.string.date_picker_title)
.setPositiveButton(android.R.string.ok,null)
.create();
}
fragment 间的数据传递
CrimeFragment与DatePickerFragment间的对话要传递crime的记录日期给DatePickerFragment,需实现一个newInstance(Date)方法,然后将Date作为argument附加给fragment。
为返回新日期给CrimeFragment,并实现模型层以及对应视图的更新,需将日期打包为extra并附加到Intent上,然后调用CrimeFragment.onActivityResult( )方法,并传入准备好的Intent参数,如下图。
6、添加newInstance(Date)方法(DatePickerFragment.java)
public class DatePickerFragment extends DialogFragment {
private static final String ARG_DATE = "date";
private DatePicker mDatePicker;
public static DatePickerFragment newInstance(Date date){
Bundle args = new Bundle();
args.putSerializable(ARG_DATE,date);
DatePickerFragment fragment = new DatePickerFragment();
fragment.setArguments(args);
return fragment;
}
}
7、获取Date对象并初始化DatePicker(DatePickerFragment.java)
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Date date = (Date) getArguments().getSerializable(ARG_DATE);
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
int year = calendar.get(Calendar.YEAR);
final int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);
View v = LayoutInflater.from(getActivity()).inflate(R.layout.dialog_date,null);
mDatePicker = (DatePicker) v.findViewById(R.id.dialog_date_picker);
mDatePicker.init(year,month,day,null);
return new AlertDialog.Builder(getActivity())
.setView(v)
.setTitle(R.string.date_picker_title)
.setPositiveButton(android.R.string.ok,null)
.create();
}
返回数据给CrimeFragment
1、设置目标fragment
类似于activity间的关联,可将CrimeFragment设置成DatePickerFragment的目标 fragment。要建立这种关联,可调用以下Fragment方法:
public void setTargetFragment(Fragment fragment, int requestCode)
2、传递数据给目标fragment
建立CrimeFragment与DatePickerFragment间的联系后,需将数据返还给CrimeFragment。返回的日期数据将作为extra附加给Intent。
8、回调目标fragment(DatePickerFragment.java)
public class DatePickerFragment extends DialogFragment {
public static final String EXTRA_DATE = "com.bignerdranch.android.criminalintent.date";
private static final String ARG_DATE = "date";
...
@Override
public Dialog onCreateDialog(Bundle saveInstanceState{
...
}
private void sendResult(int resultCode,Date date){
if(getTargetFragment() == null){
return;
}
Intent intent = new Intent();
intent.putExtra(EXTRA_DATE,date);
getTargetFragment()
.onActivityResult(getTargetRequestCode(),resultCode,intent);
}
}
10、一切是否都OK?(DatePickerFragment.java)
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
...
return new AlertDialog.Builder(getActivity())
.setView(v)
.setTitle(R.string.date_picker_title)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
int year = mDatePicker.getYear();
int month = mDatePicker.getMonth();
int day = mDatePicker.getDayOfMonth();
Date date = new GregorianCalendar(year,month,day).getTime();
sendResult(Activity.RESULT_OK,date);
}
})
.create();
}
11、响应DatePicker对话框(CrimeFragment.java)
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if(resultCode != Activity.RESULT_OK){
return;
}
if(requestCode == REQUEST_DATE){
Date date = (Date) data.getSerializableExtra(DatePickerFragment.EXTRA_DATE);
mCrime.setDate(date);
mDateButton.setText(mCrime.getDate().toString());
}
}
}
为CriminalIntent应用创建工具栏菜单
工具栏 工具栏重要代码
1、添加字符串资源(values/strings.xml)
<resources>
<string name="new_crime">New Crime</string>
<string name="show_subtitle">Show Subtitle</string>
<string name="hide_subtitle">Hide Subtitle</string>
<string name="subtitle_format">%1$d crimes</string>
</resources>
在 XML 文件中定义菜单
在项目工具窗口中,右键单击res目录,选择New → Android resource file菜单项。在弹出的窗
口界面,选择Menu资源类型,并命名资源文件为fragment_crime_list,点击OK按钮确认
2、创建菜单资源(fragment_crime_list.xml)
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_item_new_crime"
android:icon="@android:drawable/ic_menu_add"
android:title="@string/new_crime"
app:showAsAction="ifRoom|withText"/>
</menu>
为工具栏创建图片
在项目工具窗口中,右键单击drawable目录,选择New → Image Asset菜单项
Asset Studio
3、引用资源(menu/fragment_crime_list.xml)
<item
android:id="@+id/menu_item_new_crime"
android:icon="@drawable/ic_menu_add"
android:title="@string/new_crime"
app:showAsAction="ifRoom|withText"/>
4、实例化选项菜单(CrimeListFragment.java)
@Override
public void onResume() {
super.onResume();
updateUI();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.fragment_crime_list, menu);
}
5、 调用 SetHasOptionsMenu 方法(CrimeListFragment.java)
...
private RecyclerView mCrimeRecyclerView;
private CrimeAdapter mAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
...
6、响应菜单项选择事件(CrimeListFragment.java)
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.fragment_crime_list, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_new_crime:
Crime crime = new Crime();
CrimeLab.get(getActivity()).addCrime(crime);
Intent intent = CrimePagerActivity
.newIntent(getActivity(), crime.getId());
startActivity(intent);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
层级式导航的工作原理
CriminalIntent应用中,后退按钮导航和向上按钮导航执行同样的操作。在 CrimePagerActivity
界面,无论按哪个按钮导航,都是回到 CrimeListActivity 界面。虽然结果一样,但它们各自的后台实现机制却大不相同。知道这一点很重要,因为取决于具体应用,向上导航很可能会让用户迷失在众多activity中(这里指回退栈内的众多activity)。
用户点击向上按钮自 CrimePagerActivity 界面向上导航时,如下的intent会被创建:
Intent intent = new Intent(this, CrimeListActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
finish();
FLAG_ACTIVITY_CLEAR_TOP 指示Android在回退栈中寻找指定的activity实例。如存在,则弹出
栈内所有其他activity,让启动的目标activity出现在栈顶(显示在屏幕上)
工作中的FLAG_ACTIVITY_CLEAR_TOP
总而言之,显示子标题并旋转设备,可以看到依然正确
横屏SQLite数据库
定义schema
crimes数据表重要代码
1、定义 CrimeTable 内部类(CrimeDbSchema.java)
public class CrimeDbSchema {
public static final class CrimeTable {
public static final String NAME = "crimes";
}
}
2、定义数据表字段(CrimeDbSchema.java)
public class CrimeDbSchema {
public static final class CrimeTable{
public static final String NAME = "crimes";
public static final class Cols{
public static final String UUID = "uuid";
public static final String TITLE = "title";
public static final String DATE = "date";
public static final String SOLVED = "solved";
public static final String SUSPECT = "suspect";
}
}
3、创建 CrimeBaseHelper 类(CrimeBaseHelper.java)
public class CrimeBaseHelper extends SQLiteOpenHelper {
private static final int VERSION = 1;
private static final String DATABASE_NAME = "crimeBase.db";
public CrimeBaseHelper(Context context) {
super(context, DATABASE_NAME, null, VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
4、写入数据库
- 使用ContentValues
- 插入和更新记录
负责处理数据库写入和更新操作的辅助类是 ContentValues 。它是个键值存储类,类似于Java的 HashMap 和前面用过的 Bundle 。不同的是, ContentValues 只能用于处理SQLite数据。
5、读取数据库
- 使用CursorWrapper
- 创建模型对象
Cursor 是个神奇的表数据处理工具,其任务就是封装数据表中的原始字段值
总结
这三章的内容主要是一些基础的功能实现,所以有的代码没有具体贴,相对而言还是比较简单的部分,跟着书完成还是算得上零失误的,写起来也很欢乐,但博客写的真的是宛如流水帐...还是希望能有所收获有所进步吧!