利用MVC编程模式-开发一个简易记事本app

  学了极客学院一个开发记事本的课程,利用自己对MVC编程模式的简单理解重写了一遍该app。

  github地址:https://github.com/morningsky/MyNote

MVC即,模型(model)-视图(view)-控制器(controller),有效的实现了数据-业务逻辑-视图显示的代码分离,使得加入新功能时不需要重新编写业务逻辑,大大提高了代码的可维护性。

  利用MVC编程模式-开发一个简易记事本app

  

   在这个案列中,一开始只是开发了添加文字内容的记事功能,添加图片功能时在activity文件中写入imageview的逻辑 在数据库中加入图片路径数据 在视图中加一个imageview的。后期若再添加视频功能可参照之前添加图片的操作快速实现app的升级。整个代码编写过程脉络清晰,加上Android Studio的帅气主题,开发过程感觉极好。

  

下面是整个app的开发流程:

/*步骤:
1.model构建
  1.1创建数据库 NoteDB类
  1.2创建自定义的adapter MyAdapter类
    1.2.1构造函数
    1.2.2复写4个子类方法 注意getView方法

2.创建视图
  2.1布局主界面 两个按钮 一个listview activity_main.xml
  2.2 listview每一条数据的视图格式 图片imageview 内容textview 时间textview cell.xml
  2.3添加内容界面 imageview editext 两个Button addcontent.xml
  2.4创建详情页视图 与addcontent视图相似 将Editext转换为Textview Button的内容由返回变成删除 incontent.xml

3.逻辑实现
  MainActivity:
    3.1初始化主界面布局 定义initView方法 给按钮设置监听
    3.7在MainActivity实例化一个SQLiteDatabase 获取读取权限 用于加载listview的内容
    3.8添加查询数据方法selectDB 并在该方法中加载MyAdapter
  

  AddContent:
    3.2创建添加内容界面的activity 并在AndroidManifest文件中注册该activity 两个activity添加固定竖屏参数
    3.3初始化AddContent界面布局 定义initView方法 给按钮设置监听 实例化SQLiteDatabase 获取写入数据权限
    3.4添加addDB方法获取内容 时间并写入数据库
    3.5添加getTime方法获取系统当前时间
    3.6为按钮添加事件
    3.9增加根据添加文字还是图文加载不同界面的initView逻辑
    4.0添加Intent调用系统相机 实例化一个File存放照片路径
    4.1复写onActivityResult来查看照片效果
    4.2add函数添加图片路径

  MyAdapter:
    4.3添加查看缩略图函数getImageThumbnail listview中显示
    4.5添加用来查询的String path 储存地址

  InContent:
    4.6添加详情页Activity 并注册
    4.7给listview添加监听事件 跳转到详情页 并传入部分数据
    4.8根据图文还是文字加载不同视图 显示文字 图片信息
    4.9实例化一个SQLiteDatabase 获取写入数据权限 用来删除数据
    5.0添加删除数据方法delDB 给按钮加上方法
*/

  

  model层:

    NoteDB.java 创建了一个数据库 用来存放记事内容 记事时间 图片路径

    

 package com.bluesky.mynote;

 import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; /**
* Created by 清晨 on 2015/5/6.
*/
public class NoteDB 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 PATH="path"; //路径,用来存放照片路径 //构造函数参数保留一个Content即可
public NoteDB(Context context) {
super(context, "notes", null, 1);
} //注意属性内的空格 " TEXT NOT NULL,"第一个引号后的空格不能省略 否则名称会变为contentTEXT
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + TABLE_NAME + " ("
+ ID+ " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ CONTENT+" TEXT NOT NULL,"
+ PATH +" TEXT NOT NULL,"
+ TIME +" TEXT NOT NULL)");
} //不需要更新
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { }
}

  MyAdapter.java 用来设定主界面listview的内容格式

 package com.bluesky.mynote;

 import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.ThumbnailUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView; /**
* Created by 清晨 on 2015/5/7.
*/
public class MyAdapter extends BaseAdapter {
private Context mContext;
private Cursor mCursor;
private LinearLayout layout; public MyAdapter(Context context,Cursor cursor){
mContext=context;
mCursor=cursor;
}
@Override
public int getCount() {
return mCursor.getCount();
} @Override
public Object getItem(int position) {
return mCursor.getPosition();
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater=LayoutInflater.from(mContext);//加载视图权限
layout= (LinearLayout) inflater.inflate(R.layout.cell,null);//加载视图
//初始化控件
TextView content_tv= (TextView) layout.findViewById(R.id.list_content);
TextView time_tv= (TextView) layout.findViewById(R.id.list_time);
ImageView img_iv= (ImageView) layout.findViewById(R.id.list_img);
//查询mCursor 用String获取查询内容
mCursor.moveToPosition(position);
String content=mCursor.getString(mCursor.getColumnIndex("content"));
String time=mCursor.getString(mCursor.getColumnIndex("time"));
String url=mCursor.getString(mCursor.getColumnIndex("path"));
content_tv.setText(content);
time_tv.setText(time);
img_iv.setImageBitmap(getImageThumbnail(url,200,200));
return layout;
} //获取缩略图
public Bitmap getImageThumbnail(String uri,int width,int height){
Bitmap bitmap=null;
BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds=true;
bitmap=BitmapFactory.decodeFile(uri,options);
options.inJustDecodeBounds=false;
int beWidth=options.outWidth/width;
int beHeight=options.outHeight/height;
int be=1;
//防止图片超出过大或过小不予缩小
if(beWidth<beHeight){
be=beWidth;
}else {
be=beHeight;
}
if(be<=0){
be=1;
}
options.inSampleSize=be;
bitmap=BitmapFactory.decodeFile(uri,options);
bitmap=ThumbnailUtils.extractThumbnail(bitmap,width,height,ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
return bitmap;
}
}

  视图层(View):

利用MVC编程模式-开发一个简易记事本app            利用MVC编程模式-开发一个简易记事本app            利用MVC编程模式-开发一个简易记事本app

分别是主界面 activity_main.xml    添加内容addcontent.xml  内容详情页incontent.xml

内容详情页与添加内容界面 基本相似 所以可实现代码的简单修改 将编辑框改为文本框 再修改相应ID即可

接下来是核心部分

  控制器(Controler):

    主activity:

 package com.bluesky.mynote;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView; public class MainActivity extends ActionBarActivity implements View.OnClickListener {
private Button text_btn, img_btn;
private ListView lv;
private Intent i;
private MyAdapter adapter;
private NoteDB noteDB;
private SQLiteDatabase dbReader;
private Cursor cursor; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
//给按钮加入监听事件
text_btn.setOnClickListener(this);
img_btn.setOnClickListener(this);
noteDB = new NoteDB(this);
//获取读取权限 用于加载listview的内容
dbReader = noteDB.getReadableDatabase();
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
cursor.moveToPosition(position);//游标挪到了position的位置上
Intent i=new Intent(MainActivity.this,InContent.class);
i.putExtra(NoteDB.ID,cursor.getInt(cursor.getColumnIndex(NoteDB.ID)));//以便根据ID删除数据
i.putExtra(NoteDB.CONTENT,cursor.getString(cursor.getColumnIndex(NoteDB.CONTENT)));
i.putExtra(NoteDB.TIME,cursor.getString(cursor.getColumnIndex(NoteDB.TIME)));
i.putExtra(NoteDB.PATH,cursor.getString(cursor.getColumnIndex(NoteDB.PATH)));
startActivity(i);
}
}); } //初始化控件
public void initView() {
lv = (ListView) findViewById(R.id.list);
text_btn = (Button) findViewById(R.id.text);
img_btn = (Button) findViewById(R.id.image);
} //查询数据
public void selectDB() {
cursor = dbReader.query(NoteDB.TABLE_NAME,null,null,null,null,null,null,null);
adapter = new MyAdapter(this,cursor);
lv.setAdapter(adapter);
} @Override
public void onClick(View v) {
i = new Intent(this, AddContent.class);
switch (v.getId()) {
case R.id.text:
i.putExtra("flag", "1");
startActivity(i);
break;
case R.id.image:
i.putExtra("flag", "2");
startActivity(i);
break;
}
} @Override
protected void onResume() {
super.onResume();
selectDB();
}
}

添加内容 activity

  

 package com.bluesky.mynote;

 import android.app.Activity;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.PersistableBundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.VideoView; import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date; /**
* Created by 清晨 on 2015/5/6.
*/
public class AddContent extends Activity implements View.OnClickListener {
private NoteDB noteDB;
private SQLiteDatabase dbWriter;
private String flag; //接受从mainactivity传来的标识 用于判定加载不同的添加内容界面(图文或者纯文字)
private EditText editText;
private Button save_btn,cancel_btn;
private ImageView c_img;
private File imgfile;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.addcontent);
flag=getIntent().getStringExtra("flag");
initView();
save_btn.setOnClickListener(this);
cancel_btn.setOnClickListener(this);
noteDB=new NoteDB(this);
dbWriter=noteDB.getWritableDatabase();//获取写入数据库权限
} //初始化控件
public void initView(){
editText= (EditText) findViewById(R.id.ettext);
save_btn= (Button) findViewById(R.id.save);
cancel_btn= (Button) findViewById(R.id.cancel);
c_img= (ImageView) findViewById(R.id.c_img);
if(flag.equals("1")){
c_img.setVisibility(View.GONE);//隐藏imageview
}
if(flag.equals("2")){
c_img.setVisibility(View.VISIBLE);//显示imageview
//启动系统相机拍照
Intent getImg=new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//图片是放在存储卡中 路径存在数据库中 以时间命名图片 避免重名
imgfile=new File(Environment.getExternalStorageDirectory()
.getAbsolutePath()+"/"+getTime()+".jpg");
getImg.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(imgfile));
startActivityForResult(getImg,1);//便于立即查看效果 }
} //获取内容并写入数据库
public void addDB(){
ContentValues cv=new ContentValues();
cv.put(NoteDB.CONTENT,editText.getText().toString());
cv.put(NoteDB.TIME,getTime());
cv.put(NoteDB.PATH,imgfile + "");
dbWriter.insert(NoteDB.TABLE_NAME,null,cv);
} //获取系统当前时间
public String getTime(){
SimpleDateFormat format=new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
Date curDate=new Date();
String str=format.format(curDate);
return str;
} @Override
public void onClick(View v) {
switch (v.getId()){
case R.id.save:
addDB();
finish();
break;
case R.id.cancel:
finish();
break; } } //预览显示拍摄内容
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode==1){
Bitmap bitmap= BitmapFactory.decodeFile(imgfile.getAbsolutePath());
c_img.setImageBitmap(bitmap);
}
}
}

内容详情页Activity

 package com.bluesky.mynote;

 import android.app.Activity;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView; /**
* Created by 清晨 on 2015/5/8.
*/
public class InContent extends Activity implements View.OnClickListener {
private Button del_btn;
private Button back_btn;
private ImageView in_img;
private TextView in_tv;
private NoteDB noteDB;
private SQLiteDatabase dbWriter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.incontent);
initView();
noteDB= new NoteDB(this);
dbWriter=noteDB.getWritableDatabase();
del_btn.setOnClickListener(this);
back_btn.setOnClickListener(this);
//根据记事方式加载不同视图
if(getIntent().getStringExtra(NoteDB.PATH).equals("null")){
in_img.setVisibility(View.GONE);
}else {
in_img.setVisibility(View.VISIBLE);
}
//显示文字
in_tv.setText(getIntent().getStringExtra(NoteDB.CONTENT));
//显示图片
Bitmap bitmap= BitmapFactory.decodeFile(getIntent().getStringExtra(NoteDB.PATH));
in_img.setImageBitmap(bitmap);
} public void initView(){
del_btn= (Button) findViewById(R.id.delete);
back_btn= (Button) findViewById(R.id.back);
in_img= (ImageView) findViewById(R.id.in_img);
in_tv= (TextView) findViewById(R.id.in_tv);
} @Override
public void onClick(View v) {
switch (v.getId()){
case R.id.delete:
delDB();
finish();
break;
case R.id.back:
finish();
break;
}
}
//删除数据
public void delDB(){
dbWriter.delete(NoteDB.TABLE_NAME,"id="+getIntent()
.getIntExtra(NoteDB.ID,0),null);
}
}

  新人一枚,初学安卓,也初次尝试着写博客,暂且把这一路的code time记下来吧.

上一篇:第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法


下一篇:【手把手教程】uniapp + vue 从0搭建仿微信App聊天应用:腾讯云TXIM即时通讯的最佳实践