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/ <package name>/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操作数据库的相关知识。

若文章中有错误的地方欢迎大家反馈或者留言,十分感谢!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章