Android實踐——密碼本SecretBook

看了幾天的東西,特別是看完數據庫SQLite之後(第一行代碼),想着無非還是CRUD之類的,也是該練練手了。做個什麼呢,要簡單一點的,實用點的,於是想好了——密碼本,記錄自己用到的各種賬號密碼。


這幾天又看了點《代碼大全2》,想着這東西要運用一下啊,於是結合作者軟件構建的思維加Android的技術來練練手唄~雖然說系統有大有小,大系統大架構大考慮,小系統不需考慮那麼多。


開發過程中遇到了各種問題,看書時覺得挺簡單的東西,寫起來就會出各種bug,開發工具又不是太會用,解決個問題要費老大的勁啊,真是抓狂!做完了現在記錄下下~有的問題解決了,有的問題還是不知道怎麼回事……(′д` )…彡…彡


按照軟件開發過程,開始前的準備工作三部曲:問題定義、需求分析、軟件架構。


Android實踐——密碼本SecretBook - hh-csq - HA HA

 

Android實踐——密碼本SecretBook - hh-csq - HA HA

 

一、問題定義

從客戶的角度來看問題,用客戶的語言來描述問題:開發一個個人密碼管理工具


二、需求分析

通過口令(密碼)進入密碼本;

可以新增賬號,保存到數據庫;

可以查看全部賬號密碼信息,全部展示出來,就在一個頁面,類似記事本;

可以修改賬號信息,CRUD(增刪改查);

安全:暫時不做要求


三、系統架構

  1. 程序組織:密碼本是用來管理賬戶密碼的(這就是概述)         構造快:界面、數據庫操作、業務邏輯……只能想到這三個~(@_@;)             通信規則:Activity調用xml顯示界面,調用DB類操作數據庫;外部通信:暫時作爲本地服務,不與其他應用交互
  2. 主要類:MainActivity:登錄界面;ChooseOperationActivity:操作選擇;AddActivity:新增;DisplayAllActivity:查看全部信息;MyDataBaseHelper:數據庫操作
  3. 數據庫設計:一張表:secretbook;字段:id、site、account、password、note
  4. 界面設計:見需求分析,Login——》Select——》Edit——》ViewAll
  5. 安全性:暫時不考慮安全性能,因爲對SQLite數據庫不瞭解,對Android系統管理文件的情況也不清楚,也不知道加密規則
  6. 可擴展性:要考慮以後會增加到多張表,增加新功能
  7. 可行性:100%,完全是個人實踐之作,項目很小,就不多花時間準備了,第一次做,肯定會遇到問題,那就邊做邊解決問題吧
  8. ……不多說了,動手吧!<( ̄︶ ̄)>

編程語言:肯定是Java啊,Android的界面是用xml寫

下面問題出現了:
首先考慮登錄界面activity_main.xml:Android中有四種佈局LinearLayout(線性佈局)、RelativeLayout(相對佈局)、FrameLayout(幀佈局)、TableLayout(表格佈局),到底用哪種呢?糾結了……一個輸入框、一個登錄按鈕而已,我想把他們一起放在屏幕中間位置,那用RelativeLayout是最方便的呢,因爲他可以用android:layout_alignParentTop="true"在子控件裏,這個屬性讓本控件相對父級控件水平垂直方向上都居中,外面用<RelativeLayout>裏面用一個<TableLayout>就可以了,我剛開始把android:layout_width和android:layout_height設置成了"match_parent",在手機上顯示時密碼輸入框和按鈕就位於最左上角,且佔了整個屏幕寬度,不好看,於是改成了"wrap_content",才顯示在了屏幕中間位置,可是在我輸入時,由於輸入法的高度擋住了按鈕,於是又將TableLayout往上拖了一點,這就是所謂的用戶體驗了吧,要時時刻刻考慮使用上的方便性,

登錄時的密碼問題我寫死在了程序裏面,由於沒有設置對應的數據庫來管理,暫時就這樣吧……

然後是操作選擇界面<LinearLayout>裏面嵌<LinearLayout>吧。暫時還好,沒出問題,可是按返回鍵時,密碼還留在上一個Activity的輸入框裏,這是不對滴~我也不知道怎麼做好,只有讓它在通過密碼驗證之後清除數據了:editText.setText("");但是這樣寫效果很不好,會有個明顯的清除過程後纔會跳轉,讓人不舒服,可是我目前也不知道怎麼辦,就先這樣吧……

接着新增數據,可以,操作數據庫MyDataBaseHelper就得了。
package com.thsware.secretbook;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.widget.Toast;

/**
 * Created by 世祥 on 2015/9/4.
 */
public class MyDataBaseHelper extends SQLiteOpenHelper {

    private static final String CREATE_SECRETBOOK="create table secretbook("
            +"id integer primary key autoincrement,"
            +"site text,"
            +"account text,"
            +"password text,"
            +"note text,"
            +"deleteFlag integer"
            +")";

    private Context mContext;

    public MyDataBaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        mContext=context;
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        sqLiteDatabase.execSQL(CREATE_SECRETBOOK);
        Toast.makeText(mContext,"secretbook數據表創建成功~",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
        switch (oldVersion){
            case 1:
                sqLiteDatabase.execSQL("alter table secretbook add column deleteFlag integer");
            default:
        }
    }
}


但是如果這裏要退出app我得按三次返回鍵啊????難受不難受!得考慮在工具欄上面加個退出app的按鈕,於是做一個ActivityCollector類來管理每次新建的Activity,這就涉及到設計模式的問題了,讓每個Activity在onCreate()方法中將自己交給ActivityCollector管理,當點擊退出按鈕時就調用ActivityCollector的方法一次性來結束所有的Activity,
package com.thsware.secretbook;

import android.app.Activity;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by 世祥 on 2015/9/4.
 */
public class ActivityCollector {

    private static List<Activity> activities=new ArrayList<Activity>();

    public static void addActivity(Activity activity){
        activities.add(activity);
    }

    public static void removeActivity(Activity activity){
        activities.remove(activity);
    }

    public static void finishAll(){
        for (Activity ac : activities){
            if (!ac.isFinishing()){
                ac.finish();
            }
        }
    }
}

再就是查看全部數據了,問題又來了:怎麼展示出來,又得考慮UI了,用<ListView>,裏面就用默認的android.R.layout.simple_list_item_1吧,適配器就用String數組好了,將數據庫中查到的每一條數據組裝成我想要的格式放到數據列表中,這也算是結束了,可是問題就是這時出現的:我聲明瞭一個全局變量private List<String>dataList;可是忘記初始化了,我想着儘量遲一點初始化,等到用時再初始化,可是運行時始終崩潰。這個Android Studio工具不知道怎麼調試的,沒顯示錯誤的原因啊,像Eclipse中就會明明白白寫出是什麼問題並顯示錯誤鏈拋出的棧信息,可是這AS調試就什麼都讓人看不懂,在這裏糾結了好久,,可能是我不會用吧……但我還是覺得AS設計的不好……(〃` 3′〃)

再考慮ListView中每個子項的點擊事件吧,根據點擊的位置將信息帶到新增頁面上去(當時就不應該叫AddActivity的,這裏明顯是修改的功能嘛,所以設計時就要考慮好這個類是幹嘛用的,這裏就看出設計的重要性了,改動的成本是很大的,幸好我只是在自己練習),可是每當我點擊時總崩潰,,又一次出錯了不會調試……真難受啊……都不知道從哪裏拋出了什麼錯誤……哎╮(╯▽╰)╭慢慢試,,發現點擊新增頁面時也會崩潰,那麼就是AddActivity有問題了,可是找了好久還是不知道怎麼回事,google……也沒找到怎麼回事,於是沒辦法了,最傻的方法來吧,一點點的改回去,邊改邊測試,看看是什麼問題,我退啊退,,終於發現了問題所在,我將全局變量初始化時出的問題,如下:
private MyDataBaseHelper dbHelper=new MyDataBaseHelper(AddActivity.this, DATABASE_NAME, null, 2);
private SQLiteDatabase db= dbHelper.getWritableDatabase();
我還是不知道爲什麼,我想可能是startActivity(intent)出的問題吧,也可能是類加載的機制問題,看來我又忘了Java類加載的機制問題了吧~要找時間將沒看完的《Thinking in Java》再好好學學了,,當時還明明白白的,現在好久沒弄就忘了,,,,衰( ⊙ o ⊙ )啊!

於是改成了在onCreate()方法中再初始化dbHelper,這樣就可以了,但是問題是永遠不會結束的,永遠解決不完的……接着出現的問題是intent帶過來的數據問題,後來弄清楚了若某intent沒有攜帶相應的數據,則獲取到的是null,最後onCreate方法如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_add);
    ActivityCollector.addActivity(this);

    siteEdit= (EditText) findViewById(R.id.site);
    accountEdit= (EditText) findViewById(R.id.account);
    passwordEdit= (EditText) findViewById(R.id.password);
    noteEdit= (EditText) findViewById(R.id.note);
    saveButton= (Button) findViewById(R.id.save_button);
    dbHelper=new MyDataBaseHelper(AddActivity.this, DATABASE_NAME, null, 2);
    db = dbHelper.getWritableDatabase();

    Intent intent=getIntent();
    //未檢測intent是否爲空,此處id無數據時爲null
    id=intent.getStringExtra("id");
    if (id!=null && !id.equals("")){
        Cursor cursor=db.query(SECRETBOOK_TABLE, null, "id=?", new String[]{id}, null, null, null);
        if (cursor.moveToFirst()){
            String site=cursor.getString(cursor.getColumnIndex("site"));
            String account=cursor.getString(cursor.getColumnIndex("account"));
            String password=cursor.getString(cursor.getColumnIndex("password"));
            String note=cursor.getString(cursor.getColumnIndex("note"));

            siteEdit.setText(site);
            accountEdit.setText(account);
            passwordEdit.setText(password);
            noteEdit.setText(note);
        }
    }

    //保存數據
    saveButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            String site = siteEdit.getText().toString().trim();
            if (site.equals("")) {
                Toast.makeText(AddActivity.this, R.string.sitename + "必須填寫", Toast.LENGTH_SHORT).show();
                return;
            }
            ContentValues values = new ContentValues();
            values.put("site", site);
            values.put("account", accountEdit.getText().toString());
            values.put("password", passwordEdit.getText().toString());
            values.put("note", noteEdit.getText().toString());

            //如果id爲空,則是新增數據
            if (id==null || id.equals("")){
                values.put("deleteFlag", 0);
                db.insert(SECRETBOOK_TABLE, null, values);
            }else{
                db.update(SECRETBOOK_TABLE,values,"id=?",new String[]{id});
            }

            //未做判斷,就提示成功了
            Toast.makeText(AddActivity.this, "保存成功!", Toast.LENGTH_SHORT).show();
        }
    });
}
肯定是不好的,但是目前我也只能寫成這樣了,以後再慢慢優化吧!

數據庫也出了問題,創建時沒考慮刪除的問題,後來想到刪除時,就直接將數據給刪除了,這是不好的,於是設置了一個deleteFlag刪除標記,0就是正常的,1就是刪除了的,本來只需要一位做標記就可以了的,可是查資料,SQLite中沒有bit或者Boolean類的數據,於是選擇了integer,但是數據庫改變了,版本升級的問題也出現了,最後照着作者給的方案解決了。


本以爲是一個小實踐,可是前前後後也出了好多問題。也算是學到了好多吧!這次是將前前後後各章的內容都結合起來用到了一遍!還有很多問題遺留,以待解決

所以說讀萬卷書不如行萬里路啊~實踐纔是檢驗真理的唯一標準~紙上得來終覺淺~……

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章