Android学习笔记(十二)——数据存储(SQLite数据库)
【第一部分】历史文章:
Android学习笔记(一)——创建第一个Android项目
Android学习笔记(二)android studio基本控件及布局(实现图片查看器)
Android学习笔记(三)android studio中CheckBox自定义样式(更换复选框左侧的勾选图像)
Android学习笔记(四)Android 中Activity页面的跳转及传值
Android学习笔记(五)——Toast提示、Dialog对话框、Menu菜单
Android学习笔记(六)——自定义ListView布局+AsyncTask异步任务
Android学习笔记(七)——数据存储(共享参数SharedPreferences)
Android学习笔记(八)——数据存储(SD卡文件操作)
Android学习笔记(九)——网络技术
Android学习笔记(十)——实现新闻列表案例
Android学习笔记(十一)——一些高级控件的使用
【第二部分】主要问题解决:
Android Studio(存)读取不了SD卡上的文件——【已解决】
本篇文章将介绍,Android的数据存储方式——SqLite的使用方法,包括创建数据库、创建表、进行相应表中数据的CRUD(增删改查)操作。
- SQLite是一款轻量级的关系数据库,它的运算是非常快的,并且占用资源少,通常只需要占用几百KB的内存就足够了,因而特别适合在移动设备上使用。
- SQLite不仅支持标准的SQL语法,还遵循数据库的ACID事务,所以只要你以前使用过其他的关系数据库,就可以很快的上手SQLite。
- SQLite比一般的数据库简单,甚至不用设置用户名及密码就可以使用。
1、数据库的创建。
Android为了让我们能够更加方便的管理数据库,专门提供了一个SQLiteOpenHelper
帮助类。借助这个类可以非常简单的对数据库进行创建与升级。
SQLiteOpenHelper的基本用法:
SQLiteOpenHelper
是一个抽象类,使用的时候必须自己创建一个类,去继承SQLiteOpenHelper。SQLiteOpenHelper有两个抽象方法,如下所示:
public void onCreate(SQLiteDatabase db) {}public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {}
我们必须在自己定义的类中重写以上的方法。然后分别在这两个方法中去实现创建、升级数据库的逻辑。
SQLiteOpenHelper中有两个重要的实例方法:
- getReadableDatabase()
getReadableDatabase()
方法先读写方式打开数据库,如果数据库的磁盘空间满了,就会打开失败,当打开失败后会继续尝试以只读方式打开数据库。 - getWritableDatabase()
getWritableDatabase()
方法以读写方式打开数据库,一旦数据库的磁盘空间满了,数据库就只能读而不能写,倘若使用getWritableDatabase()打开数据库就会出错。
以上两个实例方法都可以创建或者打开一个现有的数据库,如果数据库已经存在,则直接打开,否则,创建一个新的数据库。
SQLiteOpenHelper中的两个构造方法:
一般情况下,我们使用参数少的那个构造函数(上图中第一个),这个构造函数里面有四个参数:
- 第一个参数Context类型:表示上下文对象。
- 第二个参数String类型:表示数据库的名字。
- 第三个参数CursorFactory类型:一般我们填入null。
- 第四个参数int类型:表示数据库版本,用于对数据库进行升级操作。
注:构建完成SQLiteOpenHelper的实例之后,再调用getReadableDatabase()
或者getWritableDatabase()
方法就可以创建数据库了。
创建完成的数据库存放的地址:/data/data/
目录。/databases/
2、使用SQl操作数据库。
在上面我们已经知道调用getReadableDatabase()
或者getWritableDatabase()
可以用于创建和升级数据库,不仅如此,这两个方法可以返回一个SQLiteDatabase
对象,我们可以用这个对象完成数据库的CRUD操作。
(1)通过使用原生的SQL语句操作数据库:
- execSQL(String sql , Object[] args) 执行带占位符的sql语句(update,insert,delete语句)。
String sql_insert="insert into user(account,password) values(?,?)";db.execSQL(sql_insert,new String[]{account,password});
- rawQuery( String sql , String[] args ) 执行带占位符的sql查询(select语句)。
String sql_query="select * from user where account=? and password=?";Cursor cursor= db.rawQuery(sql_query,new String[]{account,password});
(2) 通过以下四个辅助方法来实现:
SQLite直接使用的数据结构是ContentValues类
,类似于映射Map
,提供了put和get
方法用来存取键值对。
注意:区别在于ContentValues的键只能是字符串;ContentValues主要用于记录增加和更新操作,即SQLiteDatabase的insert和update
方法。对于查询操作来说,使用的是另一个游标类Cursor
。调用SQLiteDatabase的query方法,返回Cursor对象。因此,要获取查询结果的话,要根据游标的指示一条一条的遍历结果集合即可。
- insert():插入数据
Button addData = (Button) findViewById(R.id.add_data); addData.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { SQLiteDatabase db = dbHelper.getWritableDatabase(); ContentValues values = new ContentValues(); // 开始组装第一条数据 values.put("name", "The Da Vinci Code"); values.put("author", "Dan Brown"); values.put("pages", 454); values.put("price", 16.96); db.insert("Book", null, values); // 插入第一条数据 values.clear(); // 开始组装第二条数据 values.put("name", "The Lost Symbol"); values.put("author", "Dan Brown"); values.put("pages", 510); values.put("price", 19.95); db.insert("Book", null, values); // 插入第二条数据 } });
- update():更新数据
Button updateData = (Button) findViewById(R.id.update_data); updateData.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { SQLiteDatabase db = dbHelper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put("price", 10.99); db.update("Book", values, "name = ?", new String[] { "The Da Vinci Code" }); } });
- delete():删除数据
Button deleteButton = (Button) findViewById(R.id.delete_data); deleteButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { SQLiteDatabase db = dbHelper.getWritableDatabase(); db.delete("Book", "pages > ?", new String[] { "500" }); } });
- query():查询数据
Button queryButton = (Button) findViewById(R.id.query_data); queryButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { SQLiteDatabase db = dbHelper.getWritableDatabase(); // 查询Book表中所有的数据 Cursor cursor = db.query("Book", null, null, null, null, null, null); if (cursor.moveToFirst()) { do { // 遍历Cursor对象,取出数据并打印 String name = cursor.getString(cursor.getColumnIndex("name")); String author = cursor.getString(cursor.getColumnIndex("author")); int pages = cursor.getInt(cursor.getColumnIndex("pages")); double price = cursor.getDouble(cursor.getColumnIndex("price")); } while (cursor.moveToNext()); } cursor.close(); } });
下面是一个具体的简单的案例项目:使用了上篇文章中自定义适配器的相关知识点。
关键词:SQLite的CRUD操作、自定义数据适配器、ListView、对话框、监听器、各种控件的应用。
1、编写主活动布局。
activity_main.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="cn.edu.hznu.sqlist.MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/addLL" android:orientation="horizontal" > <EditText android:id="@+id/nameET" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:hint="学生姓名" android:inputType="textPersonName" /> <EditText android:id="@+id/balanceET" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:hint="分数" android:inputType="number" /> <ImageView android:onClick="add" android:id="@+id/addIV" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@android:drawable/ic_input_add" /> </LinearLayout> <ListView android:id="@+id/accountLV" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/addLL"> </ListView></LinearLayout>
- 该布局中有两个
EditText
控件,主要完成输入学生的姓名、分数。 - 一个
ImageView
控件,主要完成显示+
图片,点击后向数据库添加数据。 - 一个
ListView
控件,用于展示数据。
2、编写列表项的布局。
item_layout.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" android:padding="10dp"> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:id="@+id/idTV" android:text="1" android:textColor="#000000" android:textSize="20sp" /> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="2" android:id="@+id/nameTV" android:text="PQ" android:textColor="#000000" android:textSize="20sp" android:singleLine="true" /> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="2" android:id="@+id/balanceTV" android:text="12345" android:singleLine="true" android:textColor="#000000" android:textSize="20sp" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> <ImageView android:id="@+id/upIV" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="2dp" android:src="@android:drawable/arrow_up_float" /> <ImageView android:id="@+id/downIV" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@android:drawable/arrow_down_float" /> </LinearLayout> <ImageView android:id="@+id/deleteIV" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@android:drawable/ic_menu_delete" /></LinearLayout>
- 该布局文件,主要包括三个
TextView
控件,主要显示,学生的学号、姓名、分数。 - 三个ImageView控件,显示,向上、向下箭头图标及删除图标。
3、编写对应的实体类。
Student.java
package cn.edu.hznu.sqlist.bean;public class Student { public Student(Long id, String name, Integer score) { this.id = id; this.name = name; this.score = score; } public Student(String name,Integer score){ this.name=name; this.score=score; } private Long id; private String name; private Integer score; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getScore() { return score; } public void setScore(Integer score) { this.score = score; } @Override public String toString() { return "[学号" + id + ", 姓名:" + name + " , 分数:" + score + "]"; }}
4、编写SQLiteOpenHelper的工具帮助类。
注意:SQLiteOpenHelper
是一个抽象类,需要自己定义,继承SQLiteOpenHelper
类即可,同时,重写对应的方法。
MyHelper.java
package cn.edu.hznu.sqlist.utils;import android.content.Context;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;public class MyHelper extends SQLiteOpenHelper { public MyHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE student(id INTEGER PRIMARY KEY AUTOINCREMENT,name VARCHAR(20),score INTEGER)"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { }}
5、编写数据库操作类。(CRUD对应的方法)
StudentDao.java
package cn.edu.hznu.sqlist.dao;import android.content.ContentValues;import android.content.Context;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import java.util.ArrayList;import java.util.List;import cn.edu.hznu.sqlist.bean.Student;import cn.edu.hznu.sqlist.utils.MyHelper;public class StudentDao { private MyHelper helper; public StudentDao(Context context) { helper=new MyHelper(context,"student.db",null,1); } public void insert(Student account) { //获取数据库对象 SQLiteDatabase db = helper.getWritableDatabase(); //用来装载要插入的数据的map<列名,列的值> ContentValues values = new ContentValues(); values.put("name", account.getName()); values.put("score", account.getScore()); //向account表插入数据values long id = db.insert("student", null, values); account.setId(id);//的到id db.close();//关闭数据库 } //根据id 删除数据 public int delete(long id) { SQLiteDatabase db = helper.getWritableDatabase(); //按条件删除指定表中的数据,返回受影响的行数 int count = db.delete("student", "id=?", new String[]{id + ""}); db.close(); return count; } //更新数据 public int update(Student account) { SQLiteDatabase db = helper.getWritableDatabase(); ContentValues values = new ContentValues();//要修改的数据 values.put("name", account.getName()); values.put("score", account.getScore()); int count = db.update("student", values, "id=?", new String[]{account.getId() + ""});//更新并得到行数 db.close(); return count; } //查询所有数据倒序排列 public List<Student> queryAll() { SQLiteDatabase db = helper.getReadableDatabase(); Cursor c = db.query("student", null, null, null, null, null, "score DESC"); List<Student> list = new ArrayList<Student>(); while (c.moveToNext()) { //可以根据列名获取索引 long id = c.getLong(c.getColumnIndex("id")); String name = c.getString(1); int score = c.getInt(2); list.add(new Student(id, name, score)); } c.close(); db.close(); return list; }}
6、自定义数据适配器StudentAdapter。
StudentAdapter.java
package cn.edu.hznu.sqlist.adapter;import android.app.AlertDialog;import android.content.Context;import android.content.DialogInterface;import android.support.annotation.NonNull;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.ArrayAdapter;import android.widget.EditText;import android.widget.ImageView;import android.widget.ListView;import android.widget.TextView;import java.util.List;import cn.edu.hznu.sqlist.R;import cn.edu.hznu.sqlist.bean.Student;import cn.edu.hznu.sqlist.dao.StudentDao;public class StudentAdapter extends ArrayAdapter<Student> { private Context c; private int item_layout_id; private EditText nameET; //学生姓名 private EditText balanceET; //学生分数 private ListView accountLV; private List<Student> list; private StudentDao dao; public StudentAdapter(Context context, int resource, List objects) { super(context, resource,objects); item_layout_id=resource; c=context; list=objects; dao=new StudentDao(context); } @NonNull @Override public View getView(int position, View convertView, ViewGroup parent) { View view=null; final ViewHolder holder; if(convertView==null){//回收站为空\ /** * LayoutInflater.from()得到布局填充器对象 * getContext()获取当前上下文 * inflate() 加载填充布局 */ view= LayoutInflater.from(getContext()) .inflate(item_layout_id,parent,false); holder=new ViewHolder(view); view.setTag(holder); }else {//显示后续的列表项 view=convertView; holder= (ViewHolder) view.getTag(); } final Student itemData=getItem(position); holder.idTV.setText(itemData.getId()+""); holder.nameTV.setText(itemData.getName()+""); holder.balanceTV.setText(itemData.getScore()+""); holder.upIV.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { itemData.setScore(itemData.getScore()+1); notifyDataSetChanged(); dao.update(itemData); } }); holder.downIV.setOnClickListener(new View.OnClickListener(){ public void onClick(View v){ itemData.setScore(itemData.getScore()-1); notifyDataSetChanged(); dao.update(itemData); } }); holder.deleteIV.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { android.content.DialogInterface.OnClickListener listener=new android.content.DialogInterface.OnClickListener(){ @Override public void onClick(DialogInterface dialog, int which) { list.remove(itemData); dao.delete(itemData.getId()); notifyDataSetChanged(); } }; AlertDialog.Builder builder=new AlertDialog.Builder(c); // builder.setTitle("确定要删除吗?"); builder.setPositiveButton("确定",listener); builder.setNegativeButton("取消",null); builder.show(); } }); return view; } class ViewHolder{ TextView idTV; TextView nameTV; TextView balanceTV; ImageView upIV; ImageView downIV; ImageView deleteIV; public ViewHolder(View view) { idTV=(TextView)view.findViewById(R.id.idTV); nameTV=(TextView)view.findViewById(R.id.nameTV); balanceTV=(TextView)view.findViewById(R.id.balanceTV); upIV=(ImageView)view.findViewById(R.id.upIV); downIV=(ImageView)view.findViewById(R.id.downIV); deleteIV=(ImageView)view.findViewById(R.id.deleteIV); } }}
7、编写MainActivity.java。
MainActivity.java
package cn.edu.hznu.sqlist;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.text.TextUtils;import android.view.View;import android.widget.AdapterView;import android.widget.EditText;import android.widget.ListView;import android.widget.Toast;import java.util.List;import cn.edu.hznu.sqlist.adapter.StudentAdapter;import cn.edu.hznu.sqlist.bean.Student;import cn.edu.hznu.sqlist.dao.StudentDao;public class MainActivity extends AppCompatActivity { private EditText nameET; //学生姓名 private EditText balanceET; //学生分数 private ListView accountLV; private List<Student> list; private StudentDao dao; private StudentAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); accountLV=(ListView)findViewById(R.id.accountLV); nameET=(EditText)findViewById(R.id.nameET); balanceET=(EditText)findViewById(R.id.balanceET); dao=new StudentDao(this); list=dao.queryAll(); //查询数据库 adapter=new StudentAdapter(MainActivity.this, R.layout.item_layout,list); accountLV.setAdapter(adapter); //点击列表项的事件 accountLV.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Student a=(Student) parent.getItemAtPosition(position); Toast.makeText(getApplicationContext(),a.toString(),Toast.LENGTH_SHORT).show(); } }); } //增加操作 public void add(View v) { String name = nameET.getText().toString().trim(); String score = balanceET.getText().toString().trim(); if(TextUtils.isEmpty(name)||TextUtils.isEmpty(score)){ Toast.makeText(MainActivity.this,"请填写信息!", Toast.LENGTH_SHORT).show(); return ; }else{ Student a = new Student(name, score.equals("") ? 0 : Integer.parseInt(score)); dao.insert(a); list.add(a); adapter.notifyDataSetChanged(); //动态更新listview } }}
总结:通过本次的小案例,对SQLite的四种操作,做了理解及使用,结合前几次的其他知识的学习,做了一次阶段性的小总结。但是,该案例还有需要改进的地方。比如,插入数据的有效性的检验,修改分数的区间范围限制等;在后期持续改进中。
在下接下来的一篇文章中将会介绍,使用LitePal操作数据库的相关知识。
若文章中有错误的地方欢迎大家反馈或者留言,十分感谢!!!
更多相关文章
- Android中的EditText默认时不弹出软键盘的方法
- Android中文API(127) ―― MessageQueue
- android的sqlite数据库中单引号的诡异作用
- Android(安卓)失去焦点,关闭弹出的软键盘
- Android(安卓)Studio 如何快速添加override或者implements方法
- Android(安卓)studio虚拟机adb环境配置
- Android(安卓)Mms专题之:Android短彩信收发流程(Framework)
- android实现异步加载图片
- Android(安卓)Studio 常见问题集锦