一、应用程序功能
基本功能:
- 基于 Android 平台实现简单的个人笔记管理的 APP
2. 要求提供登录界面给用户
3. 要求有界面让用户输入笔记内容(包括但不限于以下类型内容:笔记标题, 记录时间,记录地点,内容(可以是文字,支持图片,声音等更好))
4. 笔记可以分类管理(类别可以自定义)
5. 支持按照时间、按照内容进行查询。登陆后首页展示列表,点击列表上的某 一项支持查看笔记明细等
6. 以上内容要求记入 SQLITE 或者文件系统中,APP 重启后不丢失。
拓展功能:
1 、修改登录密码(界面隐蔽,不易被找到)
2 、覆盖自定义了导航栏
3 、支持按照关键字,模糊搜索笔记
4 、支持按照开始结束时间,范围搜索笔记
5 、通过加锁实现私密笔记和普通笔记的分类
6 、可以设置笔记提醒时间,即闹铃
7 、设置了分享按钮
二、程序功能设计
图 2.1 系统功能结构
三、实现原理
(1)登录密码判断
通过一个延时动画,从 Main2Activity 跳转到登录界面(LoginActivity), 在登录界面定义一个 fileIsExists 方法来判断是否设置过密码,方法里通过
File f=new File(getFilesDir ()+"/abc.txt")来读取私有目录下的文件。点击 登录按钮时,调用此方法判断,若文件中不存在已有密码,则设定初始密码为 “0000 ”并写入文件;若文件中已有密码,则与 EditText 中输入的密码进行比 较,相等则跳转到主界面(MainActivity)。若输入密码错误,会进行友好 Toast 提示,显示"密码错误!!!002"+正确密码,这样知道规则的记事本使用者就会 想起所设定的密码。
图 3-1-1 延时动画与简介页面
图 3-1-2 登录界面
(2)对修改记事和新增记事的判断
由于在主界面中,单击已有记事显示明细和新增记事的方法最后都会跳转到 AddActivity ,这时候需要体现出界面的不同来。我是通过 putExtra 方法将键值对 放入 intent 中,定义 update 和 newadd 两个不同的 value,然后跳转时候将 intent 传过去在 AddActivity 中进行判断。在它的 onCreate 方法中调动一个自定义的加 载数据函数 loadData ,来实现对传过来的健进行判断。
部分核心代码如下:
表 3-2 AddActivity.java
/加载数据private void loadData() {/如果是新增记事模式,则将editText清空if (editModel.equals("newAdd")) {et_Notes.setText("");}/如果编辑的是已存在的记事,则将数据库的保存的数据取出,并显示在 EditText中else if (editModel.equals("update")) {tv_title.setText("编辑记事");dop.create_db(); |
---|
Cursor cursor = dop.query_db(item_Id);cursor.moveToFirst();/取出数据库中相应的字段内容context = cursor.getString (cursor.getColumnIndex("context")); datatype =cursor.getString (cursor.getColumnIndex("datatype"));datatime =cursor.getString (cursor.getColumnIndex("datatime"));locktype =cursor.getString (cursor.getColumnIndex("locktype"));lock = cursor.getString (cursor.getColumnIndex("lock")); |
---|
图 3-2 判断新增记事和编辑记事
(3)模糊搜索实现
先通过搜索框的 getText 获得输入的关键字,再用 if 判断输入的是否为空, 若不为空 ,则通过键值对的形式把关键字放入 intent 中进行跳转 ,跳转到 SearchActivity 。在 SearchActivity 中定义一个 showNotesList 的方法,通过调用数 据库操作类 DatabaseOperation 中定义的 query_db(keword)方法进行模糊查询, 最后通过实例化自定义的适配器方法将搜索到的笔记信息动态装载到首页。
部分核心代码如下:
表 3-3 SearchActivity.java
// 显示记事列表private void showNotesList() {/打开数据库dop.create_db();Cursor cursor = dop.query_db(keword);/%模糊查询if (cursor.getCount() > 0) {List<SQLBean> list = new ArrayList<SQLBean>();/实例化数据列表while (cursor.moveToNext()) {/光标移动成功SQLBean bean = new SQLBean ();/创建数据库实体类 /保存日记信息id到实体类bean.set_id("" +cursor.getInt(cursor.getColumnIndex("_id")));/保存日记内容到实体类bean.setContext(cursor.getString (cursor.getColumnIndex("context")));/保存日记标题到实体类bean.setTitle(cursor.getString (cursor.getColumnIndex("title")));/保存日记记录时间到实体类bean.setTime(cursor.getString (cursor.getColumnIndex("time")));/保存日记是否设置提醒时间到实体类bean.setDatatype(cursor.getString (cursor.getColumnIndex("datatype")));/保存日记提醒时间到实体类bean.setDatatime(cursor.getString (cursor.getColumnIndex("datatime")));/保存日记是否设置了日记锁到实体类bean.setLocktype(cursor.getString (cursor.getColumnIndex("locktype")));/保存日记锁秘密到实体类bean.setLock(cursor.getString (cursor.getColumnIndex("lock")));/把保存日记信息实体类保存到日记信息集合里list.add(bean);} |
---|
/倒序显示数据Collections.reverse(list);/装载日记信息到首页MainAdapter adapter = new MainAdapter (list, this);/日记列表设置日记信息适配器lv_notes.setAdapter (adapter);}else{Toast.makeText(SearchActivity.this, "暂无记事!",Toast.LENGTH LONG).show();}/关闭数据库dop.close_db();} |
---|
图 3-3 模糊搜索
(4) 日期范围搜索实现
与模糊搜索类似,不过 SQL 判断的时候需要注意,要在月份前补 0,否则语句会 出错。然后也是带参数跳转到专门的一个处理类 DataSearchActivity,与前面 的模糊查询不同之处在于 ,这里调用的是另一个自定义的数据库操作类 query_db(startData, endData)
部分核心代码如下
表 3-4 MainActivity.java
// 日期范围搜索public void onData(View v) {// 最后一个 false 表示不显示日期,如果要显示日期,最后参数可以是 true 或者不用输入Calendar c = Calendar.getInstance();new DoubleDatePickerDialog (MainActivity.this, 0,new DoubleDatePickerDialog.OnDateSetListener () { @Overridepublic void onDateSet(DatePicker startDatePicker,in t startYear, in tstartMonthOfYear,in t startDayOfMonth, DatePickerendDatePicker,int endYear, int endMonthOfYear,int endDayOfMonth) {if (startYear < endYear || startYear == endYear && startMonthOfYear <= endMonthOfYear) {in t st = startMonthOfYear + 1;in t et = endMonthOfYear + 1;Intent intent = new Intent(MainActivity.this, DataSearchActivity.class);//跳转到日期搜索// sql 判断 需要在月份前补 0 否则 sql 语句判断 不正确。if (st < 10) {intent.putExtra("startData", startYear +"-0"+ st + "-" + "01");} else {intent.putExtra("startData", startYear +" "-+ st + "-" + "01");}if (et < 10) {intent.putExtra("endData", endYear + "-0" + et |
---|
+ "-" + "30");} else {intent.putExtra("endData", endYear + "-" + et+ "-" + "30");}startActivity (intent);} else {Toast.makeText(MainActivity.this, " 日期选择错误请重新选择!",Toast.LENGTH_LONG).show(); }}}, c.get(Calendar.YEAR), c.get(Calendar.MONTH), c .get(Calendar.DATE), false).show();} |
---|
(5)长按弹出框功能实现
在主界面(MainActivity)、模糊搜索展示界面(SearchActivity)和日期搜索展示 界面(DataSearchActivity)中都重写了 onStart 方法,里面加入了显示记事明细 方法、为单击记事和长按记事添加了监听器,然后在弹出的对话框中所选会被 switch 语句所匹配,再进行相应的操作。
部分核心代码如下:
表 3-5-1 onStart 方法的重写
@Overrideprotected void onStart() {/ TODO Auto-generated method stubsuper.onStart();/显示记事列表showNotesList();/为记事列表添加监听器lv_notes.setOnItemClickListener (new ItemClickEvent());/为记事列表添加长按事件lv_notes.setOnItemLongClickListener (new ItemLongClickEvent()); } |
---|
表 3-5-2 单击与长按监听器
/记事列表单击监听器class ItemClickEvent implements OnItemClickListener {@Overridepublic void onItemClick(AdapterView<?> parent, View view, in t position,long id) {tv_note_id = (TextView) view.findViewById(R.id.tv noteid);tv_locktype = (TextView) view.findViewById(R.id.tv locktype); tv_lock = (TextView) view.findViewById(R.id.tv lock);String locktype = tv_locktype.getText().toString (); String lock = tv_lock.getText().toString ();in t item_id = Integer.parseInt(tv_note_id.getText().toString ()); if ("0".equals(locktype)) {/如果没加锁Intent intent = new Intent(MainActivity.this,AddActivity.class);intent.putExtra("editModel", "update");intent.putExtra("noteId", item_id);startActivity (intent);} else {inputTitleDialog (lock, 0, item_id); |
---|
}}}/记事列表长按监听器class ItemLongClickEvent implements OnItemLongClickListener {@Overridepublic boolean onItemLongClick(AdapterView<?> parent, View view, in t position, long id) {/初始化日记id保存控件tv_note_id = (TextView) view.findViewById(R.id.tv noteid);/初始化是否添加日记锁保存控件tv_locktype = (TextView) view.findViewById(R.id.tv locktype);/初始化日记锁秘密保存信息tv_lock = (TextView) view.findViewById(R.id.tv lock);/获取控件上是否设置日记锁信息String locktype = tv_locktype.getText().toString ();/获取控件上日记密码信息String lock = tv_lock.getText().toString ();/获取控件上id信息转换成int类型in t item_id = Integer.parseInt(tv_note_id.getText().toString ());/弹出选择操作框方法simpleList(item_id, locktype, lock);return true;}} |
---|
表 3-5-3 弹出对话框
/简单列表对话框,用于选择操作public void simpleList(final in t item_id, final String locktype, final String lock) {/实例化AlertDialogAlertDialog.Builder alertDialogBuilder = newAlertDialog.Builder (this,R.style.custom dialog);/设置弹窗标题alertDialogBuilder.setTitle("选择操作");/设置弹窗图片alertDialogBuilder.setIcon (R.mipmap.ic launcher);/设置弹窗选项内容alertDialogBuilder.setItems(R.array.itemOperation,new android.content.DialogInterface.OnClickListener () {@Overridepublic void onClick(DialogInterface dialog, in t which) { |
---|
switch (which) {/编辑case 0:if ("0".equals(locktype)) {/判断是否添加了秘密锁0没有Intent intent = new Intent(MainActivity.this, AddActivity.class);/跳转到添加日记页intent.putExtra("editModel", "update");/传递编辑信息intent.putExtra("noteId", item_id);/传递id信息startActivity (intent);/开始跳转 } else {/有秘密锁/弹出输入密码框inputTitleDialog (lock, 0, item_id); }break;/删除case 1:if ("0".equals(locktype)) {/判断是否是加密日记0没有dop.create_db();/打开数据库dop.delete_db(item_id);/删除数据 dop.close_db();/关闭数据库/刷新列表显示lv_notes.invalidate();showNotesList();} else {/有秘密锁/弹出输入密码框inputTitleDialog (lock, 1, item_id);/刷新列表显示lv_notes.invalidate();/显示日记列表信息showNotesList();}break;}}});alertDialogBuilder.create();/创造弹窗alertDialogBuilder.show();/显示弹窗} |
---|
图 3-5 长按实现弹出框
(6)闹钟提醒的实现
在 AddActivity 中定义字符串 datatype 代表是否设置了闹钟,字符串 datatime 代表提醒时间,并定义闹钟设置方法,并判断之前有没有设置过闹钟提醒。若没 有,则先通过 DataFormat 格式化当前时间,并显示,然后把保存是否设置闹钟 的变量 datatime 设置为 1。这样当编辑此次记事结束时候,点击右上角√按钮 时 候 , 调 用 自 定 义 数据 库操 作类 方 法 update_db(title, context, time, datatype, datatime,locktype, lock, item_Id)就可以保存状态到数据库中。
还有一点要注意,不然做出的闹钟会有问题,那就是在最初的主界面重写的 onCreate 方法中要考虑闹钟。即需要通过 AlarmManager 先获取系统服务,然后 跳转到 CallAlarm 判断是否已经到了闹钟提醒时间,若是则将参数 shake 和 ring 设置为 true,传参跳转到闹钟具体实现类 AlarmAlert 从而弹出闹钟,播放声音 并震动(虽然虚拟机不会震动哈哈)
部分核心代码:
表 3-6 MainActivity 中重写的 onCreate 方法
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);bt_add = (Button) findViewById(R.id.btad);bt_add.setOnClickListener (new ClickEvent());et_keyword = (EditText) findViewById(R.id.et keyword);/数据库操作dop = new DatabaseOperation (this, db);lv_notes = (MyGridView) findViewById(http://R.id.lv notes);if (am == null) {am = (AlarmManager) getSystemService(ALARM SERVICE);/通过系统服 务获取}try {Intent intent = new Intent(MainActivity.this,CallAlarm.class);/onCreate方法中先判断闹钟PendingIntent sender = PendingIntent.getBroadcast( MainActivity.this, 0, intent, 0);am.setRepeating (AlarmManager.RTC WAKEUP, 0, 60 * 1000, sender);} catch (Exception e) {e.printStackTrace();}} |
---|
图 3-6- 1 设定闹钟及闹钟图标的显示
图 3-6-2 闹钟显示及闹钟图标的消失
(7)记事加密实现
与闹钟的定义类似,通过定义 locktype代表是否加密,lock 代表密码。在点击加 密锁的图标后,判断 locktype 的值,来判断是否已经设置了密码。若值为 0 则代 表未设置,调用自定义的 inputlockDialog 方法弹出密码设置窗口,然后把 locktype 设为 1 ,把锁的图标换成上锁的图案;若 locktype 为 1 ,即代表已设置过密码, 调用自定义的 inputunlockDialog 方法弹出是否取消密码的窗口,监听器判断若点 击的是确认,则将 locktype 和 lock 重置为 0 ,再将锁的图标换成未加锁的图片。 在自定义的适配器 MainAdapter 重写的 getView 函数中调用模型层的 getLocktype 获取判断是否上锁,若不上锁则正常显示。若上锁,则主界面不显示记事的 title, 并将记事的背景设为一个大大锁的图案。
核心代码分析:
表 3-7-1 设置密码弹窗
/设置密码弹窗private void inputlockDialog () {final EditText inputServer = new EditText(this);/创建EditText输 入框inputServer.setInputType(InputType.TYPE CLASS TEXT| InputType.TYPE TEXT VARIATION PASSWORD);/设置输入框类 型inputServer.setFocusable(true);/获取焦点AlertDialog.Builder builder = new AlertDialog.Builder (this);/创建 弹出框builder.setTitle("设置密码").setView(inputServer).setNegativeButton ("取消", null);/在弹窗上设置标题添加输入框builder.setPositiveButton ("确认", newDialogInterface.OnClickListener () {public void onClick(DialogInterface dialog, in t which) {/设置 确认按钮String inputName = inputServer.getText().toString ();if ("".equals(inputName)) {/判断输入框内容是否为空Toast.makeText(AddActivity.this, "密码不能为空 请重新输入!",Toast.LENGTH LONG).show();} else {/输入框内容不为空lock = inputName;/密码locktype = "1";/添加了密码锁ib_lk.setBackgroundResource(R.drawable.locky);/设置添加锁图案Toast.makeText(AddActivity.this, "密码设置成功!", Toast.LENGTH LONG).show();} |
---|
}});builder.show();/弹出设置密码弹窗} |
---|
表 3-7-2 getView 中的判断
@Overridepublic View getView(final in t position, View convertView, ViewGroup parent) {Holder holder;if (convertView == null) {holder = new Holder ();convertView = inflater.inflate(R.layout.note item, null); holder.tv_note_id = (TextView) convertView.findViewById(http://R.id.tv noteid);holder.tv_locktype= (TextView) convertView.findViewById(http://R.id.tv locktype);holder.tv_lock= (TextView) convertView.findViewById(http://R.id.tv lock);holder.tv_note_title = (TextView) convertView.findViewById(http://R.id.tv note title);holder.tv_note_time = (TextView) convertView.findViewById(http://R.id.tv note time);holder.iv_img e = (ImageView)convertView.findViewById(R.id.iv imge);holder.im_datatime = (ImageView) convertView.findViewById(http://R.id.im datatime);holder.ll_bg = (RelativeLayout) convertView.findViewById(R.id.ll bg);convertView.setTag (holder);} else {holder = (Holder) convertView.getTag ();}dm = context.getResources().getDisplayMetrics();/获得系统屏幕信息 LinearLayout.LayoutParams imagebtn_params = newLinearLayout.LayoutParams(WindowManager.LayoutParams.WRAP CONTENT,WindowManager.LayoutParams.WRAP CONTENT);imagebtn_params.width = dm.widthPixels / 2;/imagebtn_params.height = dm.widthPixels / 2;/holder.ll_bg.setLayoutParams(imagebtn_params);holder.tv_note_id.setText(list.get(position).get_id());holder.tv_note_title.setText(list.get(position).getTitle()); |
---|
holder.tv_note_time.setText(list.get(position).getTime());holder.tv_locktype.setText(list.get(position).getLocktype()); holder.tv_lock.setText(list.get(position).getLock());if (list.get(position).getDatatype().equals("0")) {/判断是否开启提 醒功能holder.im_datatime.setVisibility (View.GONE);} else {holder.im_datatime.setVisibility (View.VISIBLE);}if ("0".equals(list.get(position).getLocktype())) {/判断是否上锁 String context = list.get(position).getContext();/定义正则表达式,用于匹配路径Pattern p = Pattern.compile("/([^\\.]*)\\.\\w{3}"); Matcher m = p.matcher (context);while (m.find()) {String path = m.group ().toString ();String type = path.substring (path.length() - 3, path.length());Bitmap bm = null;Bitmap rbm = null;if (holder.iv_img e.getDrawable() == null) {FileInputStream f;try {f = new FileInputStream(m.group ());BitmapFactory.Options options = newBitmapFactory.Options();options.inJustDecodeBounds = true;Bitmap bmp = BitmapFactory.decodeFile(m.group (), options);options.inSampleSize = options.outHeight / 100;options.inJustDecodeBounds = false;options.inDither = false;options.inPreferredConfig = null;options.inPurgeable = true;options.inInputShareable = true;bm = BitmapFactory.decodeFile(m.group (), options); } catch (FileNotFoundException e) {/ TODO Auto-generated catch blocke.printStackTrace();}holder.iv_img e.setVisibility (View.VISIBLE); holder.tv_note_title.setLines(1);/一行holder.iv_img e.setImageBitmap (bm); |
---|
}}} else {/若上锁holder.tv_note_title.setVisibility (View.GONE);holder.iv_img e.setVisibility (View.VISIBLE);holder.iv_img e.setBackgroundResource(R.drawable.lock bg); }return convertView;} |
---|
图 3-7-1 设置记事密码
图 3-7-2 加锁后主界面的变化
(8)selector 选择器
通过 Xml 方式实现状态选择器 ,然后将写好的 selector 选择器存在放 res - drawable 文件下。
部分核心代码:
表 3-7 navigationbar_setting.xml
//设置是否获得焦点状态,true 表示获得焦点,默认为 false ,表示未获得焦点 android:state_focused="true"android:drawable="@drawable/navigationbar_setting_pressed"//设置是否按压状态,一般在 true 时设置该属性,表示已按压状态,默认为 false android:state_pressed="true"android:drawable="@drawable/navigationbar_setting_default" |
---|
(9)修改密码实现
在简介界面,点击隐蔽在右上角的灰色齿轮图标,即可进入修改密码的界面。
核心代码实现:
表 3-9-1 修改密码
public void onOk(View view){EditText oldT = (EditText)findViewById(R.id.editText3); EditText newT1 = (EditText)findViewById(R.id.editText4); EditText newT2 = (EditText)findViewById(R.id.editText5);String old = oldT.getText().toString ();String new1 = newT1.getText().toString ();String new2 = newT2.getText().toString ();String yuan = getPass();if(yuan.equals(old)){if(new1.equals(new2)){writePass(new1);/单独写出来Toast.makeText(XiugaimimaActivity.this,"密码修改成功 新密 码:"+new1,Toast.LENGTH LONG).show();XiugaimimaActivity.this.finish();}elseToast.makeText(XiugaimimaActivity.this,"两次密码不同 ",Toast.LENGTH LONG).show();}else{Toast.makeText(XiugaimimaActivity.this,"原始密码错误 ",Toast.LENGTH LONG).show();}} |
---|
表 3-9-2 writePass 方法
public void writePass(String str) {String mi = str;File appDir = getFilesDir ();File f = new File(appDir, "abc.txt");FileOutputStream fstream = null;try {fstream = new FileOutputStream(f);fstream.write(mi.getBytes("UTF-8"));} catch (Exception e) {} finally { |
---|
try {fstream.close();} catch (Exception e2) {}}} |
---|
四、课程总结
时间过得真的很快,转眼间一学期的安卓学习已告一段落。这是门很有趣的 课,才接触到就很喜欢,用的语言也是熟悉的 java,而且能在虚拟机上看见自己 写的程序跑起来,甚至连接真机测试运行,这是以前想都不敢想的。
不过从一开始安装软件就不太顺利,看到 C 盘由于默认路径被塞得满满的, 就想把路径改到机械去,网上相关的博客倒是不少,不过介绍的版本都有点旧, 捣鼓半天还是给 AS 整崩掉,前前后后大概安装了 7 、8 次 AS ,最后一次安装熟 练的让人心痛哈哈,其实增加一个系统环境变量,几个 G 的avd 就会默认安装在 别的盘,Gradle 和 m2 也算是两个大头,转移的话不是不可以,不过后期跑的时 候还是小毛病不断,所以还是老实点吧。一开始初始化 build 因为被墙,也是转 圈圈好久,最怕的是转到最后还报错,也试过网上找免费的梯子,不过也不是很 好 用 。 最 让 我 惊 喜 的 是 偶 然 找 到 的 阿 里 云 仓 库 镜 像 , 只 要 在 gradle-wrapper.properties 中配置一下,绿色圈圈转的嗖嗖的。
再说说学习历程吧,一开始自己挺瓜皮的,不是犯方法的嵌套定义错误,就 是代码放的位置不对劲,老师也很耐心地给我一一指出这些低级错误,哪怕已经 下课也会再待一会来给我们解答。课后的话,边看 PPT 边回忆老师讲的这个方法 怎么调,这个函数应该放在什么前面,以及有关生命周期的理解。慢慢的摸到了 门路,也整明白两个 activity 间,通过 fragment 与 activity 下的 fragment 以及一 个 activity 下的两个 fragment 间穿数据,这三种交互模式,一下子豁然开朗。买 了那本厚厚的权威指南也是我提高的一个契机,不懂的不会的地方就反复看,反 复查资料,好记性不如烂笔头,当我再翻看书上一行一列间的笔记时,不敢说烂 熟于心,起码一下子就能有大致的印象。书上的交流网站 bignerdranch 也很好, 里面有着全世界正在学这本书的人,初学者遇到的问题总是相似的,如果百度不 到的话,去那里看看有时候会有意想不到的收获。
最后就是这次的大作业了,一开始真的觉得很难很难。之前每次是对已有的 程序进行补充完善,突然一下子要自己写一个独立的程序,并且要去实现各种功 能,感觉无从下手。其实一开始我也是准备用已有的 CriminalIntent 进行修改, 而且每次挑战练习我都有做,平时没事看书后面的美化部分还整了点花里胡哨的 在我的 CriminalIntent 上,我想改出来的作品应该不会太差。虽然期末时间真的 很紧,大作业也好报告也罢堆在了一起,不过我想做点和别人不一样的。可能是 兴趣使然,更多的是想挑战下自己吧,我开始捣鼓我自己的小记事本。一开始疯 狂看书,确实书上知识点都基本整明白了,但就是上不了手,轮到打代码的时候 脑子一片浆糊。后来改变了策略,去看博客上别人怎么写的,整体应该怎么架构, 也看了极客学院和黑马的一些视频。通过学习别人的 Demo ,造出自己的轮子, 我觉得这是我最大的思想转变。不同于上学期 java 考试这种理论性的东西,光 抱着看书就差不多。我现在觉得实现一个程序更多要从写开始,能写多少先写多 少,遇到问题懂得去学习别人是怎么解决的,找到适合自己且能理解的方法应用 上去,实在解决不了就试试换种方式,一定不能死磕一个小功能的实现或者传值 的无效(血与泪的教训)。后面其实上手以后,进度慢慢的就快了起来,整体架 构也有了,剩下的就是往里面加更多东西了。不过我觉得一个好习惯是把复用的 代码抽出来,不然一个类可能有很多方法堆在一起,出错根本找不到在那里,看 来看去会浪费大量时间。好在有 log.d 这么个 debug 功能,调试时候把一个大的 方法中每一小步都输出一个测试消息,这样看跟踪日志就很有针对性,不过也不
能整太多,不然太混乱了。
之后的学期会继续学习安卓高级,虽然在这次的大作业中已经涉及到了一些 皮毛,一通捣鼓下来也算是懂得原理并且用熟了,不过离了解底层的具体实现还 差的远。虽然安卓现在框架也蛮多的,像 andbase,XUtil 等只需调用封装好的类, 实现增删改查只需一行代码。不过就像上学期基础的 java 知识一样,越想走得 远对底层就要越了解,封装好的东西大家用熟了基本没差,个人能力的体现往往 在于这些编程思维,你需要尝试着去创造。不过这些离我还很遥远,希望接下来 的自己能够踏踏实实走好每一步,努力努力再努力。