Android 应用开发】Android 数据存储 之 SQLite数据库详解(二·)

2. 详细代码



SQLiteDataBase示例程序下载地址 :


-- GirHub - https://github.com/han1202012/SQLite_NewsList.git.




(1) MainActivity代码




package shuliang.han.database;
import android.app.Activity;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
public class MainActivity extends Activity {
    private SQLiteDatabase db;  //数据库对象
    private ListView listView;  //列表
    private EditText et_tittle; //输入的新闻标题
    private EditText et_content;    //输入的新闻内容
    @Override
    protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
 
  //打开或者创建数据库, 这里是创建数据库
  db = SQLiteDatabase.openOrCreateDatabase(this.getFilesDir().toString() + "/news.db", null);
  System.out.println(this.getFilesDir().toString() + "/news.db");
  //初始化组件
  listView = (ListView) findViewById(R.id.lv_news);
  et_tittle = (EditText) findViewById(R.id.et_news_tittle);
  et_content = (EditText) findViewById(R.id.et_news_content);
 
 
    }
    /*
  * 插入数据到数据库中的触发点击事件
  * 如果数据库存在就能正常访问数据库, 如果不存在访问数据库的时候就会出现 SQLiteException 异常
  * 正常访问 : 获取输入的新闻标题 和 新闻内容, 将标题 和 内容插入到数据库, 重新获取Cursor, 使用Cursor刷新ListView内容
  * 异常访问 : 如果访问出现了SQLiteException异常, 说明数据库不存在, 这时就需要先创建数据库
  */
    public void insertNews(View view) {
  String tittle = et_tittle.getText().toString();
  String content = et_content.getText().toString();
 
  try{
    insertData(db, tittle, content);
    Cursor cursor = db.rawQuery("select * from news_table", null);
    inflateListView(cursor);
  }catch(SQLiteException exception){
    db.execSQL("create table news_table (" +
        "_id integer primary key autoincrement, " +
        "news_tittle varchar(50), " +
        "news_content varchar(5000))");
    insertData(db, tittle, content);
    Cursor cursor = db.rawQuery("select * from news_table", null);
    inflateListView(cursor);
  }
 
    }
    /*
  * 向数据库中插入数据
  * 参数介绍 : 
  * -- 参数① : SQL语句, 在这个语句中使用 ? 作为占位符, 占位符中的内容在后面的字符串中按照顺序进行替换
  * -- 参数② : 替换参数①中占位符中的内容
  */
    private void insertData(SQLiteDatabase db, String tittle, String content) {
  db.execSQL("insert into news_table values(null, ?, ?)", new String[]{tittle, content});
    }
    /*
  * 刷新数据库列表显示
  * 1. 关联SimpleCursorAdapter与数据库表, 获取数据库表中的最新数据
  * 2. 将最新的SimpleCursorAdapter设置给ListView
  */
    private void inflateListView(Cursor cursor) {
  SimpleCursorAdapter cursorAdapter = new SimpleCursorAdapter(
    getApplicationContext(), 
    R.layout.item, 
    cursor, 
    new String[]{"news_tittle", "news_content"}, 
    new int[]{R.id.tittle, R.id.content});
 
  listView.setAdapter(cursorAdapter);
    }
    @Override
    protected void onDestroy() {
  super.onDestroy();
  //在Activity销毁的时候, 如果没有
  if(db != null && db.isOpen())
    db.close();
    }
}


(2) XML布局文件



主布局文件 :



<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    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=".MainActivity" 
    android:orientation="vertical">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="新闻标题" />
   
    <EditText 
        android:id="@+id/et_news_tittle"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:singleLine="true"
        android:hint="点击此处输入新闻标题"/>
   
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="新闻内容" />
   
    <EditText 
        android:id="@+id/et_news_content"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:lines="2"
        android:hint="点击此处输入新闻内容"/>
   
    <Button
        android:id="@+id/bt_add_news"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="insertNews"
        android:text="添加新闻" />
   
    <ListView 
        android:id="@+id/lv_news"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"/>
</LinearLayout>



ListView条目布局文件 :


<?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="vertical" >
   
    <TextView 
        android:id="@+id/tittle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="15dp"
        android:textColor="#FFFF00"/>
   
    <TextView 
        android:id="@+id/content"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textSize="10dp"
        android:textColor="#00FF00"/>
</LinearLayout>

效果图 :



Android 应用开发】Android 数据存储 之 SQLite数据库详解(二·)





三. SQLiteOpenHelper类操作数据库



单独使用SQLiteDataBase操作数据库的弊端: 对数据库的表进行操作的时候, 我们不知道数据库中表是否存在, 首先要进行表操作, 在出现异常之后, 在异常捕获的try catch 代码块中创建表, 这样操作很繁琐;




SQLiteOpenHelper作用 : 该类用来管理数据库的创建 和版本更新, 通常使用其子类, 实现onCreate() 和 onUpgrade()方法;




1. 类中的方法介绍



(1) 读写打开数据库



以读写的方式打开数据库 :先以读写方式打开数据库, 如果磁盘满了, 就会打开失败,然后会尝试以只读的方式打开数据库;



public SQLiteDatabase getReadableDatabase ()



(2) 写方式打开数据库



以写的方式打开数据库 :先以读写方式打开数据库, 如果磁盘满了, 就会出错,不推荐使用这种方法, 使用 getReadableDatabase()方法即可;



public SQLiteDatabase getWritableDatabase ()


(3) 创建数据库



创建数据库 : 第一次创建数据库的时候回调该方法, 一般在该方法中 创建数据库表;



public abstract void onCreate (SQLiteDatabase db)

方法解析 :

-- 调用时机: 当使用getReadableDatabase()方法 获取数据库 实例 的时候, 如果数据库不存在, 就会调用这个方法;


-- 方法内容 : 重写该方法一般 将 创建数据库表的 execSQL()方法 和 初始化表数据的一些 insert()方法写在里面;





(4) 更新数据库



更新数据库 : 升级软件的时候更新数据库表结构, 在数据库版本发生变化的时候调用;



public abstract void onUpgrade (SQLiteDatabase db, int oldVersion, int newVersion)

方法解析 :

-- 调用时机 : 数据库版本发生变化的时候回调, newVersion是当前数据库版本号, oldVersion是旧的版本号;


-- 识别版本 : 创建SQLiteOpenHelper子类对象的时候,必须传入一个version参数, 该参数就是当前数据库版本, 只要这个版本高于之前的版本, 就会触发这个onUpgrade()方法;






(5) 关闭数据库



关闭打开的数据库 :



public synchronized void close ()



2. 示例程序要点解析



(1) 在onCreate()方法中创建表



创建数据库表 : 定义一个数据库SQL语句, 之后在onCreate()方法中 execSQL()执行该语句;


final String SQL_CREATE_TABLE = "create table news_table (" +
    "_id integer primary key autoincrement, " +
    "news_tittle varchar(50), " +
    "news_content varchar(5000))";
    @Override
    public void onCreate(SQLiteDatabase db) {
  db.execSQL(SQL_CREATE_TABLE);
    }



(2) 插入数据



插入内容 : 打开数据库,如果存在直接插入内容, 如果不存在就创建表在插入内容;



 

helper.getReadableDatabase().execSQL("insert into news_table values(null, ?, ?)", 
    new String[]{tittle, content});


(3) 查询数据库



查询数据 : 使用SQLiteOpenHelper子类对象打开数据库, 并且执行查询语句;



Cursor cursor = helper.getReadableDatabase().rawQuery("select * from news_table", null);


(4) 解析Cursor记录



将Cursor中的数据转为 ArrayList<Map<String, String>> 类型数据 :


-- 遍历条件 : Cursor的moveToNext()方法, 如果成功移到下一个记录, 就执行循环内容;


-- 获取表中数据 : Cursor的getString(1) 就是获取 这一样记录中的 第二列的数据, 第一列是 "_id" 主键;



private ArrayList<Map<String, String>> cursor2list(Cursor cursor) {
  ArrayList<Map<String, String>> list = new ArrayList<Map<String,String>>();
 
  //遍历Cursor
  while(cursor.moveToNext()){
    Map<String, String> map = new HashMap<String, String>();
    map.put("tittle", cursor.getString(1));
    map.put("content", cursor.getString(2));
    list.add(map);
  }
  return list;
    }


(5) 开启一个Activity并传递数据



流程 :


-- ① 创建Bundle对象 : 该对象可以存放数据, 并可以放到Intent对象中, 传递给另外的组件;


-- ② Bundle存数据 : 使用putSerializable()方法, 可以存放对象, 将 ArrayList<Map<String, String>> 存入里面;


-- ③ 创建Intent对象 : 传入要跳转的Activity的Class对象;


-- ④ Bundle加入Intent对象 : 将存放好数据的Bundle对象加入到Intent对象中;


-- ⑤ 开启Activity : 使用startActivity(intent)方法跳转Activity;



Bundle bundle = new Bundle();
  bundle.putSerializable("news", cursor2list(cursor));
  Intent intent = new Intent(this, SearchResultActivity.class);
  intent.putExtras(bundle);
  startActivity(intent);



(6) 在Activity中获取Intent传递的数据



执行流程 :


-- 获取Intent对象: 调用 getIntent()方法, 可以获取Activity跳转到额Intent对象;


-- 获取Bundle对象 : Intent对象调用 getExtras()方法, 可以获取存放数据的Bundle对象;


-- 将数据从Bundle对象取出 : 调用getSerializable()方法, 并将返回值转换成 List<Map<String, String>> 类型;



 //获取跳转到该Activity的intent对象

 Intent intent = getIntent();

 //获取Intent对象所携带的数据

 Bundle bundle = intent.getExtras();

 //从Bundle中取出List<Map<String,String>>数据

 @SuppressWarnings("unchecked")

 List<Map<String, String>> list = (List<Map<String, String>>)bundle.getSerializable("news");



(7) 适配器转化



将 List<Map<String, String>> 类型数据转为 SimpleAdapter类型适配器 :


参数介绍 :


-- 参数① context : 上下文对象;


-- 参数②List<Map<String, String>> : 数据源;


-- 参数③ id : ListView元素条目布局文件;


-- 参数④ string[] : 数据源中Map对象的键;


-- 参数⑤ int[]: 数据源中Map每个键对应的值 存放的组件 id;


SimpleAdapter adapter = new SimpleAdapter(
    getApplicationContext(),  //上下文对象
    list,       //数据源
    R.layout.item,      //List显示布局
    new String[]{"tittle", "content"}, //List中map的键值
    new int[]{R.id.tittle, R.id.content});  //填充到的布局文件



3. 实例程序源码



(1) SQLiteOpenHelper源码




package shuliang.han.newssearch;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class NewsSearchDatabaseHelper extends SQLiteOpenHelper {
    final String SQL_CREATE_TABLE = "create table news_table (" +
    "_id integer primary key autoincrement, " +
    "news_tittle varchar(50), " +
    "news_content varchar(5000))";
    /*
  * 构造方法 : 
  * 参数介绍 : 
  * 参数① : 上下文对象
  * 参数② : 数据库名称
  * 参数③ : 数据库版本号
  */
    public NewsSearchDatabaseHelper(Context context, String name, int version) {
  super(context, name, null, version);
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
  db.execSQL(SQL_CREATE_TABLE);
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  System.out.println("call update");
    }
}


(2) MainActivity源码




package shuliang.han.newssearch;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
public class MainActivity extends Activity {
    private NewsSearchDatabaseHelper helper;    //数据库帮助类
    private EditText et_tittle;     //输入新闻标题
    private EditText et_content;    //输入新闻内容
    private ListView listView;      //显示新闻列表
    @Override
    protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
 
  helper = new NewsSearchDatabaseHelper(getApplicationContext(), "news", 1);
 
  //初始化控件
  et_tittle = (EditText) findViewById(R.id.et_news_tittle);
  et_content = (EditText) findViewById(R.id.et_news_content);
  listView = (ListView) findViewById(R.id.lv_news);
 
    }
    /*
  * 按钮点击事件
  * 通过判断被点击的组件, 执行不同的操作
  */
    public void onClick(View view) {
  int id = view.getId();
  switch (id) {
    case R.id.bt_add_news:
    insertNews();
    break;
    case R.id.bt_query_news:
    queryNews();
    break;
    default:
    break;
  }
    }
    /*
  * 插入新闻数据
  * 1. 从EditText组件中获取新闻的标题 和 新闻内容
  * 2. 获取数据库并从将 新闻标题 和 内容 插入到数据库中
  * 3. 重新查询数据库 获得Cursor对象
  * 4. 根据cursor对象创建SimpleCursorAdapter对象
  * 5. 将SimpleCursorAdapter设置给ListView, 显示新闻列表
  */
    private void insertNews() {
  String tittle = et_tittle.getText().toString();
  String content = et_content.getText().toString();
 
  helper.getReadableDatabase().execSQL("insert into news_table values(null, ?, ?)", 
    new String[]{tittle, content});
 
  Cursor cursor = helper.getReadableDatabase().rawQuery("select * from news_table", null);
  inflateListView(cursor);
    }
    /*
  * 刷新数据库列表显示
  * 1. 关联SimpleCursorAdapter与数据库表, 获取数据库表中的最新数据
  * 2. 将最新的SimpleCursorAdapter设置给ListView
  */
    private void inflateListView(Cursor cursor) {
  SimpleCursorAdapter cursorAdapter = new SimpleCursorAdapter(
    getApplicationContext(), 
    R.layout.item, 
    cursor, 
    new String[]{"news_tittle", "news_content"}, 
    new int[]{R.id.tittle, R.id.content});
 
  listView.setAdapter(cursorAdapter);
    }
    /*
  * 查询新闻
  * 1. 获取要查询的新闻标题 和 新闻内容
  * 2. 查询数据库 获取 Cursor, 并将Cursor转化为List<Map<String, String>>类型的集合
  * 3. 将集合放入bundle, Intent开启另一个Activity, 将bundle放入intent对象, 跳转Activity
  * 
  */
    private void queryNews() {
  String tittle = et_tittle.getText().toString();
  String content = et_content.getText().toString();
 
  Cursor cursor = helper.getReadableDatabase().rawQuery(
    "select * from news_table where news_tittle like ? or news_content like ?", 
    new String[]{"%" + tittle + "%", "%" + content + "%"});
 
  Bundle bundle = new Bundle();
  bundle.putSerializable("news", cursor2list(cursor));
  Intent intent = new Intent(this, SearchResultActivity.class);
  intent.putExtras(bundle);
  startActivity(intent);
 
 
    }
    /*
  * 返回一个ArrayList集合, 这个集合中每个元素是一个Map集合, 每个Map集合有两个元素
  * 解析Cursor对象 : 
  * 1. cursor光标向下移动一格; 
  * 2. 创建一个HashMap对象
  * 3. 使用 cursor.getString(列标号)获取该行中某列值, 将这个值放入map中
  * 4. 将Map对象放入
  */
    private ArrayList<Map<String, String>> cursor2list(Cursor cursor) {
  ArrayList<Map<String, String>> list = new ArrayList<Map<String,String>>();
 
  //遍历Cursor
  while(cursor.moveToNext()){
    Map<String, String> map = new HashMap<String, String>();
    map.put("tittle", cursor.getString(1));
    map.put("content", cursor.getString(2));
    list.add(map);
  }
  return list;
    }
    @Override
    protected void onDestroy() {
  super.onDestroy();
  //释放数据库资源
  if(helper !=null)
    helper.close();
    }
}



(3) SearchResultActivity源码




package shuliang.han.newssearch;
import java.util.List;
import java.util.Map;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.ListView;
import android.widget.SimpleAdapter;
public class SearchResultActivity extends Activity {
    private ListView listView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
 
  //设置布局文件
  setContentView(R.layout.news_search_result);
  //初始化组件
  listView = (ListView) findViewById(R.id.lv_search_result);
  //获取跳转到该Activity的intent对象
  Intent intent = getIntent();
  //获取Intent对象所携带的数据
  Bundle bundle = intent.getExtras();
  //从Bundle中取出List<Map<String,String>>数据
  @SuppressWarnings("unchecked")
  List<Map<String, String>> list = (List<Map<String, String>>)bundle.getSerializable("news");
 
  SimpleAdapter adapter = new SimpleAdapter(
    getApplicationContext(),  //上下文对象
    list,       //数据源
    R.layout.item,      //List显示布局
    new String[]{"tittle", "content"}, //List中map的键值
    new int[]{R.id.tittle, R.id.content});  //填充到的布局文件
 
  listView.setAdapter(adapter);
    }
}




(4) XML文件



主界面布局 :



<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    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=".MainActivity" 
    android:orientation="vertical">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="新闻标题" />
   
    <EditText 
        android:id="@+id/et_news_tittle"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:singleLine="true"
        android:hint="点击此处输入新闻标题"/>
   
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="新闻内容" />
   
    <EditText 
        android:id="@+id/et_news_content"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:lines="2"
        android:hint="点击此处输入新闻内容"/>
   
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_gravity="center_horizontal">
     <Button
         android:id="@+id/bt_add_news"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:onClick="onClick"
         android:text="添加新闻" />
   
     <Button
         android:id="@+id/bt_query_news"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:onClick="onClick"
         android:text="查找新闻" />
    </LinearLayout>
   
    <ListView 
        android:id="@+id/lv_news"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"/>
</LinearLayout>
ListView条目布局 : 
<?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="vertical" >
   
    <TextView 
        android:id="@+id/tittle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="15dp"
        android:textColor="#FFFF00"/>
   
    <TextView 
        android:id="@+id/content"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textSize="10dp"
        android:textColor="#00FF00"/>
</LinearLayout>
跳转Activity布局 : 
<?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="vertical" >
   
    <ListView 
     android:id="@+id/lv_search_result"
     android:layout_height="wrap_content"
     android:layout_width="wrap_content"/>
</LinearLayout>



SQLiteOpenHelper示例程序下载地址 :


-- GitHub : https://github.com/han1202012/NewsSearch.git .




效果图 :

Android 应用开发】Android 数据存储 之 SQLite数据库详解(二·)


上一篇:【Linux 操作系统】vim编辑器配置及常用命令(一)


下一篇:【嵌入式开发】C语言 指针数组 多维数组(二)