项目要求:

Android 端备忘录项目
        需求描述:我希望有一个 Android 端的 App,能让我随时随地记录一件事情,并且它能自动
帮我记下我写这段文字的时间和地点,另外,我希望它不仅能输入文字,必要时也要能插入图
片。此外,有一些内容我不希望别人能看到,当点开这条内容时最好有一个身份验证过程。
        功能分析:
        1、 用户输入文字、插入图片或拍照。
        2、 每条内容存储到本地。
        3、 获取用户当前位置信息并显示在每条记录的合适位置。
        4、 用户新建一条记录时可选择是否把该条记录设置为私密,选择“是”,则每次打开时进行
身份验证(身份验证机制用户自己决定)。

 

项目完成情况:

  • 主功能实现:笔记的展示与全体笔记的删除。
​ 主页面

 

​ 标题
​ 查看已经标记为私密的记录要输入暗号
  • 分支功能实现:笔记的添加单个笔记的删除与加密,最重要的是可以获取手机本地相册和授权相机进行拍照
​ edit编辑页面
​ 相机拍照功能
​ 本地图片选取

 

​ 点击雨伞,改变保密状态

 

​ edit页面删除单个记录
  • 分支功能实现:弹出菜单实现点击设置按钮修改密码功能

 

​ 弹出菜单

 

​ 更改密码界面

 注:地理位置将在007note2.0中由小灰灰实现。


 功能实现详解:

         我在三人小组中参与了主页面添加笔记功能的构建,单独完成了主页面的全部删除功能以及edit页面的单独删除功能,在之后添加了加密功能,完成弹出菜单以及修改密码操作。

        重点讲讲我单独做出那几个功能。

删除功能:

        首先是设置在主页的全部删除功能

    @Override
    public boolean onOptionsItemSelected(MenuItem item){
        switch (item.getItemId()){
            case R.id.menu_clear:
                final EditText et = new EditText(this);
                new AlertDialog.Builder(MainActivity.this)
                        .setMessage("要删除全部记录要密码哦,你要删吗,赶紧输入我们的暗号")
                        .setIcon(android.R.drawable.sym_def_app_icon)
                        .setView(et)
                        .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                if(et.getText().toString().equals(code)){
                                    dbHelper =new NoteDatabase(context);
                                    SQLiteDatabase db =dbHelper.getWritableDatabase();
                                    db.delete("notes",null,null);
                                    refreshListView();
                                }
                                else{
                                    fail();
                                }
                            }
                        }).setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                }).create().show();
                break;
        }
        return super.onOptionsItemSelected(item);
    }

        在上述代码中,我利用菜单的响应事件,根据菜单中的item的id来判定响应事件,利用switch的优势,判断触发的事件是不是全局删除。如果是,代码继续执行。

        定义一个文本类型变量et保存需要输入的暗号密码,输入暗号后,点击OK进入判断,如果暗号符合,则进行数据库数据删除操作,暗号不符合则执行fail方法,提示密码错误,然后再关闭对话框。

        再者,是edit页面的单独删除功能:

case R.id.delete:
                new AlertDialog.Builder(EditActivity.this)
                        .setMessage("真的要删除吗,呜呜呜")
                        .setPositiveButton(android.R.string.yes,new DialogInterface.OnClickListener(){
                            @Override
                            public void onClick(DialogInterface dialog,int which){
                                    intent.putExtra("mode",2);
                                    intent.putExtra("id",id);
                                    setResult(RESULT_OK,intent);
                                finish();
                            }
                        }).setNegativeButton(android.R.string.no,new DialogInterface.OnClickListener(){
                    @Override
                    public void onClick(DialogInterface dialog,int which){
                        dialog.dismiss();
                    }
                }).create().show();
                break;

        在EditActivity中,我仍然是用菜单的响应事件,根据菜单中的item的id来判定响应事件,利用switch判断触发的事件是不是单个记录的删除。

        确定要删除这条记录时,传mode=2和这条记录的id到MainAcitivty中的接收方法onActivityResult中去。

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data){
        int returnMode;
        long note_id;
        returnMode = data.getExtras().getInt("mode",-1);/*mode,默认是-1,代表什么都不用做*/
        note_id = data.getExtras().getLong("id", 0);/*默认值的意思就是,没找到就是默认值*/

        /*根据返回mode判断,-2代表什么都不做,0代建表新,1或者-1代表修改*/
        if(returnMode == 1 || returnMode == -1) {
            String content = data.getExtras().getString("content");
            String time = data.getExtras().getString("time");
            int tag = data.getExtras().getInt("tag", 1);
            int flag = data.getExtras().getInt("flag");
            String path = data.getExtras().getString("path");

            Note newNote = new Note(content, time, tag, path, flag);
            newNote.setId(note_id);
            CRUD op = new CRUD(context);
            op.open();
            op.updateNote(newNote);  /*修改表中记录*/
            op.close();
        } else if (returnMode == 0) {
            String content = data.getExtras().getString("content");
            String time = data.getExtras().getString("time");
            int tag = data.getExtras().getInt("tag", 1);
            String path = data.getExtras().getString("path");/*没有图片,接收到的是"null"*/
            int flag = data.getExtras().getInt("flag");

            Note newNote = new Note(content, time, tag, path, flag);
            CRUD op = new CRUD(context);
            op.open();
            op.addNote(newNote);     /*添加到表中*/
            op.close();
        }else if(returnMode==2){
            Note curNote=new Note();
            curNote.setId(note_id);
            CRUD op=new CRUD(context);
            op.open();
            op.removeNote(curNote);
            op.close();
        }
        refreshListView();
        super.onActivityResult(requestCode, resultCode, data);/*调用基类的方法*/
    }

        那么MainActivity就对returnmode==2的情况进行操作,对已传回id的那条记录进行数据删除操作。

加密功能实现:

        加密功能的思路:首先我们确定一下加密需要应用到哪个地方,第一,在首页点开加密记录的时候需要一个密码验证;第二,在首页全部删除功能应用的时候应该进行密码验证;第三,在修改密码的时候会验证一下旧密码。

        然后考虑这个功能具体怎么实现,一开始,我想创建两个独立的数据库,创建两个主页面存放两个独立的列表,在主列表里面放不需要加密的记录,在保密界面列表存放需要加密的记录,然后在两个页面的menu上面放一个转换页面的按钮,点击按钮就会跳转页面,在从主页面往保密页面跳转的时候利用一个dialog进行密码输入和判断。但是最后这个想法在我始终捋不清两个数据库和活动的发射之后破产,历时两天挣扎。

        思路更改:我就在edit编辑页面的menu上面设置一个保密状态转换的按钮,就是那个伞型的按钮,点一下就会将正在编辑的记录转为保密状态,在想要点开这条记录的时候就需要在弹出的dialog中输入正确的密码。

        那么我就想到在每条记录中多加入一个属性,用来判断这条记录的保密状态,于是,我定义了一个flag变量,如下面代码所示: 

package com.example.biji;

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

public class NoteDatabase extends SQLiteOpenHelper {

    public static final String TABLE_NAME = "notes";
    public static final String CONTENT = "content";
    public static final String ID = "_id";
    public static final String TIME = "time";
    public static final String MODE = "mode";
    public static final String PATH = "path";
    public static final String FLAG = "flag";

    public NoteDatabase(Context context){
        super(context, TABLE_NAME, null, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase db) { 
        db.execSQL("CREATE TABLE " + TABLE_NAME
                + "("
                + ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
                + CONTENT + " TEXT NOT NULL,"
                + TIME + " TEXT NOT NULL,"
                + PATH + " TEXT NOT NULL,"
                + MODE + " INTEGER DEFAULT 1,"
                + FLAG + " INTEGER NOT NULL);"
        );
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

        其实这里flag应该设置为布尔值的,但是搞成数值也不大错,,,没有默认值。

        来看看具体实现的代码:

@Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        switch (parent.getId()) {   /*第几个记录被点击,对应id,然后对应该记录*/
            case R.id.lv:
                Note curNote = (Note) parent.getItemAtPosition((position));
                if(curNote.getFlag()==0){
                    Intent intent = new Intent(MainActivity.this, EditActivity.class);
                    intent.putExtra("content", curNote.getContent());
                    intent.putExtra("id", curNote.getId());
                    intent.putExtra("time", curNote.getTime());
                    intent.putExtra("mode", 3);            /*代表已经点击过了,与EditActivity接受对应*/
                    intent.putExtra("tag", curNote.getTag());
                    intent.putExtra("path", curNote.getPath());
                    intent.putExtra("flag", 0);
                    startActivityForResult(intent, 1);     /*开始跳转*/
                }
                else if(curNote.getFlag() == 1) {
                    final EditText et = new EditText(this);
                    new AlertDialog.Builder(MainActivity.this)
                            .setMessage("这是个私密记录哦,你要查看吗,赶紧输入我们的暗号")
                            .setIcon(android.R.drawable.sym_def_app_icon)
                            .setView(et)
                            .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    if(et.getText().toString().equals(code)){
                                        Intent intent = new Intent(MainActivity.this, EditActivity.class);
                                        intent.putExtra("content", curNote.getContent());
                                        intent.putExtra("id", curNote.getId());
                                        intent.putExtra("time", curNote.getTime());
                                        intent.putExtra("mode", 3);            //代表已经点击过了,与EditActivity接受对应*/
                                        intent.putExtra("tag", curNote.getTag());
                                        intent.putExtra("path", curNote.getPath());
                                        intent.putExtra("flag",1);
                                        startActivityForResult(intent, 1);     //开始跳转
                                    }
                                    else{
                                        new AlertDialog.Builder(MainActivity.this)
                                        .setMessage("你的暗号不对呦。");
                                    }
                                }
                            }).setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {dialog.dismiss();}
                    }).create().show();
                }
                break;
        }
    }

        这里我用了onItemClick点击事件来判断点击的是第几条记录,然后获取这条记录的flag值,如果flag值为零,那么就用intent.putExtra先EditActivity传值,重要的是mode值为3,表示这次是已经创建过了的记录的再次打开,且flag仍为0,进行跳转到edit页面。

        如果这条记录已经为改变成保密状态了,那么flag==1,验证输入密码的正确性,如果正确,就进行传值与跳转,但是flag传过去的值就为1。如果密码错误,就弹出一个dialog提醒一下密码错误。

case R.id.secret:
                new AlertDialog.Builder(EditActivity.this)
                        .setMessage("每点一下会改变这条笔记的保密状态哦!")
                        .setPositiveButton(android.R.string.yes,new DialogInterface.OnClickListener(){
                            @Override
                            public void onClick(DialogInterface dialog,int which){
                                if(old_Flag == 1) {
                                    old_Flag = 0;
                                }
                                else {
                                    old_Flag = 1;
                                }
                            }
                        }).setNegativeButton(android.R.string.no,new DialogInterface.OnClickListener(){
                    @Override
                    public void onClick(DialogInterface dialog,int which){
                        dialog.dismiss();
                    }
                }).create().show();
                break;

        这一段就是在edit页面完成的一个保密状态的转换,没有太过优化,主要就是改变一下这条记录的flag值,是1就变0,是0就变1。没什么值得说的。

        加密差不多主要就是这些了,下一个。

弹出菜单

        首先在MainActivity里面声明了很多组件工具,参考了别人的代码结构。

//弹出菜单
    private PopupWindow popupWindow;
    private PopupWindow popupCover;
    private ViewGroup customView;
    private RelativeLayout main;
    private ViewGroup coverView;
    private LayoutInflater layoutInflater;
    private WindowManager wm;
    private DisplayMetrics metrics;

        然后是一大堆我看不懂的渲染代码,

//渲染
    private void initPopUpView(){
        layoutInflater=(LayoutInflater)MainActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        customView=(ViewGroup)layoutInflater.inflate(R.layout.setting_layout,null);
        coverView=(ViewGroup)layoutInflater.inflate(R.layout.setting_cover,null);
        main=findViewById(R.id.main_layout);
        wm=getWindowManager();
        metrics=new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(metrics);
    }

        这里要注意,一共是有两层layout弹出了一个customView,一个coverView,第一层就是我们需要的弹出的菜单,第二层是一个衬托第一层的蒙版。

241​ customView

 

​ coverView蒙版

         再来一个经典的showPopUpView将主弹窗的宽度设置为屏幕的70%,营造弹窗的效果,让主弹窗在蒙版的上面防止蒙版遮挡,将id为setting_img和setting_text的组件设置点击事件,调用我设置的dialog方法。

    public void showPopUpView(){
        int width=metrics.widthPixels;
        int height=metrics.heightPixels;
        popupCover =new PopupWindow(coverView,width,height,false);
        popupWindow=new PopupWindow(customView,(int)(width*0.7),height,true );
        popupWindow.setBackgroundDrawable(new ColorDrawable(Color.WHITE));
                //在主界面加载成功后,显示弹出
        findViewById(R.id.main_layout).post((new Runnable() {
            @Override
            public void run() {
                popupCover.showAtLocation(main, Gravity.NO_GRAVITY,0,0);
                popupWindow.showAtLocation(main,Gravity.NO_GRAVITY,0,0);
                codeset = customView.findViewById(R.id.setting_img);
                setting_text=customView.findViewById(R.id.setting_text);
                codeset.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        dialog();
                    }
                });
                setting_text.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        dialog();
                    }
                });
                coverView.setOnTouchListener(new View.OnTouchListener(){
                    @Override
                    public boolean onTouch(View v, MotionEvent event){
                        popupWindow.dismiss();
                        return true;
                    }
                });
                popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
                    @Override
                    public void onDismiss() {
                        popupCover.dismiss();
                    }
                });
            }
        }));
    }

         这个dialog方法就是用来更改密码的,可以输入old_code和new_code,如果旧密码验证正确,那么就改变code,调用success方法来提醒修改成功,验证失败会调用fail方法提醒密码错误。

private void dialog(){
        LayoutInflater factory = LayoutInflater.from(this);
        final View textEntryView = factory.inflate(R.layout.dialog_code, null);
        final EditText old_code = (EditText) textEntryView.findViewById(R.id.old_code);
        final EditText new_code = (EditText)textEntryView.findViewById(R.id.new_code);
        AlertDialog.Builder ad1 = new AlertDialog.Builder(MainActivity.this);
        ad1.setTitle("更改密码,初始密码是0000哦");
        ad1.setIcon(android.R.drawable.ic_dialog_info);
        ad1.setView(textEntryView);
        ad1.setPositiveButton("是", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                if(old_code.getText().toString().equals(code)){
                    code=new_code.getText().toString();
                    success();
                }
                else{
                    fail();
                }
            }
        }).setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        }).create().show();
    }

 


         好了都说完了,总结一下,这个考核项目我们小组除去地理位置获取(那个授权出了问题,还在解决)一共用时5天完成要求的功能,其余时间都用来学JavaScript,,,简简单单,也都是从零开始学安卓,靠着一点Java基础,一点点吃透别人的代码,转化成自己的代码。

        完结撒花。