Android实践——密码本SecretBook

来源:互联网 发布:绕x轴顺时针旋转矩阵 编辑:程序博客网 时间:2024/05/18 03:12
看了几天的东西,特别是看完数据库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方法如下:
@Overrideprotected 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,但是数据库改变了,版本升级的问题也出现了,最后照着作者给的方案解决了。


本以为是一个小实践,可是前前后后也出了好多问题。也算是学到了好多吧!这次是将前前后后各章的内容都结合起来用到了一遍!还有很多问题遗留,以待解决

所以说读万卷书不如行万里路啊~实践才是检验真理的唯一标准~纸上得来终觉浅~……

1 0
原创粉丝点击