CriminalIntent项目总结(二)

2017-10-14  本文已影响14人  YTY尹尹尹

为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参数,如下图。

CrimeFragment与DatePickerFragment间的事件流

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 是个神奇的表数据处理工具,其任务就是封装数据表中的原始字段值

总结

这三章的内容主要是一些基础的功能实现,所以有的代码没有具体贴,相对而言还是比较简单的部分,跟着书完成还是算得上零失误的,写起来也很欢乐,但博客写的真的是宛如流水帐...还是希望能有所收获有所进步吧!

上一篇下一篇

猜你喜欢

热点阅读