当前位置: 首页 > news >正文

Android SQLite的基本使用、生成Excel文件保存到本地

1. Android SQLite的基本使用

在这里插入图片描述

1.1. SQLiteOpenHelper

  Android 底层已经通过一个SQLiteOpenHelper的抽象类将数据库的创建,以及修改,更新等都放在了里面。
要使用它必须实现它的OnCreate(SQLiteDatabase db),onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) 方法。
  onCreate:当数据库第一次被建立的时候被执行,例如创建表,初始化数据等。
onUpgrade:当数据库需要被更新的时候执行,例如删除久表,创建新表。

1.2. 自定义辅助类

  我们要在Android中使用SQLite,自然要一个数据库辅助类来创建或打开数据库,这个辅助类继承自SQLiteOpenHelper类。除了必须重写SQLiteOpenHelper的两个抽象方法外,我们还要创建辅助类的构造方法。

/** SqliteOpenHelper* 1.提供了onCreate(), onUpGrade()等创建数据库更新数据库的函数* 2.提供了获取数据库对象的函数*/
public class MySqliteHelper extends SQLiteOpenHelper {/*** 构造函数* @param context 上下文对象* @param name 表示创建数据库的名称* @param factory 游标工厂* @param version 表示创建数据库的版本*/public MySqliteHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {super(context, name, factory, version);}/*** 当数据库创建时回调的函数* @param db 数据库对象*/@Overridepublic void onCreate(SQLiteDatabase db) {Log.i("TAG", "-------onCreate--------");}/*** 当数据库版本更新时回调的函数* @param db 数据库对象* @param oldVersion 数据库旧版本* @param newVersion 数据库新版本*/@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {Log.i("TAG", "-------onUpGrade-------");}/*** 当数据库打开时回调的函数* @param db 数据库对象*/@Overridepublic void onOpen(SQLiteDatabase db) {super.onOpen(db);Log.i("TAG", "-------onOpen-------");}
}

  onOpen 方法并不是必须重写的方法,是打开数据库会回调的函数,我们用 log 日志来监控数据库的状况。
  看到MySqliteHelper类的构造方法可以知道,我们要传入这几个参数比较麻烦,如果每次实例化都需要传入,不利于修改阅读,所以我们通常会创建一个常量类来保存数据库名,版本号这些常量。像如果以后版本号更新了,我们可以很简单的修改。

1.3. 常量类

  在这里我们先创建这几个常量。

public class Constant {public static final String DATABASE_NAME = "info.db"; //数据库名称public static final int DATABASE_VERSION = 1; //数据库版本public static final String TABLE_NAME = "person"; //表名
}

  既然这几个参数我们都已经确定好了,那就可以修改我们的构造函数啦。

    public MySqliteHelper(Context context) {super(context, Constant.DATABASE_NAME, null, Constant.DATABASE_VERSION);}

  现在只需要传入上下文即可,CursorFactory 为 null,数据库名称和版本号引用常量就可以啦。

1.4. 业务逻辑处理类

  我们的 MySqliteHelper 实现了数据库的建立和打开,相当于是一个持久化数据的存储,而 MainActivity 是整个页面数据的展示,我们为了降低耦合度(两者之间的密切关系程度,也可以理解为互相依赖的程度),通常建立一个 DbManager 类,来实现数据库的逻辑处理。

/*** 主要是对数据库操作的工具类*/
public class DbManager {public enum Something {INSTANCE;private MySqliteHelper helper;public MySqliteHelper getInstance(Context context) {if (helper == null) {helper = new MySqliteHelper(context);}return helper;}}
}

  因为是对数据库的操作,所以我们这里用单例模式,这里我用了枚举的方法。对单例模式不了解的朋友,可以看看我的另一篇博客Java–单例模式。

1.5. 布局文件

  现在我们已经初步搭建好了数据库,可以测试数据库是否可以被创建。这里我是用 Button 的点击事件来操作,下面就是 xml 文件,只是很简单的布局,也是这个博客对数据库操作的主要内容。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"><LinearLayoutandroid:orientation="horizontal"android:layout_width="match_parent"android:layout_height="wrap_content"><Buttonandroid:layout_weight="1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="创建数据库"android:onClick="createDb"android:background="#ff3" /><Buttonandroid:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:id="@+id/btn_insert"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="插入数据"android:onClick="click"android:background="#ff3"android:layout_weight="1" /><Buttonandroid:layout_weight="1"android:id="@+id/btn_delete"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="删除数据"android:onClick="click"android:background="#ff3" /></LinearLayout><LinearLayoutandroid:layout_marginTop="60dp"android:orientation="horizontal"android:layout_width="match_parent"android:layout_height="wrap_content"><Buttonandroid:layout_weight="1"android:id="@+id/btn_update"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="修改数据"android:onClick="click"android:background="#ff3" /><Buttonandroid:layout_weight="1"android:id="@+id/btn_deleteApi"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="删除数据api"android:onClick="onClick"android:background="#ff3"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"/><Buttonandroid:layout_weight="1"android:id="@+id/btn_query"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="sql语句查询"android:onClick="click"android:background="#ff3" /></LinearLayout><LinearLayoutandroid:layout_marginTop="120dp"android:orientation="horizontal"android:layout_width="match_parent"android:layout_height="wrap_content"><Buttonandroid:layout_weight="1"android:id="@+id/btn_insertApi"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="插入数据api"android:onClick="onClick"android:background="#ff3" /><Buttonandroid:layout_weight="1"android:id="@+id/btn_queryApi"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="API查询"android:onClick="onClick"android:background="#ff3"android:layout_marginRight="10dp"android:layout_marginLeft="10dp"/><Buttonandroid:layout_weight="1"android:id="@+id/btn_updateApi"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="修改数据api"android:onClick="onClick"android:background="#ff3" /></LinearLayout>
</RelativeLayout>

在这里插入图片描述

1.5. 数据库表

  在我们的 SQLite 数据库中,数据的存储是以表的形式,所以在创建数据库的时候我们也应该创建一张数据表,学习过 SQL 语句的都知道要创建一张完整的数据表,需要表名和列名,而这些事我们有可能要去修改的,所以为了效率,我们应该把这些设置为常量去使用,前面我们建立了一个 Constant 类,让我们添加些数据进去:

public class Constant {public static final String DATABASE_NAME = "info.db"; //数据库名称public static final int DATABASE_VERSION = 1; //数据库版本public static final String TABLE_NAME = "person"; //表名public static final String _ID = "_id";public static final String NAME = "name";public static final String AGE = "age";
}

  可以看到,我们又添加了表名和三个字段,这样就方便我们日后修改。
  让我们回到 MySQLiteHelper 类:

    public void onCreate(SQLiteDatabase db) {Log.i("TAG", "-------onCreate--------");String sql = "create table " + Constant.TABLE_NAME + " (" +Constant._ID + " Integer primary key, " +Constant.NAME + " varchar(10)," +Constant.AGE + " Integer)";db.execSQL(sql);}

  execSQL(String sql) 是 SQLiteDatabase 类的执行 SQL 语句的方法,大家将刚才生成的 info.db 删除,再次运行,这样就能在创建数据库文件的时候创建一张 PERSON 的表啦。
  我们把生成的 info.db 导出到桌面或者你自己的某个硬盘目录下,用可视化数据库工具打开,你就可以看到数据库的信息,自然也能看到我们新创建的那张表。

1.6. SQL语句增删改查

  在 Android 中我们可以自己写 sql 语句去执行,但如果觉得写 sql 语句容易出错,也可以调用 api 中的方法。在 SQLite 中,查询操作是特别的,insert,update,delete都对数据做了修改,但 select 只返回结果,所以我们需要能接收这个结果,这样就不能使用 execSQL 方法啦。
  我们还有两个方法可以执行查询,分别是 rawQuery 和 query,query() 是 api 拼接 sql 语句,我们暂且不提。
  rawQuery(String sql, String[] selectionArgs),sql 就是执行的 select 语句,selectionArgs 是查询条件的占位符,如果没有占位符,就传入null即可,最后会返回一个 Cursor 对象,帮我们确定数据的位置。
  DBManager 类是我们用来管理数据库的工具类,execSQL() 是我们必须用到的方法,为了防止出错,我们应  该在 DBManager 类中写个方法来判断,同样 rawQuery() 也应该判断。

 public static void execSQL(SQLiteDatabase db, String sql) {if (db != null) {if (sql != null && !"".equals(sql)) {db.execSQL(sql);}}}public static Cursor selectDataBySql(SQLiteDatabase db, String sql, String[] selectionArgs) {Cursor cursor = null;if (db != null) {cursor = db.rawQuery(sql, selectionArgs);}return cursor;}

  相信大家都能够看懂,这里就不讲啦。
  rawQuery 是返回一个 Cursor 游标,那么我们自然需要把它转换为熟悉的 list 集合,首先我们要有存储数据的实体类,这就是 Java Bean 思想。

public class Person {private int _id;private String name;private int age;public Person(int _id, String name, int age) {this._id = _id;this.name = name;this.age = age;}@Overridepublic String toString() {return "Person{" +"_id=" + _id +", name='" + name + '\'' +", age=" + age +'}';}public int get_id() {return _id;}public void set_id(int _id) {this._id = _id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}

  有了这个 Person 类,我们就可以在 DBManager 中写从 Cursor 中读数据的方法啦。

public static List<Person> cursorTolist(Cursor cursor) {List<Person> list = new ArrayList<Person>();while (cursor.moveToNext()) {//getColumnIndex(String columnName) 根据参数中指定的字段名称获取字段下标int columnIndex = cursor.getColumnIndex(Constant._ID);//getInt(int columnIndex)根据参数中指定的字段下标 获取对应int类型的valueint _id = cursor.getInt(columnIndex);String name = cursor.getString(cursor.getColumnIndex(Constant.NAME));int age = cursor.getInt(cursor.getColumnIndex(Constant.AGE));Person person = new Person(_id, name, age);list.add(person);}return list;}

  根据字段名获取字段下标,再得到字段的 value,懂得一点 Cursor 知识的朋友应该都能明白。

 public void click(View view) {switch (view.getId()) {case R.id.btn_insert :SQLiteDatabase db = helper.getWritableDatabase();for (int i = 1; i <= 30; i++) {String sql1 = " insert into " + Constant.TABLE_NAME + " values(" + i + ",'张三" + i + "'," + i + ") ";DbManager.execSQL(db, sql1);}db.close();break;case R.id.btn_update :db = helper.getWritableDatabase();String updatesql=" update "+ Constant.TABLE_NAME + " set " + Constant.NAME +"='xiao' where " + Constant._ID + "=1";DbManager.execSQL(db,updatesql);db.close();break;case R.id.btn_delete :db = helper.getWritableDatabase();String deletesql = " delete from " + Constant.TABLE_NAME + " where " + Constant._ID + "=2";DbManager.execSQL(db,deletesql);db.close();break;case R.id.btn_query :db = helper.getWritableDatabase();String sql = "select * from " + Constant.TABLE_NAME;Cursor cursor = DbManager.selectDataBySql(db, sql, null);List<Person> list = DbManager.cursorTolist(cursor);for (Person p: list) {Log.i("TAG", p.toString());}db.close();break;}}

  因为这个代码只是测试数据,所以就随便添加,只要实现功能即可。
  这些代码是很容易理解的,现在打开模拟器点击按钮来看看。
  我们可以看到表中已经有了30条数据,并且 id = 2的数据已经被删除了,id = 1的数据名字也已经别改成了 xiao,这说明我们的功能已经实现了。这里要注意的是,如果你按了几次 insert,会报错,因为 id 重复了,这并没什么。
  因为我们执行的 select 是查询全部信息,所以查询结果也显然成功啦。

1.7. API 操作

  数据库的增删改查需要正确的 SQL 语句,如果对 SQL 语句不熟练的用提供的 api 也是一样的。

1.7.1. insert

insert(String table, String nullColumnHack, ContentValues values);

  table:相信大家都能理解是数据表名。
  nullColumnStack:因为在 SQLite 中可以允许列中有 NULL,但不能整列都是NULL,这个值代表强行插入null值的数据列的列名,我们都是给 null 就可以了。
  values:代表一行记录的数据。insert 方法插入的一行记录使用ContentValues存放,我们看看它的说明。

private HashMap<String, Object> mValues;

  可以知道 ContentValues 是一个键为 String 的 HashMap集合,它提供了多种 put(String key, XXX value) (其中key为数据列的列名)方法用于存入数据,多种 getAsXxx(String key) 方法用于取出数据。
  insert 方法返回 long,代表新添记录的行号,该行号是一个内部直,与主键id无关,发生错误返回-1。

1.7.2. update

update(String table, ContentValues values, String whereClause, String[] whereArgs)

  table 与 values 和 insert 方法是一样的。
  whereClause:表示修改条件,满足该whereClause子句的记录将会被更新。
  whereArgs:表示修改条件的占位符,用于为whereArgs子句传递参数。
  update 方法返回一个 int,表示修改的数据条数。

1.7.3. delete

delete(String table, String whereClause, String[] whereArgs)

  table:代表想删除数据的表名。
  whereClause:满足该whereClause子句的记录将会被删除。
  whereArgs:用于为whereArgs子句传入参数。
与 update 方法中的格式是一样的。

1.7.4. query

query(boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)

  distinct:指定是否去除重复记录。
  table:执行查询数据的表名。
  columns:要查询出来的列名。
  selection:查询条件子句。
  selectionArgs:用于为selection子句中占位符传入参数值,值在数组中的位置与占位符在语句中的位置必须一致,否则就会有异常。
  groupBy:用于控制分组。
  having:用于对分组进行过滤。
  orderBy:用于对记录进行排序。
  limit:用于进行分页。
  最后返回 Cursor 游标。

public void onClick(View view) {switch(view.getId()) {case R.id.btn_insertApi :SQLiteDatabase db = helper.getWritableDatabase();ContentValues values = new ContentValues();values.put(Constant._ID, 2); //put(表示插入数据库的字段名称,表示插入该字段的具体值)values.put(Constant.NAME, "Lily");values.put(Constant.AGE, 15);long result = db.insert(Constant.TABLE_NAME, null, values);if (result > 0) {Toast.makeText(this, "插入数据成功", Toast.LENGTH_SHORT).show();} else {Toast.makeText(this, "插入数据失败", Toast.LENGTH_SHORT).show();}db.close();break;case R.id.btn_updateApi :db = helper.getWritableDatabase();ContentValues cv = new ContentValues();cv.put(Constant.NAME, "cc");//put(表示需要修改的字段名称, 修改后的字段值)
//                int count = db.update(Constant.TABLE_NAME, cv, Constant._ID + "=3", null);int count = db.update(Constant.TABLE_NAME, cv, Constant._ID + "=?", new String[]{"3"});if (count > 0) {Toast.makeText(this, "修改数据成功", Toast.LENGTH_SHORT).show();} else {Toast.makeText(this, "修改数据失败", Toast.LENGTH_SHORT).show();}db.close();break;case R.id.btn_deleteApi :db = helper.getWritableDatabase();int count2 = db.delete(Constant.TABLE_NAME, Constant._ID + "=?", new String[]{"1"});if (count2 > 0) {Toast.makeText(this, "删除数据成功", Toast.LENGTH_SHORT).show();} else {Toast.makeText(this, "删除数据失败", Toast.LENGTH_SHORT).show();}db.close();break;case R.id.btn_queryApi :db = helper.getWritableDatabase();Cursor cursor = db.query(Constant.TABLE_NAME, null, Constant._ID + ">?",new String[]{"10"}, null, null, Constant._ID + " asc");List<Person> list = DbManager.cursorTolist(cursor);for (Person p : list) {Log.i("TAG", p.toString());}db.close();break;}}

  这可以看到,在写查询条件的时候有两种写法:

db.update(Constant.TABLE_NAME, cv, Constant._ID + "=3", null);db.update(Constant.TABLE_NAME, cv, Constant._ID + "=?", new String[]{"3"});

  这两种是完全一样的。
  并没有出现强退,我们看看数据表的变化。
  可以看到我们已经删除了 id = 1 的数据,并且以前 id = 2 被删除的数据也插入了新的,id = 3 的数据也被修改了,再看看查询。
  id > 10 的数据都被输出了,说明我们 api 的功能也都实现啦。

2. Android 生成Excel文件保存到本地

  可以下载下来修改直接用,该项目主要是依赖一个叫jxl.jar的包,导到项目中libs文件下加即可。
  下载地址:https://download.csdn.net/download/qq_36158551/89808728?spm=1001.2014.3001.5503
在这里插入图片描述

  逻辑代码

public class ExcelUtil {//内存地址public static String root = Environment.getExternalStorageDirectory().getPath();public static void writeExcel(Context context, List<Order> exportOrder,String fileName) throws Exception {if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)&&getAvailableStorage()>1000000) {Toast.makeText(context, "SD卡不可用", Toast.LENGTH_LONG).show();return;}String[] title = { "订单", "店名", "电话", "地址" };File file;
//		File dir = new File(context.getExternalFilesDir(null).getPath());File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath());file = new File(dir, fileName + ".xls");if (!dir.exists()) {dir.mkdirs();}// 创建Excel工作表WritableWorkbook wwb;OutputStream os = new FileOutputStream(file);wwb = Workbook.createWorkbook(os);// 添加第一个工作表并设置第一个Sheet的名字WritableSheet sheet = wwb.createSheet("订单", 0);Label label;for (int i = 0; i < title.length; i++) {// Label(x,y,z) 代表单元格的第x+1列,第y+1行, 内容z// 在Label对象的子对象中指明单元格的位置和内容label = new Label(i, 0, title[i], getHeader());// 将定义好的单元格添加到工作表中sheet.addCell(label);}for (int i = 0; i < exportOrder.size(); i++) {Order order = exportOrder.get(i);Label orderNum = new Label(0, i + 1, order.id);Label restaurant = new Label(1, i + 1, order.restName);Label nameLabel = new Label(2,i+1,order.restPhone);Label address = new Label(3, i + 1, order.receiverAddr);sheet.addCell(orderNum);sheet.addCell(restaurant);sheet.addCell(nameLabel);sheet.addCell(address);Toast.makeText(context, "写入成功", Toast.LENGTH_LONG).show();}// 写入数据wwb.write();// 关闭文件wwb.close();}public static WritableCellFormat getHeader() {WritableFont font = new WritableFont(WritableFont.TIMES, 10,WritableFont.BOLD);// 定义字体try {font.setColour(Colour.BLUE);// 蓝色字体} catch (WriteException e1) {e1.printStackTrace();}WritableCellFormat format = new WritableCellFormat(font);try {format.setAlignment(jxl.format.Alignment.CENTRE);// 左右居中format.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE);// 上下居中// format.setBorder(Border.ALL, BorderLineStyle.THIN,// Colour.BLACK);// 黑色边框// format.setBackground(Colour.YELLOW);// 黄色背景} catch (WriteException e) {e.printStackTrace();}return format;}/** 获取SD可用容量 */private static long getAvailableStorage() {StatFs statFs = new StatFs(root);long blockSize = statFs.getBlockSize();long availableBlocks = statFs.getAvailableBlocks();long availableSize = blockSize * availableBlocks;// Formatter.formatFileSize(context, availableSize);return availableSize;}
}

3. Android SQLite及生成Excel文件保存到本地完整代码

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

3.1. SqlActivity

package com.inspur.szyj.activity;import android.database.Cursor;
import android.os.Bundle;
import android.view.View;
import android.widget.ListView;import androidx.appcompat.app.AppCompatActivity;import com.inspur.szyj.R;
import com.inspur.szyj.adapter.SqlAdapter;
import com.inspur.szyj.bean.PersonBean;
import com.inspur.szyj.helper.DateHelper;
import com.inspur.szyj.helper.db.DbService;
import com.inspur.szyj.util.ExcelUtil;import java.util.ArrayList;
import java.util.List;public class SqlActivity extends AppCompatActivityimplements View.OnClickListener {private ListView lv;//控件private Cursor cursor;//数据源//自定义的封装类private DbService dbService;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_sql);findViewById(R.id.create).setOnClickListener(this);findViewById(R.id.update_db).setOnClickListener(this);findViewById(R.id.insert).setOnClickListener(this);findViewById(R.id.update).setOnClickListener(this);findViewById(R.id.select).setOnClickListener(this);findViewById(R.id.delete).setOnClickListener(this);findViewById(R.id.delete_all).setOnClickListener(this);findViewById(R.id.save_excel).setOnClickListener(this);lv = findViewById(R.id.lv);dbService = new DbService(this);}@Overridepublic void onClick(View v) {if (v.getId() == R.id.create) {dbService.createDB();} else if (v.getId() == R.id.update_db) {dbService.updateDatabase();} else if (v.getId() == R.id.insert) {PersonBean personBean = new PersonBean();personBean.setName("张三");personBean.setIdCard("372929199901010101");personBean.setSignTime(DateHelper.getCurTime());personBean.setSignDate(DateHelper.getCurDate());dbService.insertDb(personBean);} else if (v.getId() == R.id.update) {dbService.updateDb();} else if (v.getId() == R.id.select) {List<PersonBean> personList = new ArrayList<>();cursor = dbService.queryDb();if (cursor != null) {//1, 游标向下移动一条数据   2, 判断当前移动到的数据是否存在while (cursor.moveToNext()) {//获取指定的内容cursor.getString(列的编号, 编号是从0开始)PersonBean personBean = new PersonBean();//personBean.setName(cursor.getString(1));int nameIndex = cursor.getColumnIndex("name");personBean.setName(cursor.getString(nameIndex));int idCardIndex = cursor.getColumnIndex("id_card");personBean.setIdCard(cursor.getString(idCardIndex));int signTimeIndex = cursor.getColumnIndex("sign_time");personBean.setSignTime(cursor.getString(signTimeIndex));int signDateIndex = cursor.getColumnIndex("sign_date");personBean.setSignDate(cursor.getString(signDateIndex));personList.add(personBean);}//关闭游标cursor.close();}SqlAdapter adapter = new SqlAdapter(this, personList);lv.setAdapter(adapter);} else if (v.getId() == R.id.delete) {dbService.deleteDb();} else if (v.getId() == R.id.delete_all) {dbService.deleteDbAll();} else if (v.getId() == R.id.save_excel) {try {List<PersonBean> personList2 = new ArrayList<>();cursor = dbService.queryDb();if (cursor != null) {//1, 游标向下移动一条数据   2, 判断当前移动到的数据是否存在while (cursor.moveToNext()) {//获取指定的内容cursor.getString(列的编号, 编号是从0开始)PersonBean personBean = new PersonBean();//personBean.setName(cursor.getString(1));int nameIndex = cursor.getColumnIndex("name");personBean.setName(cursor.getString(nameIndex));int idCardIndex = cursor.getColumnIndex("id_card");personBean.setIdCard(cursor.getString(idCardIndex));int signTimeIndex = cursor.getColumnIndex("sign_time");personBean.setSignTime(cursor.getString(signTimeIndex));int signDateIndex = cursor.getColumnIndex("sign_date");personBean.setSignDate(cursor.getString(signDateIndex));personList2.add(personBean);}//关闭游标cursor.close();}ExcelUtil.writeExcel(this, personList2, "excelFile");} catch (Exception e) {}}}
}

3.2. activity_sql.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:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".activity.JsonActivity"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="5dp"android:layout_marginBottom="5dp"android:gravity="center_vertical"android:orientation="horizontal"><Buttonandroid:id="@+id/create"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="3dp"android:padding="3dp"android:text="创建数据库"android:textSize="13sp" /><Buttonandroid:id="@+id/update_db"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="3dp"android:padding="3dp"android:text="版本更新"android:textSize="13sp" /><Buttonandroid:id="@+id/insert"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="3dp"android:padding="3dp"android:text="插入数据"android:textSize="13sp" /><Buttonandroid:id="@+id/update"android:layout_width="wrap_content"android:layout_height="wrap_content"android:padding="3dp"android:text="修改数据"android:textSize="13sp" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="5dp"android:layout_marginBottom="5dp"android:gravity="center_vertical"android:orientation="horizontal"><Buttonandroid:id="@+id/select"android:layout_width="wrap_content"android:layout_height="wrap_content"android:padding="3dp"android:text="查询数据"android:textSize="13sp" /><Buttonandroid:id="@+id/delete"android:layout_width="wrap_content"android:layout_height="wrap_content"android:padding="3dp"android:text="删除数据"android:textSize="13sp" /><Buttonandroid:id="@+id/delete_all"android:layout_width="wrap_content"android:layout_height="wrap_content"android:padding="3dp"android:text="删除所有数据"android:textSize="13sp" /><Buttonandroid:id="@+id/save_excel"android:layout_width="wrap_content"android:layout_height="wrap_content"android:padding="3dp"android:text="保存excel"android:textSize="13sp" /></LinearLayout><ListViewandroid:id="@+id/lv"android:layout_width="match_parent"android:layout_height="match_parent"android:divider="@null" />
</LinearLayout>

3.3. DbService

package com.inspur.szyj.helper.db;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import com.inspur.szyj.bean.PersonBean;/*** 自定义的封装类* 用来实现数据库的操作* @author zzs*/
public class DbService {private Context context;private DbOpenHelper dbOpenHelper;public DbService(Context context) {this.context = context;dbOpenHelper = new DbOpenHelper(context);}//创建数据库public void createDB() {//打开数据库连接(数据库只有在执行次方法后, 数据库才会被创建)//底层已经做了实现:  如果数据库不存在, 创建数据库并且打开连接;// 如果数据库存在,直接打开数据库连接dbOpenHelper.getWritableDatabase();}//数据库版本更新public void updateDatabase() {dbOpenHelper.getWritableDatabase();}//插入数据public void insertDb(PersonBean personBean) {//打开连接()SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
//        //方式一
//        db.execSQL("insert into person(name,id_card,sign_time,sign_date) values('张三','372929199901010101','2024-03-23 34:23:32','2024-03-23')");
//        //方式二
//        db.execSQL("insert into person(name,id_card,sign_time,sign_date) values (?,?,?,?)",
//                new String[]{"李四", "372929199901010101",
//                        "2024-03-23 34:23:32", "2024-03-24"});/** 方式三* 封装好的方法* table 表名* nullColumnHack 可以为空* 如果ContentValues为空:  insert into person() values(null)* values	值  ContentValues*/ContentValues values = new ContentValues();//key:表中的字段     value:字段对应的内容values.put("name", personBean.getName());values.put("id_card",  personBean.getIdCard());values.put("sign_time",  personBean.getSignTime());values.put("sign_date",  personBean.getSignDate());db.insert("person", null, values);}//查询数据public Cursor queryDb() {SQLiteDatabase db = dbOpenHelper.getReadableDatabase();//顺序//sql = "select * from person order by _id asc";//倒序//sql = "select * from person order by _id desc";//得到查询后的游标//Cursor cursor=sqLiteDatabases.rawQuery(sql,null);//Cursor cursor  = db.rawQuery("select * from person ", null);/*** distinct			是否去除重复* table			表名* columns			查询的列名   new String[]{"_id","name","id_card","amount"}* 可以为null, 代表查询所有数据* selection		where 后面的字句  _id = ?* selectionArgs	占位符的值   new String[]{"1"}* groupBy			分组* having			放置在where 后再次筛选* orderBy			排序* limit			区间(分页加载数据)*///Cursor cursor = db.query("person", null, null, null, null, null, null);
//        Cursor cursor = db.query("person",
//                null, "name = ?", new String[]{"张三"},
//                null, null, "name desc");Cursor cursor = db.query("person",null, null,null,null, null, "name asc");return cursor;}//修改数据public void updateDb() {SQLiteDatabase db = dbOpenHelper.getWritableDatabase();//方式一//db.execSQL("update person set amount = ? where _id=?",// new String[]{"5000","2"});/*** 方式二* table		表名* values		contentValues 值* whereClause  where 字句之后的内容* whereArgs    占位符的取值*/ContentValues values = new ContentValues();values.put("name", "小灰灰");db.update("person", values, "name = ?",new String[]{"张三"});db.close();}/*** 删除数据*/public void deleteDb() {SQLiteDatabase db = dbOpenHelper.getWritableDatabase();//方式一//db.execSQL("delete from person where _id  = 1");//方式二//table:表名   whereClause:where 字句之后的内容  whereArgs:占位符的取值//db.delete("person", "name=2", null);//方式三//String[] args2 = {"111", "222", "333"};//db.delete("person", "[name]=? and [id_card]=? and [sign_time]=?", args);String[] args = {"小灰灰"};db.delete("person", "[name]=?", args);db.close();}/*** 删除所有数据*/public void deleteDbAll() {SQLiteDatabase db = dbOpenHelper.getWritableDatabase();db.delete("person", null, null);db.close();}
}

3.4. DbOpenHelper

package com.inspur.szyj.helper.db;import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;/*** 数据库的创建和版本的更新* @author zzs**/
public class DbOpenHelper extends SQLiteOpenHelper {/*** 构造方法   初始数据库创建的必要参数* @param context    上下文对象* name		数据库的名称* factory   游标工厂   null* version   数据库的版本号*/public DbOpenHelper(Context context) {super(context, "qf25", null, 1);}/*** 创建初始的数据表* 第一次创建数据库时被调用(只调用一次)* SQLiteDatabase db   数据库操作类*/@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL("create table person(_id integer primary key autoincrement," +"name varchar(50),id_card varchar(500))");db.execSQL("alter table person add sign_time varchar(500)");db.execSQL("alter table person add sign_date varchar(500)");//db.execSQL("alter table person add amount integer");}/*** 更新数据库* 如果数据库的版本号发生变化后, 执行此方法* SQLiteDatabase db 	数据库操作类* int oldVersion		旧版本号* int newVersion       新版本号**/@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {Log.i("info", "数据库更新了");db.execSQL("alter table person add amount integer");}}

3.5. ExcelUtil

package com.inspur.szyj.util;import android.content.Context;
import android.os.Environment;
import android.os.StatFs;
import android.widget.Toast;import com.inspur.szyj.bean.OrderBean;
import com.inspur.szyj.bean.PersonBean;import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.List;import jxl.Workbook;
import jxl.format.Colour;
import jxl.write.Label;
import jxl.write.WritableCellFormat;
import jxl.write.WritableFont;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;/*** Created by zzs on 2016/1/15.*/
public class ExcelUtil {//内存地址public static String root = Environment.getExternalStorageDirectory().getPath();public static void writeExcel(Context context, List<PersonBean> exportOrder,String fileName) throws Exception {if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)&& getAvailableStorage() > 1000000) {Toast.makeText(context, "SD卡不可用", Toast.LENGTH_LONG).show();return;}String[] title = {"姓名", "身份证号", "签到时间", "签到日期"};File file;File dir = new File(context.getExternalFilesDir(null).getPath());
//        File dir = new File(Environment.getExternalStorageDirectory()
//                .getAbsolutePath()+ "/excelDirectory");file = new File(dir, fileName + ".xls");if (!dir.exists()) {dir.mkdirs();}// 创建Excel工作表WritableWorkbook wwb;OutputStream os = new FileOutputStream(file);wwb = Workbook.createWorkbook(os);// 添加第一个工作表并设置第一个Sheet的名字WritableSheet sheet = wwb.createSheet("订单", 0);Label label;for (int i = 0; i < title.length; i++) {// Label(x,y,z) 代表单元格的第x+1列,第y+1行, 内容z// 在Label对象的子对象中指明单元格的位置和内容label = new Label(i, 0, title[i], getHeader());// 将定义好的单元格添加到工作表中sheet.addCell(label);}for (int i = 0; i < exportOrder.size(); i++) {PersonBean order = exportOrder.get(i);Label orderNum = new Label(0, i + 1, order.getName());Label restaurant = new Label(1, i + 1, order.getIdCard());Label nameLabel = new Label(2, i + 1, order.getSignTime());Label address = new Label(3, i + 1, order.getSignDate());sheet.addCell(orderNum);sheet.addCell(restaurant);sheet.addCell(nameLabel);sheet.addCell(address);Toast.makeText(context, "写入成功", Toast.LENGTH_LONG).show();}// 写入数据wwb.write();// 关闭文件wwb.close();}public static WritableCellFormat getHeader() {WritableFont font = new WritableFont(WritableFont.TIMES, 10,WritableFont.BOLD);// 定义字体try {font.setColour(Colour.BLUE);// 蓝色字体} catch (WriteException e1) {e1.printStackTrace();}WritableCellFormat format = new WritableCellFormat(font);try {format.setAlignment(jxl.format.Alignment.CENTRE);// 左右居中format.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE);// 上下居中// format.setBorder(Border.ALL, BorderLineStyle.THIN,// Colour.BLACK);// 黑色边框// format.setBackground(Colour.YELLOW);// 黄色背景} catch (WriteException e) {e.printStackTrace();}return format;}/** 获取SD可用容量 */private static long getAvailableStorage() {StatFs statFs = new StatFs(root);long blockSize = statFs.getBlockSize();long availableBlocks = statFs.getAvailableBlocks();long availableSize = blockSize * availableBlocks;// Formatter.formatFileSize(context, availableSize);return availableSize;}
}

相关文章:

Android SQLite的基本使用、生成Excel文件保存到本地

1. Android SQLite的基本使用 1.1. SQLiteOpenHelper Android 底层已经通过一个SQLiteOpenHelper的抽象类将数据库的创建&#xff0c;以及修改&#xff0c;更新等都放在了里面。 要使用它必须实现它的OnCreate(SQLiteDatabase db)&#xff0c;onUpgrade(SQLiteDatabase db, int…...

记一次因视频编码无法在浏览器播放、编码视频报错问题

起因 ... f cv2.VideoWriter_fourcc(*h264) ...我这边使用h264编码会提示 OpenCV: FFMPEG: tag 0x34363268/h264 is not supported with codec id 27 and format mp4 / MP4 (MPEG-4 Part 14) OpenCV: FFMPEG: fallback to use tag 0x31637661/avc1 [ERROR:02.711] global /i…...

【深度学习】深度卷积神经网络(AlexNet)

在 LeNet 提出后&#xff0c;卷积神经网络在计算机视觉和机器学习领域中很有名气&#xff0c;但并未起到主导作用。 这是因为 LeNet 在更大、更真实的数据集上训练的性能和可行性还有待研究。 事实上&#xff0c;在 20 世纪 90 年代到 2012 年之间的大部分时间里&#xff0c;…...

C语言扫盲

文章目录 C版本C语言特征GCCprintf数据类型函数指针内存管理void指针 Struct结构和Union结构typedef预处理器make工具cmake工具Projectintegral of sinc functionemulator embedded systeman event schedule 补充在线Linux终端安装Linux参考 建议还是国外教材学习…人家的PPT比…...

视频融合共享平台LntonAIServer视频智能分析抖动检测算法和过亮过暗检测算法

LntonAIServer作为一款智能视频监控平台&#xff0c;集成了多种先进的视频质量诊断功能&#xff0c;其中包括抖动检测和过暗检测算法。这些算法对于提升视频监控系统的稳定性和图像质量具有重要意义。 以下是对抖动检测算法和过暗检测算法的应用场景及优势的详细介绍。 一、L…...

【笔记篇】Davinci Configurator OS模块(上)

目录 1 简介1.1 架构概览2 功能描述2.1 特性2.2 规范偏离2.2.1 API 函数的泛型偏离2.2.2 可信函数 API 偏离2.2.3 服务保护偏离2.2.4 代码保护2.2.5 SyncScheduleTable API 偏差2.2.6 CheckTask/ISRMemoryAccess API 偏差2.2.7 中断 API 偏差2.2.8 Cross Core Getter API2.2.9 …...

19.3 打镜像部署到k8s中,prometheus配置采集并在grafana看图

本节重点介绍 : 打镜像&#xff0c;导出镜像&#xff0c;传输到各个节点并导入运行该项目配置prometheus和grafana 打镜像 本地build docker build -t ink8s-pod-metrics:v1 .build过程 导出镜像 docker save ink8s-pod-metrics > ink8s-pod-metrics.tar 传输到各个node…...

如何让系统u盘重新可用

目录 引言开始操作遇到的错误 引言 我们将 u 盘制作为系统 U 盘后&#xff0c;U 盘就没法在电脑中正常识别出了。当装完系统&#xff0c;不再需要 u 盘充当系统 U 盘想要正常使用该 U 盘&#xff0c;这时候就需要有些操作&#xff0c;让这个 U 盘正常化。 上图就是充当系统盘的…...

14.安卓逆向-frida基础-编写hook脚本2

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;图灵Python学院 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要盲目相信。 工…...

车辆零部件检测和分割数据集-车体数据集-yolo格式-yolov5-yolov10可用

这些标签是用于实例分割任务中的类别&#xff0c;通常在汽车图像识别或自动驾驶技术中使用。以下是这些类别&#xff1a; back_bumper - 后保险杠back_glass - 后挡风玻璃back_left_door - 后左车门back_left_light - 后左灯back_right_door - 后右车门back_right_light - 后右…...

甄选范文“论分布式存储系统架构设计”,软考高级论文,系统架构设计师论文

论文真题 分布式存储系统(Distributed Storage System)通常将数据分散存储在多台独立的设备上。传统的网络存储系统采用集中的存储服务器存放所有数据,存储服务器成为系统性能的瓶颈,也是可靠性和安全性的焦点,不能满足大规模存储应用的需要。分布式存储系统采用可扩展的…...

第十四章:html和css做一个心在跳动,为你而动的表白动画

💖 让心跳加速,传递爱意 💖 在这个特别的时刻,让爱在跳动中绽放!🌟 无论是初次相遇的心动,还是陪伴多年的默契,我们的心总在为彼此跳动。就像这颗炙热的爱心,随着每一次的跳动,传递着满满的温暖与期待。 在这个浪漫的季节,让我们一同感受爱的律动!无论你是在…...

poetry安装

文章目录 前言1. 为什么pip install poetry 会造成依赖冲突1.1 全局环境依赖混淆&#xff1a;1.2 工具和项目之间的冲突&#xff1a;1.3 缺乏依赖隔离&#xff1a;1.4 多出很多额外依赖&#xff1a; 2. 不推荐pipx安装3. poetry高级安装3.1 默认安装路径3.2自定义安装 4. 安装p…...

Proteus如何添加数码管

1、打开安装好的Proteus&#xff0c;点击上方菜单栏中的“库”&#xff0c;再选择“从库选取零件”&#xff0c;或者在左侧元件列表中单击鼠标右键&#xff0c;再点击右键菜单中的“从库中挑选”选项。 2、之后在元器件库中&#xff0c;点击类别中的“Optoelectronics”&#…...

5 apache poi实现excel的动态下拉框功能

excel下拉框 RequestMapping("xiala")public void xiala(HttpServletResponse response){String fileName "僵尸表";try{response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharact…...

深度对比:etcd、Consul、Zookeeper 和 Nacos 作为注册中心和配置中心的优势与劣势

在现代分布式系统和微服务架构中&#xff0c;服务注册中心 和 配置中心 是系统稳定运行的关键组成部分。服务注册中心负责服务的动态注册与发现&#xff0c;而配置中心用于集中管理配置&#xff0c;确保系统在变化的环境中保持一致性。本文将对比 etcd、Consul、Zookeeper 和 N…...

Android webview拦截H5的接口请求并返回处理好的数据

Android webview拦截H5的接口请求并返回处理好的数据 Android 可以通过 WebView 的 shouldInterceptRequest 方法拦截到 H5 中的网络请求。这是一个 WebViewClient 中的回调方法&#xff0c;允许开发者在 WebView 发起网络请求时对其进行处理和修改。 具体使用方法如下&#…...

vue echarts tooltip使用动态模板

先上代码 tooltip: {// 这里是车辆iconshow: true,// trigger: "item",// backgroundColor: "transparent",appendToBody: true,textStyle: {color: "#ffffff" //设置文字颜色},formatter: (params) > {return formatHtml(params.data)},}, …...

網路本地連接沒有有效的IP配置:原因與解決方法

網路本地連接顯示“沒有有效的IP配置”。這通常意味著你的電腦無法從路由器或其他網路設備獲取有效的IP地址&#xff0c;從而導致無法上網。本文將從原因和解決方法兩個方面&#xff0c;詳細解析這個問題。 一、問題的原因 路由器或數據機問題&#xff1a; 路由器或數據機出…...

如何使用ssm实现基于web的学生就业管理系统的设计与实现+vue

TOC ssm726基于web的学生就业管理系统的设计与实现vue 第1章 绪论 1.1 课题背景 二十一世纪互联网的出现&#xff0c;改变了几千年以来人们的生活&#xff0c;不仅仅是生活物资的丰富&#xff0c;还有精神层次的丰富。在互联网诞生之前&#xff0c;地域位置往往是人们思想上…...

忍者像素绘卷镜像免配置部署:自动检测GPU型号并加载最优配置

忍者像素绘卷镜像免配置部署&#xff1a;自动检测GPU型号并加载最优配置 1. 产品概览&#xff1a;打破次元壁的像素艺术工作站 忍者像素绘卷是一款基于Z-Image-Turbo深度优化的图像生成工作站&#xff0c;专为像素艺术创作而设计。它将传统漫画创作与现代AI技术相结合&#x…...

别只看成功率!拆解AlphaFold3在抗体对接中那60%的失败案例

AlphaFold3抗体对接失败的深层解析&#xff1a;60%案例背后的技术挑战与突破路径 当AlphaFold3&#xff08;AF3&#xff09;在抗体-抗原对接领域取得8.9%的高精度成功率时&#xff0c;科学界为之振奋。但鲜少有人关注到&#xff0c;在单种子采样条件下&#xff0c;这一系统仍有…...

Windows内存泄漏排查实战:用VMMap揪出C++程序中的‘内存黑洞’(附Heap快照对比技巧)

Windows内存泄漏排查实战&#xff1a;用VMMap精准定位C程序中的"内存黑洞" 1. 内存泄漏&#xff1a;程序员的隐形噩梦 在C开发领域&#xff0c;内存泄漏堪称最顽固的"慢性病"之一。不同于程序崩溃这类明显故障&#xff0c;内存泄漏往往悄无声息地蚕食系统资…...

开源工具优化Cursor API调用:突破限制提升开发效率的完整方案

开源工具优化Cursor API调用&#xff1a;突破限制提升开发效率的完整方案 【免费下载链接】cursor-free-vip [Support 0.45]&#xff08;Multi Language 多语言&#xff09;自动注册 Cursor Ai &#xff0c;自动重置机器ID &#xff0c; 免费升级使用Pro 功能: Youve reached y…...

AMD笔记本性能优化与温度控制完全指南:使用G-Helper实现CPU降压调优

AMD笔记本性能优化与温度控制完全指南&#xff1a;使用G-Helper实现CPU降压调优 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops. Control tool for ROG Zephyrus G14, G15, G16, M16, Flow X13, Flow X16, TUF, Strix, Scar and other mod…...

嵌入式系统中SipHash轻量级哈希实现与优化

1. SipHash 嵌入式底层实现技术解析SipHash 是一种基于加法-循环-异或&#xff08;Add-Rotate-Xor, ARX&#xff09;结构的伪随机函数族&#xff0c;专为短输入消息设计&#xff0c;在嵌入式系统中广泛用于哈希表键值保护、拒绝服务&#xff08;DoS&#xff09;防护、安全计数器…...

OpenWrt SDK实战:如何用SDK高效开发自定义驱动和应用

OpenWrt SDK实战&#xff1a;如何用SDK高效开发自定义驱动和应用 在嵌入式开发领域&#xff0c;OpenWrt因其高度模块化和可定制性成为路由器及物联网设备的首选操作系统。但对于需要频繁修改驱动或开发定制应用的工程师来说&#xff0c;每次完整编译整个系统不仅耗时耗力&#…...

从FPGA到ASIC:实战中如何为你的IP核选择合适的Wishbone互联拓扑?

从FPGA到ASIC&#xff1a;实战中如何为你的IP核选择合适的Wishbone互联拓扑&#xff1f; 在复杂SoC设计中&#xff0c;总线架构的选择往往决定了系统性能的上限。Wishbone作为轻量级片上总线协议&#xff0c;其灵活的互联拓扑为工程师提供了四种截然不同的设计范式&#xff1a;…...

突破平台限制:基于Go+Qt5的喜马拉雅音频下载解决方案

突破平台限制&#xff1a;基于GoQt5的喜马拉雅音频下载解决方案 【免费下载链接】xmly-downloader-qt5 喜马拉雅FM专辑下载器. 支持VIP与付费专辑. 使用GoQt5编写(Not Qt Binding). 项目地址: https://gitcode.com/gh_mirrors/xm/xmly-downloader-qt5 喜马拉雅FM作为国内…...

从BIOS到BMC:手把手拆解Redfish协议在服务器开机时的‘数据握手’全过程

从BIOS到BMC&#xff1a;手把手拆解Redfish协议在服务器开机时的‘数据握手’全过程 凌晨3点的数据中心&#xff0c;一台刚上电的服务器正以毫秒级速度完成自检。在这不足5秒的瞬间里&#xff0c;BIOS与BMC之间正通过Redfish协议进行着精密的数据舞蹈——这不是简单的信息交换&…...