笔记本项目总结
1、项目环境
- Android studio
- JDK 8
- sqlite
2、项目说明
功能:
登陆 注册 跳转 写入文件 删除文件 修改文件 退出
项目描述:
拥有基本功能的记事本 注册 登陆 忘记密码 显示笔记 新建笔记 修改笔记 退出登陆 等。。。用来做基本的写笔记是没有任何问题
3、项目注意点
页面问题
- 行高居中靠左对齐
android:gravity="center_vertical|left"
- 文字居中
android:textAlignment="center"
- 需要图标可以直接右键单击drawable文件 new 一个Vector Asset导入一些需要的
- 创建有侧边栏的注意它是有menu的 不可以直接的修改或者获取它的元素
- Fragment布局结构 一样不能直接引用类名 因为它并不是真实的 跟普通的activty不一样
- 普通的activty跳转+传数据
Intent intent = new Intent(LoginFragment.this, MainActivity.class);
intent.putExtra("username",usm);
startActivity(intent);
- Fragment跳转+传数据
Intent intent1 = new Intent(getActivity(), WriteMainFragment.class);
intent1.putExtra("username",strTel);
startActivity(intent1);
- 以上两种结构可以发现 一个Intent第一个参数填的是LoginFragment.this 指的是当前的activty ;一个填的是getActivity()或者填Context 都是为了获取上下文对象 找到自己的activity
- 如果要跳转的话也要注意步骤
- 要先在AndroidManifest.xml文件下声明 否则无法跳转
<!--注册页面 没注册不能跳转-->
<activity android:name=".ui.login.LoginFragment"></activity>
<activity android:name=".MainActivity" ></activity>
<activity android:name=".ui.register.RegisterFragment"></activity>
<activity android:name=".ui.WriteMain.WriteMainFragment"></activity>
<activity android:name=".ui.user.UserFragment"></activity>
- 如果要修改第一次进入的页面的话就得在AndroidManifest.xml文件中修改 比如我这个是要跳转到ui.index.IndexFragment
- 注意:在这里声明的第一次跳转实际上也等于注册 所以不要在下面重复声明 否则报错
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme.NoActionBar">
<activity
android:name=".ui.index.IndexFragment"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
连接数据库问题
- 防止sql注入
String sql ="select * from login where username=? and password=?";
String[] arr ={name,psd};
Cursor cursor =database.rawQuery(sql,arr);
- 首先得创建一个MyOpenHelper 继承SQLiteOpenHelper 实现它的方法
//这个构造可以传递四个参数
/*
public MyOpenHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
上下文对象 文件名称 null 版本号
*/
public MyOpenHelper(Context context) {
super(context, "node.db", null, 4);
}
@Override
public void onCreate(SQLiteDatabase db) {
//当数据库文件第一次创建的时候 会调用这个方法,在这个方法中 我们一般做表结构的创建
//创建表 注意 在mysqlz中没有autoincrement(自增长)的提示 在这不会报错
//在sqlite 中id 是习惯命名为_id的
db.execSQL("create table login(_id integer primary key autoincrement,username varchar(20),password varchar(20))");
System.out.println("oncreate调用");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//表结构的修改
//添加新的表格
//如果要修改数据表的列的增删 那就可以在这里修改它列 在上面定义版本号
db.execSQL("alter table login add nodeText Text");
}
- 查询语句
- 注意点 百度查到number=cursor.getCount(); 当它返回0代表没有数据 当它返回1代表有数据
- 数据库比对可以用tnode == null ||"".equals(tnode)判断是否为空
- 比对账号密码是否正确不能直接username==uname 而是要name.equals(uname)&&psd.equals(upwd) 否则不相等
intent1 = getActivity().getIntent();
myOpenHelper = new MyOpenHelper(getActivity());
//获取数据
//获取到传入的id
strTel = intent1.getStringExtra("username");
System.out.println(strTel);//传值到了
/*查询*/
SQLiteDatabase database = myOpenHelper.getReadableDatabase();
String sql ="select nodeText from login where username='"+strTel+"'";
Cursor cursor =database.rawQuery(sql,null);
//查询之后的结果返回一个游标Cursor 可以通过游标遍历整个结果集
while (cursor.moveToNext()){
//可以通过某一列的索引返回某一个列的值
tnode=cursor.getString(0);
}
int number=0;
number=cursor.getCount();
System.out.println(number);
if (tnode == null ||"".equals(tnode)){
// showText.setVisibility(View.GONE);
//设置不可见 如果没用数据的话
ToWriteMain.setVisibility(View.GONE);
}
showText.setText(tnode);
database.close();
- 插入语句
- 在这里除了数据库之类的声明执行以及关闭数据库 就只有插入语句 很好写
SQLiteDatabase database = myOpenHelper.getReadableDatabase();
// String sql ="insert into login(node) values("+text+")";
String sql ="update login set nodeText='"+text+"' where username='"+strTel+"'";
//String sql ="delete from login where username='123'";
database.execSQL(sql);
database.close();
- 跳转时候要注意的点
- 跳转到目标页面 目标页面要接收到数据怎么办?
- 普通的activity
intent1 = getIntent();
//获取数据
//获取到传入的id
strTel = intent1.getStringExtra("username");
- Fragment
intent1 = getActivity().getIntent();
strTel = intent1.getStringExtra("username");
- 问题还是在于avtivity与Fragment的区别 获取到它的本质
总结注意:
1、要留意是否要传参 返回的时候是否要带着参数 参数的传递
2、要继承了SQLiteOpenHelper才可以使用sqlite
3、注意节点问题
4、引入自己的头像图标 直接在mipmap引用drawable的图片加载进去就好
5、如果遇到空指针异常 先检查以下自己是不是传了上下文对象centext 如果是Fragment就得传getActivity() 虽然没有直接报错但是会报空指针
6、自 Android Studio 3.0 开始弃用 Android Device Monitor,浏览模拟器内文件可通过
View--Tool Windows--Device File Exolorer 完成,在窗口右侧
7、图标不能直接加载color里面的@color:/xxx 可能values文件是加载到最后的 所以会报错
8、适配器渲染问题 获取到当前item 要找到它的item方法 当前的item 是position
项目结构说明(仅仅在AndroidStudio)
Java MainActivity是项目主文件 所以程序的入口 但不代表第一个进去的页面
com.liang.ui包下 是格个页面的驱动文件 俗称Java代码
res是资源包+页面包
drawable包下可以放一些图片 图标
layout 是页面包 放xml页面文件
menu 包是侧边栏
mipmap包是放图标文件 文件入口的图标
value 包下可以放 颜色 字体 节点 字符串
style包下可以放样式 是否显示
4、BUG盘点
4、1.跳转传参
- 传参
Intent intent = new Intent(MainActivity.this, WriteMainFragment.class);
intent.putExtra("username",strTel);
startActivity(intent);
- 接收(在普通的avtivity)
//获取intent
Intent intent = null;
intent = getIntent();
//获取数据
//获取到传入的id
strTel = intent.getStringExtra("username");
- 接收(在fragment中的)
- 要注意在fragment中 是不能直接取到值的 要去获取到当前的activity对象
intent1 = getActivity().getIntent();
myOpenHelper = new MyOpenHelper(getActivity());
//获取数据
//获取到传入的id
strTel = intent1.getStringExtra("username");
- 如果要传递id也是一样
4、2.图片加载
- 在加载图片的时候会出bug原因有可能复制上去的图片是有红色下划线的
- 1、可能是图片后面还有vxx的就会错误 显示不出来 可能是页面不支持这个格式
- 2、可能是你没引用对 导致的错误
- 3、你放错了文件位置 要放在drawable文件夹中
4、3.图标引用问题
- 使用自带图标需要右键drawable文件夹 --》new -->vector Asset --》里面有一些常用的图标
- 注意问题:颜色
不可以直接在导入的图标xml文件中修改颜色为value文件夹下color 中的颜色否则会报错
- 需要改变自带图标的颜色的话
- 1、就在new 的时候直接改变该颜色
- 2、就在xml中把颜色写死
4、4.集合保存查询值
- 在要批量导入数据一行行渲染到listView中是需要保存查询到的数据的于是我会想到使用ArrayList来保存传过来的数据
在渲染的过程中会出bug ,会在添加中出现空指针异常 这是因为 如果刚刚创建的账号是没有数据的 那么就会报错。
- 解决思路
- 1、我会先判断我查的这个值是否为空 如果为空我就不添加否则我就添加(一般这样就会成功)
- 2、先查看Arraylist里面是什么,会不会是类型的问题
4、5.跳转问题
- 跳转的时候会出现acticity跳转到fragment 的问题、fragment跳转到activity的问题、activity跳转到activity的问题
- 注意的问题 就是起始点
- 解决思路
- 需要考虑是不是会传空指针对象 (大多是这个的问题)
这个问题需要注意在activity中intent第一个参数传的应该是Login.this (xxx.this)
在fragment中intent传的第一个对象应该是getActivity() 道理一样,获取到对应的Activity - 是不是传的参数没传到位 是前面参数没传入造成的
这个问题常见 如果这个页面需要传username但是上一个页面没有传出username就会闪退(报错)
遇到这个问题就得去查这个username的去向 直到找到登陆 - fragment要不要在AndroidManifest.xml中注册呢? 答案是不需要 因为他并没有this 只是依附于Activity的
4、6.sqlite问题
- 创建数据库的问题
- 可以同时创建多张数据表 但是会遇到问题
- 1、会出现运行了但是数据表没有创建出来
这个是需要找到database中的数据库的 在本地的数据库中查看是否有表
解决办法:如果是版本1 可以是直接删掉本地的该数据库再次运行(这个可行) 或者可以修改版本号在下面实现的OnUpdate函数中执行修改的语句
4、7.登陆验证问题
- 登陆成功和失败认证问题
- 在做的过程中会出现个问题 查询登陆表 判断登陆表中是否有对应的账号并且密码正确
- 这个时候会出现比对问题 不能用单纯的 username=name 这样会是false 尽管的同一个字符串 但是本质类型却不同
解决方法:name.equals(uname) 使用equals函数即可单纯比对字符
- 接下来是登陆失败问题
- 在比对的时候会有一个是正确的,但是其余都是失败 如果在if(name.equals(uname)...)中判断在else中写toast提示就会出现问题
一个登陆成功其他全部失败 就会出现明明账号密码都正确了 页面也跳转了 但是提示了登陆失败
- 解决方法
在函数外面创建两个值int count 和int allnum 分别用来保存else了多少次 一共有多少条行数据(while中执行) 在循环结束的时候比对 如果比对一致那么代表着登陆失败 如果有五条数据 else执行了五次 代表着都失败了 否则都是成功
4、8.适配器adept问题
- 适配器问题
adapter = new ArrayAdapter<>(getActivity(),android.R.layout.simple_expandable_list_item_1,arrayList);
add.setAdapter(adapter);
- 上面是把Arraylist的内容渲染到适配器 以ListView的方式显示每一行的数据
- 问题:那么如何去监听它的事件呢? 我要实现点击当前项就传当前的id过去方便我后面操作怎么办
- 解决办法:
- 删除操作
adapter.remove(adapter.getItem(p));
- add是ListView 的对象
- 它里面会有这些参数 那个position就是当前那个列表的id 第一个就是1 第二个就是2
- 接下来我要查到当前数据对应的id的话 就跟ArrayList一样 我也创建一个ArrayList来保存对应的id 并且通过当前值就传当前的位置就好 比如 我添加的是[14,15,16,17,18] 那么我传第一个值就等于是14 就可以根据id对对应的文章进行一个操作,我要删除对应id的文章的话 我只要查到对应用户名和id两个条件删除对应的文章就可以了
add.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent intent = new Intent(getActivity(), UpdateNode.class);
intent.putExtra("username",strTel);
intent.putExtra("id",arrayId.get(position));
startActivity(intent);
// Toast.makeText(getActivity(),"姓名是1:"+"aa",Toast.LENGTH_SHORT).show();
}
});
- 适配器渲染问题 (如果我要删除当前值的适配器的话刷新数据刷新当前页面的话怎么办?)
- 添加下面的构造
- 调用下面的构造
@Override
public void onResume() {
super.onResume();
onCreate(null);
}
//==========================
调用的位置(){
onResume()
}
5、自我评价
这个软件我打算作为一个比较实用的 、设计思路是我手机上有一款随笔记 ,根据那个软件我进行了一些模仿 但是 也在创新 也比较方便你甚至可以给自己创建不同的账号来储存不同的文件 日记 日程安排 有方便点 但是也有需要完善的点 目前这个1.0版本是没有太多功能的。当然有兴趣的话我会去完善这些未完成的功能。总体来说我比较满意这个作品,这个作品能让我学到一些东西,但现在的开发模式都采用框架,很少人会有回原来的组件方法,但是这是我一个比较具有完整性质的软件。我可能会在将来通过云服务器的方式将数据云同步。在开发的过程中是有点困难的,无论是遇到的bug还是代码的问题还是配置的问题 都得一步一步的学习 sqlite的使用让我觉得一个程序居然能内置这么小的数据库,而且是手机,在手机上跑起来没有毛病 。编程其中有乐趣,有成就感。
6、记录一些必要的组件
- 对话框
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("删除操作");
builder.setMessage("你确定要删除该文本吗?");
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
String str="";
SQLiteDatabase database = myOpenHelper.getReadableDatabase();
// String sql ="insert into login(node) values("+text+")";
//String sql ="update nodetext set text='' where username='"+strTel+"'";
String sql ="delete from nodetext where username='"+strTel+"' and _id='"+id+"'";
database.execSQL(sql);
database.close();
// ToWriteMain.setVisibility(View.GONE);
Toast ts = Toast.makeText(getActivity(),"删除成功",Toast.LENGTH_LONG);
ts.show();
adapter.remove(adapter.getItem(p));
}
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
Toast ts = Toast.makeText(getActivity(),"删除操作已取消",Toast.LENGTH_SHORT);
ts.show();
}
});
builder.show();