1、引用了Android studio自带的*个模板(右下角带小圆按钮的),用这个按钮做点击事件用来跳到编辑笔记界面。项目创建会多带两个java文件(FirstFragment、SecondFragment)以及对应的两个xml文件,这里我尝试将这几个文件删掉结果运行会崩,然后我将这几个文件的主题代码注释掉又没问题。暂时不管那么多。

2、创建一个编辑笔记的activity和对应的xml,新建的activity要在androidManifest.xml文件里面注册。然后在自建Activity的标签里面添加android:windowSoftInputMode=“adjustResize”,这样弹出输入法的时候就不会遮挡文本输入框。(这行代码意思作用是当弹出输入法时以更小的布局去显示界面,相当于输入法把界面挤上去了。还有一个adjustPan,能使当前内容自动移动避免被输入法遮挡)。xml文件里面很简单就一个EditView和一个Button,EditView里面填入的提示文字用android:hint,这样输入时就不用像android:text一样再删一次提示文字了。然后设置android:inputType=“textMultiLine” android:singleLine=“false”,这样就可以像文本文档一样一直输入多行了。然后背景色android:background设置一个颜色以区分,虽然简陋但还是要有个样子哈哈。以前设置颜色的时候都要去网上搜颜色代码,这次才发现这一行设置左边行数旁边会有一个小颜色框显示的当前颜色,点一下就可以调颜色了。我就说这么功能全面的开发软件怎么会没有调色板呢!

%title插图%num
然后就是Button的点击事件了,首先判断EditView里面不为空,然后就是痛苦的数据库环节。但好在AndroidStudio里面自带的SQLite操作简单且方便,提供了一系列方法避免了直接写sql语句。

3,要操作数据库,首先就要先创建一个类继承SQLiteOpenHelper类,看名字就知道,数据库开启助手,专门用来进行数据库操作的类,虽然我了解的不多,但是要进行数据库操作应该是离不了它了。创建好我的NoteDatabasehelper后,首先当然是实现父类的两个抽象方法,一个onCreate(SQLiteDatabase db){},一个onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){}。前一个当然就是建表等sql语句的执行地方了。后面一个则是升级数据库用的,只要传进去的新版本号大于老版本号,就可以在里面执行升级逻辑。然后我们还要一个构造函数,传一个Context对象进来。这个Context我搞得不是很清楚,反正就是到处都有用。然后构造方法里面会有一个父类的构造实现,传进四个参数,一个就是Context,然后数据库名,factory(一般都为null),数据库版本号。数据库名和版本在前面定义一个static final的就好了,然后再onCreate里面执行建表语句。这样当我们调用这个构造方法的时候就会建库,然后会自动调用onCreate方法建表(这里我是自己写的建表语句然后在onCreate里面执行的,之前老是建库建表傻傻分不清楚)。但是如果后续想要再加表的话貌似会失败?因为数据库已经存在,不会再调用onCreate方法了,这里就需要onUpgrade方法来更新数据库了。

4、写好NoteDataBaseHelper类以后,我们就可以在之前的Activity的点击事件里面调用了,首先当然是new 一个NoteDatabaseHelper类,传进一个当前的Context对象(当前Activity.this),然后就是声明一个SQLitDatabase对象 = 我们new出来的NoteDataBaseHelper对象.getReadableDatabase()。一直到这里数据库才算真正创建出来。这里注意其实有两个方法都可以创建数据,即getReadableDatabase()和getWritableDatabase()。这两个都可以创建数据库,数据库如果已经存在就会自动打开数据库,然后返回一个可对数据库进行操作的对象。**区别在于当数据库不可写入(如磁盘满了)的情况下,getReadableDatabase()方法返回的对象将以只读的方式打开数据库,而getWritableDatabase()会出现异常。**这里我使用的前者。然后就是往表里存数据了(建库的时候表就已经一起建好了)。存数据当然也是有快捷方法的。

创建好数据库之后可以setting-plugins-marketplace里面搜索database navigator插件并下载,再重启,然后就可以用这个工具查看数据库了。具体步骤首先要点击view-tools windows-device file explorer,在右边弹出的窗口浏览设备文件,data-data-包名-databases下面会有一个database文件,右键另存为,然后在Android Studio*左边应该会有一个DB Browser,点进去然后点击绿色加号,浏览刚刚存储database文件的目录打开文件就可以了。

5,首先new出一个ContentValues对象,这个对象会帮助我们存储数据到时候一并插入表。用.put()方法向对象里面传入数据,这里有两个参数,前一个是String类型的,即插入表中的数据的key名,后面一个则是对应的value了。这里我传入了两个,一个是获取的EidtView里面的数据,对应我表里的note,第二个是当前时间,对应表里的time。(我的表里就三项数据,id,note和time)。再然后就是用之前get的数据库操作对象的insert方法插入数据,insert方法有三个参数,*个表名,第二个不知道是啥填null,第三个就是ContentValues对象了。然后我调用了一个startActivity方法跳回了MainActicity界面,这样就直接刷新MainActivity界面了,就能直接看到笔记的显示记录。

%title插图%num
6,想要在主界面看到笔记记录,当然不可能就这样就好了,MainActivity的界面都还没做呢!到activity_main.xml文件里面,会看到已经有一些代码了,这是这个模板自带的,把没用的去掉(我不记得这个xml有没有没用的组件了,反正留一个右下角的小圆按钮就行了)。然后在*上面写一个ListView就ok了。ListView的属性里面加一个id,高度选匹配内容wrap_content,然后添加上下左右的约束就好。*好用layout_marginXXX加点边距,显得好看一点。这里有一点,上边距(marginTop)要设一个合适的数值,因为应用的label会占用屏幕上方部分空间,如果上边距不设或者设的太少,后面会发现*行视图少了一截。这个之前看到过有解决方式但是我没有去深究,在这里记一下!

7、然后就是很很很很让人头疼的ListView的使用了,我是在网上看了好多别人做的,越看越头疼,可能是我脑子不太好使吧。反正*后我是照着实体书(《Android*行代码》)的入门再结合网上别人做的才整出来。

这个是先写了一个存一条数据的bean类,里面有两个属性即note和time。然后在MainActivity里面声明一个ArrayList数组,数据类型存的这个bean类的对象(我写的类名为ItemInfo)。然后再写了一个initItemInfo()方法,故名思意用来向ArrayList里面存储数据。说到数据当然还是要数据库操作了。老样子NoteDatabaseHelper DBHelper = new NoteDatabaseHelper(this);
SQLiteDatabase db = DBHelper.getReadableDatabase();获取数据库操作对象实例之后,声明一个Cursor对象存储db.query()方法查询出来的数据。这个query方法应该是数据库增删查改里*复杂的了,*少都有七个参数当然这里我们查询整张表只需要*个参数填表名,其他统统null就好。但还是要说一下其他几个参数,后面有的会用到。第二个是columns,即指定查询的列名,第三个是seletion,选择条件,相当于where,填入比如note=? , ?当然就是占位符,第四个就是选择条件的数据,用来替换占位符。

获取到数据库的数据之后,就是要将之存入ArrayList里面了。先if(cursor.moveToFirst)判断一下,里面好像是让cursor从*行开始读取?实体书上用的是kotlin,我记得kotlin的if与java的if用法好像是基本相同的,所以我就照着写了,具体为什么判断一下,暂时不了解。然后在if里面做一个do{}while()循环。循环里面调用ArrayList的add方法,循环存入ItemInfo对象,即add(new ItemInfo( cursor.getString(cursor.getColumnIndex(“note”)) , cursor.getString(cursor.getColumnIndex(“time”)) ))。这里忘了说了,我的ItemInfo的构造方法传了note和time进去的。然后就可以循环把所有的item存入ArrayList里面了。每一条item信息都会分别显示在listview里面的每一个item组件里。

%title插图%num
8、那么要怎么显示呢?首先当然是为item组件创建一个属于它自己的界面,即新建一个xml文件,这个文件也不需要很复杂,只需要两个Textview,分别展示记的内容和记录时间,以及给它限制一个高度。然后,很重要!我们需要一个装载器Adapter,负责将item组件一个个装载进ListView的展示列表。我是做完再回头看才终于了解了一些ListView和装载器的关系的。之前一直被几个实体类,xml,实现类砸的头晕。

这里我先写了一个NoteAdapter的实体类继承系统的Adapter,这里有好几个可选,我用的是ArrayAdapter。然后重写构造函数,我选的*后一个,三个参数。*个context没什么好说的,第二个是一个int类型的resource,待会儿用来传入之前写的那个属于item自己的xml文件,也是这里我才知道,原来R.layout.组件文件ID原来是int类型啊!*后一个参数List类型当然就是用来传入之前存储数据的ArrayList了。然后重写getView方法,也是三个参数,这里并不需要我们动手调用这个方法,我也没去弄明白参数意思。在这个方法里面,先声明一个View view= LayoutInflaterfrom(context).inflate(resource, parent, false);resource就是刚说的要传入xml文件的。另外两个不懂,跟着写的。这样item的组件布局就被填充进装载器了。然后我们就可以声明TextView把item组件里的两个TextView根据id声明出来。如TextView noteInfo = (TextView)view.findViewById(R.id.novteInfo);跟在MainActivity里面声明组件也差不多。然后要声明Item itemInfo = (ItemInfo) getItem(position);所以这里还得重写一个getItem()方法,position指的是当前item在ListView中的坐标位置(从0开始),getView会传一个进来。再就是setText将对应item的内容显示出来了。这里判断一下,如果note大于一定长度就只显示一截,毕竟空间有限嘛!*后return view。

9、接下来就是在主界面显示条目了。首先执行一次initItem方法把数据都依次加载进ArrayList里面来,然后new一个刚刚写的装载器adapter的实例,再执行listview的setAdapter方法,传入一个adapter参数进去。(这里可能要转一次型,跟着代码提示就行)。然后运行app就可以看到笔记记录都加载进来了

%title插图%num

10、光能显示记录可还不行,我们要给他一个点击事件和一个长按点击事件。点击事件用来进入界面修改笔记,这里我用的笨方法,直接再写了一个UpdateNote的Activity用来更新笔记。绑定的xml文件与之前用来写笔记的Activity的xml文件一样,只是去除了android:hintText文字提示,因为要在这个EditView里面添加note数据嘛。回到点击事件,跟Button差不多,调用listView.setOnItemClickListener方法,然后在里面new 一个OnItemCilickListener(),然后会自动重写一个onItemClick方法。这个方法会传进来四个参数,不用我们管,应该是点击的时候系统自动传进来的。其中pisition即之前所说的每个条目在ListView中的位置,这个我们会用得到。在这个方法里面,我先实例化了一个ItemInfo(即之前的条目信息bean类)对象,用来接listView的.getItemAtPosition()方法返回的数据。这个方法即根据位置获取单个item信息,传进去position。然后强转一下。再然后要用到一个Bundle函数。我们先实例化Bundle,然后用.putString()方法传入数据。这里传入的是一个键值对,key随便写就好了,后面根据这个key取出数据的。value即传入对应的数据,调用先前创建的ItemInfo对象的get方法获取数据。如:bundle.putString(“note”, ii.getNote());。那么用这个Bundle存入数据是干嘛的呢?

当点击ListView条目的时候,我们要跳到相应的Acitivity(即之前创建的updateNote)以修改笔记内容。要跳界面当然要用到Intent i = new Intent(MainActivity.this, UpdateNote.class);
startActivity(i);这两个方法。但是这样是无法把数据传过去。如果我们要在UpdateNote修改数据肯定要先获得相应的数据,这里就会有点麻烦,没有搜索条件怎样取到对应的数据呢?所以我们这里就可以用Intent的.putExtras()让Intent对象帮我们传递额外的数据信息过去。而这个方法传递的就是这个Bundle对象了。所以这里就是先在点击时获取相应条目的数据,因为有position这个参数和getItemAtPosition方法,获取起来就十分简单。然后将数据传到UpdateNote这个Activity。这样既可以直接获取note信息预显示在更新笔记界面,又能在之后更新数据库的时候有条件可用。

%title插图%num
11、然后来到UpdateNoteActivity界面,首先声明一个Bundle对象来接Intent额外传过来的Bundle参数。如:Bundle b = this.getIntent().getExtras();然后用Bundle的getString方法根据自己之前的key获取对应数据,再EditText.setText显示出来。这样当我们点进对应的条目时,就会预先显示之前写的笔记数据了。然后就是做笔记修改后的点击事件。在按钮点击事件里,先判断一下现在getText获取到的EditText的数据是否等于之前的值也就是Bundle传进来的那个数据。如果不等于就进行数据库操作修改note数据。
跟之前存储数据一样,先打开数据库,再new一个ContentValues对象,把修改后的数据put进去,列名一定要跟数据库一样。再然后根据bundle传进来的数据选一个有代表性的作为条件(我用的time,因为肯定不会重复。)。调用db.update方法,传入四个参数:表名,ContentValues对象,条件(time=?),替换占位符的数据(这个一定要看清是String数组,要new一个然后把time数据放进去)。然后就更新成功了。跳一个界面回到MainAcitivity,这样主界面会再加载一次,就能实时更新ListView的条目信息了。(这里如果不跳界面,手机手动返回不知道能不能达到实时更新ListView信息的效果,感觉应该是可以的)。

%title插图%num
12、*后就是长按点击事件删除内容了,ListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener()),然后也会自动重写一个OnLongClick方法。在这个方法里,首先要实例化一个可互动的警告框,这里我使用的AlertDialog.Builder,实例化时照例传入一个当前Context对象。这里这个Builder我看别人的资料说是一种设计模式,当你想用一个控件或者对象却又无法直接new出来时,那么这个对象多半就是用到了Builder模式,至于为什么不能直接new出来,一般可能是太过复杂吧(Builder详解)。new出这个对象后,调用setTitle和setMessage两个方法为警告框添加标题和警告内容。然后调用setPositiveButton做确认事件,setNegativiButton做取消事件。这两个事件都要传入一个text,即按钮的内容(yes/no),然后new一个DialogInterface.OnClickListener()。它会自动重写一个onClick方法,在取消事件setNegativeButton的OnClick里面我们什么都不用写,或者可以写一个Toast弹出提示。而在确认事件setPositiveButto的OnClickn里面要进行数据库操作删除信息数据。这跟之前差不多,条件语句的条件通过获取当前item的ItemInfo对象然后get一个数据就可以。db.delete相比之前的更新操作db.update还要少了一个ContentValues参数。删除完以后要记得重新加载页面,即在执行一个获取页面的代码。但是这里要注意,在加载场景之前一定要将存储item信息的ArrayList通过removeAll方法清空,然后再重新initItemInfo往ArrayList里面存新的数据库信息再加载界面场景。不然会发现ListView之前的界面没删除,然后下面接着的又是新的删除指定信息后的item界面(就是会重复显示内容)。做完两个点击事件之后,记得要执行AlertDialog.Builder对象的create方法和show方法,不然警告框不会显示。*后return true。

%title插图%num

%title插图%num

13、*后有两个点要注意,我之前在adapter设置的是note数据超过十个字符时就省略后面的,但是实际过程如果一个字符换一行的话会导致该信息的item界面显示异常。还有一个问题就是如果有几十条几百条甚至更多的话,这个记事本是要一次性全部加载完再一次性装载进Listview的,这个的优化涉及到到了adapter的getView里面的convertView参数。这个参数好像是可以实现未显示的条目释放资源,当显示新的条目时自动实时加载内容。这样可以大大减轻资源负担。

14、到此一个简单的记事本就实现了,能力有限只能做到这样了。说实话确实费了我很大功夫,结果回头再看发现其实也没我做的时候想的那么难。主要还是自己平时积累太少,很多东西照着别的人用一遍就不管了,这样的话根本就得不到任何收获。个人感觉还是要多写博客,虽然记的东西可能不是什么高技术含量的东西,但是过一遍总归能加深印象,下次再做就算忘了,照着自己写的东西看也比看别人的东西更容易也更有成就感。因此,这次特地下决心一定要把这个博客写出来,这可能比我目前为止其他的博客加起来还长了。可能本文排版做的不好,写的很乱废话也多,不过这个初衷本来就是为自己所写的,所以也就不管那么多了。

如果有幸被各位大佬看到了,也欢迎指点一二。

今天解决的当少于十个字符换行会导致item界面显示出问题的问题。加一个判断就好了,判断要显示的字符是否存在回车,存在就分割字符串,再拿出分割出来的*个字符串判断长度是否大于预定要显示的长度。

关于装载器里的view复用,只需要在给view赋值时判断是否为空,为空则创建。
给每个view传一个id方便识别:

%title插图%num

页面加载完毕:

%title插图%num

当往下翻页时,*开始id为0的view被向上遮挡后又转到下面来显示下一条信息:

%title插图%num