CrimainalIntent項目的開發(fā)

介紹:CriminalIntent應(yīng)用能記錄陋習(xí)的標題,日期以及照片侵蒙,也支持在聯(lián)系人當中查找當事人造虎,通過E-mail,Twitter,FaceBook或其他應(yīng)用提出抗議。本博客是在上一個博客的基礎(chǔ)上進行的延伸蘑志。

  • 第12章:日期對話框累奈,fragment數(shù)據(jù)傳遞;
  • 第13章:工具欄急但,菜單澎媒,層級式導(dǎo)航;
  • 第14章:SQLite數(shù)據(jù)庫的使用;

項目完成步驟:

  • 添加對話框
  • 創(chuàng)建工具欄菜單
  • 連接數(shù)據(jù)庫

對話框

為應(yīng)用添加對話框波桩,以便用戶修改crime記錄日期戒努。用戶點擊
CrimeFragment中的日期按鈕時,會彈出對話框:

圖12-1 可選擇crime日期的對話框

創(chuàng)建DialogFragment

首先會創(chuàng)建名為DatePickerFragment的DialogFragment子類镐躲。然后储玫,在DatePickerFragment中,創(chuàng)建并配置顯示DatePicker組件的AlertDialog實例萤皂。DatePickerFragment同樣由CrimePagerActivity托管撒穷。圖12-2展示了以上各對象間的關(guān)系。

圖12-2 由CrimePagerActivity托管的兩個fragment對象

要顯示對話框裆熙,首先應(yīng)完成以下任務(wù):
? 創(chuàng)建DatePickerFragment類端礼;
? 創(chuàng)建AlertDialog;
? 借助FragmentManager在屏幕上顯示對話框入录。
稍后蛤奥,我們還將配置使用DatePicker,并實現(xiàn)在CrimeFragment和DatePickerFragment之間傳遞數(shù)據(jù)僚稿。

創(chuàng)建DialogFragment

    public class DatePickerFragment extends DialogFragment {
          @Override
          public Dialog onCreateDialog(Bundle saveInstanceState){
                return new AlertDialog.Builder(getActivity())
                         .setTitle(R.string.date_picker_title)
                         .setPositiveButton(android.R.string.ok, null)
                         .create();
          }
    }

顯示DialogFragment

   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.setText(mCrime.getDate().toString()); 
    //mDateButton.setEnabled(false);
    mDateButton.setOnClickListener(new View.OnClickListener() { 
         @Override 
         public void onClick(View v) { 
             FragmentManager manager = getFragmentManager(); 
             DatePickerFragment dialog = new DatePickerFragment(); 
             dialog.show(manager, DIALOG_DATE); 
         } 
     }); 
     mSolvedCheckBox = (CheckBox) v.findViewById(R.id.crime_solved); 
     ... 
     return v; 
     } 
     ...
}

運行CriminalIntent應(yīng)用凡桥,點擊日期按鈕彈出對話框:

圖12-3 帶標題和OK按鈕的AlertDialog

給AlertDialog添加DatePicker

     @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(); 
} 

運行CriminalIntent應(yīng)用,點擊日期按鈕蚀同,在對話框中顯示DatePicker視圖:


圖12-4 DatePicker視圖

fragment間的數(shù)據(jù)傳遞

圖12-5CrimeFragment與DatePickerFragment間的對話

傳遞數(shù)據(jù)給DatePickerFragment

public class DatePickerFragment extends DialogFragment {
    public static final String EXTRA_DATE =
            "com.bignerdranch.android.criminalintent.date";

    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;
    }

    @Override
    public Dialog onCreateDialog(Bundle saveInstanceState){
        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);

        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)
                .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();
    }

    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);
    }

}
public class CrimeFragment extends Fragment {
    public static CrimeFragment newInstance(UUID crimeId) {
        Bundle args = new Bundle();
        args.putSerializable(ARG_CRIME_ID, crimeId);

        CrimeFragment fragment = new CrimeFragment();
        fragment.setArguments(args);
        return fragment;
    }
  ...
    @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);
            updateDate();
        }
    }
  ...
    private void updateDate() {
        mDateButton.setText(mCrime.getDate().toString());
    }
}

工具欄

為CriminalIntent應(yīng)用創(chuàng)建工具欄菜單缅刽,提供新增crime記錄的菜單項,并實現(xiàn)向上按鈕的導(dǎo)航功能:

圖13-1 CriminalIntent應(yīng)用的工具欄

使用AppCompat庫

? 添加AppCompat依賴項唤崭;
? 使用一種AppCompat主題拷恨;
? 確保所有的activitiy都是AppCompatActivity子類。

使用AppCompat主題

<resources> 
   <style name="AppTheme"parent="Theme.AppCompat.Light.DarkActionBar"> 
   </style> 
</resources> 

工具欄菜單

菜單及菜單項需用到一些字符串資源谢肾。將它們添加到strings.xml文件中。

<resources> 
 ... 
   <string name="date_picker_title">Date of crime:</string> 
   <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$s crimes</string> 
</resources> 

創(chuàng)建菜單資源

<?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> 

創(chuàng)建菜單

在代碼中小泉,Activity類提供了管理菜單的回調(diào)函數(shù)芦疏。需要選項菜單時冕杠,Android會調(diào)用Activity的onCreateOptionsMenu(Menu)方法。然而酸茴,按照CriminalIntent應(yīng)用的設(shè)計分预,選項菜單相關(guān)的回調(diào)函數(shù)需在fragment而非activity里實現(xiàn)。不用擔心薪捍,F(xiàn)ragment有一套自己的選項菜單回調(diào)函數(shù)笼痹。稍后,我們會在CrimeListFragment中實現(xiàn)這些方法酪穿。以下為創(chuàng)建菜單和響應(yīng)菜單項選擇事件的兩個回調(diào)方法:
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
public boolean onOptionsItemSelected(MenuItem item)
在CrimeListFragment.java中凳干,覆蓋onCreateOptionsMenu(Menu, MenuInflater)方法,實例化fragment_crime_list.xml中定義的菜單被济。

實例化選項菜單

@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); 
}

在以上方法中救赐,調(diào)用MenuInflater.inflate(int, Menu)方法并傳入菜單文件的資源ID,將布局文件中定義的菜單項目填充到Menu實例中只磷。

響應(yīng)菜單項選擇事件

    @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.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);
        }
    }

實現(xiàn)層級式導(dǎo)航

... 
<activity 
   android:name=".CrimePagerActivity" 
   android:label="@string/app_name" 
   android:parentActivityName=".CrimeListActivity"> 
</activity> 
... 
圖13-2 CrimePagerActivity界面上的向上按鈕

SQLite數(shù)據(jù)庫

創(chuàng)建數(shù)據(jù)庫前经磅,首先要清楚存儲什么樣的數(shù)據(jù)。CriminalIntent應(yīng)用要保存的是一條條crime記錄钮追,定義Schema的方式眾多预厌,如何選擇往往因人而異。處理類似的任務(wù)元媚,開發(fā)人員都有個共同的目標:“不要重復(fù)造輪子轧叽。”實際上惠毁,這也是人人都應(yīng)遵守的編程準則:多花時間思考復(fù)用代碼的編寫和調(diào)用犹芹,避免在應(yīng)用中到處使用重復(fù)代碼。
基于上述準則鞠绰,我們可以使用能統(tǒng)一定義模型層對象(如Crime)的高級ORM(對象關(guān)系映射)工具腰埂。不過,對于CriminalIntent應(yīng)用蜈膨,本章打算直接在Java代碼中定義描述表名和數(shù)據(jù)字段的數(shù)據(jù)庫schema屿笼。
首先,我們來創(chuàng)建定義schema的Java類翁巍。創(chuàng)建時驴一,命名類為CrimeDbSchema,同時在新建類對話框中輸入包名database.CrimeDbSchema灶壶。這樣肝断,就可以將CrimeDbSchema.java文件放入專門的database包中,實現(xiàn)數(shù)據(jù)庫操作相關(guān)代碼的組織和歸類。

定義CrimeTable內(nèi)部類

public class CrimeDbSchema { 
   public static final class CrimeTable { 
       public static final String NAME = "crimes"; 
   } 
} 

定義數(shù)據(jù)表字段

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";
         }
     } 
} 

創(chuàng)建初始數(shù)據(jù)庫

步驟:
(1) 確認目標數(shù)據(jù)庫是否存在胸懈。
(2) 如果不存在担扑,首先創(chuàng)建數(shù)據(jù)庫,然后創(chuàng)建數(shù)據(jù)庫表以及必需的初始化數(shù)據(jù)趣钱。
(3) 如果存在涌献,打開并確認CrimeDbSchema是否是最新版本(后續(xù)章節(jié)可能會在CriminalIntent
中有增刪操作)。
(4) 如果是舊版本首有,就運行相關(guān)代碼升級到最新版本燕垃。

創(chuàng)建CrimeBaseHelper類編寫SQL創(chuàng)建初始代碼

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) {
        db.execSQL("create table " + CrimeDBSchema.CrimeTable.NAME); 
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}

打開SQLiteDatabase

public class CrimeLab { 
     private static CrimeLab sCrimeLab; 
     private List<Crime> mCrimes; 
     private Context mContext; 
     private SQLiteDatabase mDatabase; 
     ... 
     private CrimeLab(Context context) { 
         mContext = context.getApplicationContext();
         mDatabase = new CrimeBaseHelper(mContext)
             .getWritableDatabase();
         mCrimes = new ArrayList<>(); 
     } 
     ... 
} 

調(diào)用getWritableDatabase()方法時,CrimeBaseHelper要做如下工作井联。
(1)
打開/data/data/com.bignerdranch.android.criminalintent/databases/crimeBase.db數(shù)據(jù)庫卜壕;如果不存在,就先創(chuàng)建crimeBase.db數(shù)據(jù)庫文件低矮。
(2) 如果是首次創(chuàng)建數(shù)據(jù)庫印叁,就調(diào)用onCreate(SQLiteDatabase)方法,然后保存最新的版本號军掂。
(3) 如果已創(chuàng)建過數(shù)據(jù)庫轮蜕,首先檢查它的版本號。如果CrimeOpenHelper中的版本號更高蝗锥,就調(diào)用onUpgrade(SQLiteDatabase, int, int)方法升級跃洛。
最后,再做個總結(jié):onCreate(SQLiteDatabase)方法負責(zé)創(chuàng)建初始數(shù)據(jù)庫终议;onUpgrade (SQLiteDatabase, int, int)方法負責(zé)與升級相關(guān)的工作汇竭。

修改 CrimeLab 類

創(chuàng)建完數(shù)據(jù)庫,接下來是調(diào)整CrimeLab類代碼穴张,改用mDatabase存儲數(shù)據(jù)细燎。
首先要刪除CrimeLab中所有mCrimes相關(guān)的代碼,代碼調(diào)整完畢皂甘,運行CriminalIntent應(yīng)用只會看到空列表和空CrimePagerActivity玻驻。

寫入數(shù)據(jù)庫

  • 使用 ContentValues
  • 插入和更新記錄

讀取數(shù)據(jù)庫

  • 使用 CursorWrapper
  • 創(chuàng)建模型層對象

總結(jié)

編寫代碼過程中難免遇到單詞拼寫錯誤的情況,只要細心檢查偿枕,錯誤都是可以解決的璧瞬。但是遇到手機與電腦連接不起來、虛擬機開啟失敗等問題渐夸,就很難自己解決了嗤锉。虛擬機時而運行成功時而運行失敗,手機是直接安裝不了APP墓塌,自己也找了很久瘟忱,還是不能解決問題奥额,也問了老師,但還是沒能解決酷誓,不知道是哪里出了錯披坏。整個項目梳理的還算可以态坦,就是運行不了讓人頭疼盐数。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市伞梯,隨后出現(xiàn)的幾起案子玫氢,更是在濱河造成了極大的恐慌,老刑警劉巖谜诫,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件漾峡,死亡現(xiàn)場離奇詭異,居然都是意外死亡喻旷,警方通過查閱死者的電腦和手機生逸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來且预,“玉大人槽袄,你說我怎么就攤上這事》嫘常” “怎么了遍尺?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長涮拗。 經(jīng)常有香客問我乾戏,道長,這世上最難降的妖魔是什么三热? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任鼓择,我火速辦了婚禮,結(jié)果婚禮上就漾,老公的妹妹穿的比我還像新娘呐能。我一直安慰自己,他們只是感情好从藤,可當我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布催跪。 她就那樣靜靜地躺著,像睡著了一般夷野。 火紅的嫁衣襯著肌膚如雪懊蒸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天悯搔,我揣著相機與錄音骑丸,去河邊找鬼。 笑死,一個胖子當著我的面吹牛通危,可吹牛的內(nèi)容都是我干的铸豁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼菊碟,長吁一口氣:“原來是場噩夢啊……” “哼节芥!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起逆害,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤头镊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后魄幕,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體相艇,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年纯陨,在試婚紗的時候發(fā)現(xiàn)自己被綠了坛芽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡翼抠,死狀恐怖咙轩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情机久,我是刑警寧澤臭墨,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站膘盖,受9級特大地震影響胧弛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜侠畔,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一结缚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧软棺,春花似錦红竭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至瘦棋,卻和暖如春稀火,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背赌朋。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工凰狞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留篇裁,地道東北人。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓赡若,卻偏偏與公主長得像达布,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子逾冬,可洞房花燭夜當晚...
    茶點故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫黍聂、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,121評論 4 61
  • 介紹:CriminalIntent應(yīng)用能記錄陋習(xí)的標題粉渠,日期以及照片分冈,也支持在聯(lián)系人當中查找當事人,通過E-mai...
    19377b7fb478閱讀 307評論 0 0
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,302評論 25 707
  • 1慢吧慢吧 拖著那回憶的時光 讓這三行 無限的長 2在我的心上 那貧瘠的土壤 那曾是你深深扎根的地方 3散吧 散吧...
    思嘉閱讀 318評論 0 1
  • 測試霸株,好像是現(xiàn)在年輕人的一種潮流,聰明的人想測智商集乔,懂人情世故的人想測情商去件,單身的人想測戀愛值期盼自己的另...
    鈺子閱讀 1,018評論 0 1