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的抽象类将数据库的创建,以及修改,更新等都放在了里面。 要使用它必须实现它的OnCreate(SQLiteDatabase db),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 提出后,卷积神经网络在计算机视觉和机器学习领域中很有名气,但并未起到主导作用。 这是因为 LeNet 在更大、更真实的数据集上训练的性能和可行性还有待研究。 事实上,在 20 世纪 90 年代到 2012 年之间的大部分时间里,…...
C语言扫盲
文章目录 C版本C语言特征GCCprintf数据类型函数指针内存管理void指针 Struct结构和Union结构typedef预处理器make工具cmake工具Projectintegral of sinc functionemulator embedded systeman event schedule 补充在线Linux终端安装Linux参考 建议还是国外教材学习…人家的PPT比…...
视频融合共享平台LntonAIServer视频智能分析抖动检测算法和过亮过暗检测算法
LntonAIServer作为一款智能视频监控平台,集成了多种先进的视频质量诊断功能,其中包括抖动检测和过暗检测算法。这些算法对于提升视频监控系统的稳定性和图像质量具有重要意义。 以下是对抖动检测算法和过暗检测算法的应用场景及优势的详细介绍。 一、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看图
本节重点介绍 : 打镜像,导出镜像,传输到各个节点并导入运行该项目配置prometheus和grafana 打镜像 本地build docker build -t ink8s-pod-metrics:v1 .build过程 导出镜像 docker save ink8s-pod-metrics > ink8s-pod-metrics.tar 传输到各个node…...
如何让系统u盘重新可用
目录 引言开始操作遇到的错误 引言 我们将 u 盘制作为系统 U 盘后,U 盘就没法在电脑中正常识别出了。当装完系统,不再需要 u 盘充当系统 U 盘想要正常使用该 U 盘,这时候就需要有些操作,让这个 U 盘正常化。 上图就是充当系统盘的…...
14.安卓逆向-frida基础-编写hook脚本2
免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 内容参考于:图灵Python学院 本人写的内容纯属胡编乱造,全都是合成造假,仅仅只是为了娱乐,请不要盲目相信。 工…...
车辆零部件检测和分割数据集-车体数据集-yolo格式-yolov5-yolov10可用
这些标签是用于实例分割任务中的类别,通常在汽车图像识别或自动驾驶技术中使用。以下是这些类别: 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 全局环境依赖混淆:1.2 工具和项目之间的冲突:1.3 缺乏依赖隔离:1.4 多出很多额外依赖: 2. 不推荐pipx安装3. poetry高级安装3.1 默认安装路径3.2自定义安装 4. 安装p…...
Proteus如何添加数码管
1、打开安装好的Proteus,点击上方菜单栏中的“库”,再选择“从库选取零件”,或者在左侧元件列表中单击鼠标右键,再点击右键菜单中的“从库中挑选”选项。 2、之后在元器件库中,点击类别中的“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 作为注册中心和配置中心的优势与劣势
在现代分布式系统和微服务架构中,服务注册中心 和 配置中心 是系统稳定运行的关键组成部分。服务注册中心负责服务的动态注册与发现,而配置中心用于集中管理配置,确保系统在变化的环境中保持一致性。本文将对比 etcd、Consul、Zookeeper 和 N…...
Android webview拦截H5的接口请求并返回处理好的数据
Android webview拦截H5的接口请求并返回处理好的数据 Android 可以通过 WebView 的 shouldInterceptRequest 方法拦截到 H5 中的网络请求。这是一个 WebViewClient 中的回调方法,允许开发者在 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地址,從而導致無法上網。本文將從原因和解決方法兩個方面,詳細解析這個問題。 一、問題的原因 路由器或數據機問題: 路由器或數據機出…...
如何使用ssm实现基于web的学生就业管理系统的设计与实现+vue
TOC ssm726基于web的学生就业管理系统的设计与实现vue 第1章 绪论 1.1 课题背景 二十一世纪互联网的出现,改变了几千年以来人们的生活,不仅仅是生活物资的丰富,还有精神层次的丰富。在互联网诞生之前,地域位置往往是人们思想上…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
