这是一个使用支持NFC的手机扫描NFC射频卡获取到卡号,使用greenDao数据库做本地存储,扫完之后可以将数据以Excel表格形式导出查看数据。不是什么新技术,内容比较简单,这里就主要贴源码了,没有讲解,不过对于有用的人还是比较实用的,方便查看和拿过来就能使用。下面就是整个工程的接入流程,按照步骤就能做出一个实用的工具了。
先做一下准备工作:
生成Excel文件会用到一个jar包文件,下载地址:https://download.csdn.net/download/u013184970/16155721
这里例子用到NFC功能,还有本都存储读写功能,所以要在AndroidManifest.xml文件里加入下面的权限
1
2
3
4
5
6
7<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.NFC" />
集成greenDao数据库,在app的build.gradle里加入如下内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24apply plugin: 'com.android.application' apply plugin: 'org.greenrobot.greendao' android { …… greendao { schemaVersion 1//数据库版本号 daoPackage 'com.wjy.nfcscanning.db.gen'//设置DaoMaster、DaoSession、Dao包名 targetGenDir 'src/main/java'//设置DaoMaster、DaoSession、Dao目录 } } dependencies { …… //生成Excel表格使用的 implementation files('libs/jxl.jar') //greenDao数据库 implementation 'org.greenrobot:greendao:3.2.2' implementation 'io.github.yuweiguocn:GreenDaoUpgradeHelper:v2.2.1' }
project下的build.gradle内容如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { google() jcenter() mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:3.5.2' //greenDao数据库 classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { google() jcenter() maven { url "https://jitpack.io" } } } task clean(type: Delete) { delete rootProject.buildDir }
新建db文件夹以及用到的一些文件,结构如下:
1、MyGreenDaoDbHelper文件内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.util.Log; import com.wjy.nfcscanning.db.gen.DaoMaster; import org.greenrobot.greendao.database.Database; /** * Created by WJY. * Date: 2020/8/4 * Time: 14:00 * Description: */ public class MyGreenDaoDbHelper extends DaoMaster.OpenHelper { public MyGreenDaoDbHelper(Context context, String name) { super(context, name); } public MyGreenDaoDbHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) { super(context, name, factory); } @Override @SuppressWarnings("all") public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { super.onUpgrade(db, oldVersion, newVersion); Log.e("MyGreenDaoDbHelper", "----" + oldVersion + "---先前和更新之后的版本---" + newVersion + "----"); if (oldVersion < newVersion) { Log.e("MyGreenDaoDbHelper", "进行数据库升级"); new GreenDaoCompatibleUpdateHelper() .setCallBack( new GreenDaoCompatibleUpdateHelper.GreenDaoCompatibleUpdateCallBack() { @Override public void onFinalSuccess() { Log.e("MyGreenDaoDbHelper", "进行数据库升级 ===> 成功"); } @Override public void onFailedLog(String errorMsg) { Log.e("MyGreenDaoDbHelper", "升级失败日志 ===> " + errorMsg); } } ) .compatibleUpdate( db); Log.e("MyGreenDaoDbHelper", "进行数据库升级--完成"); } } @Override public void onUpgrade(Database db, int oldVersion, int newVersion) { // 不要调用父类的,它默认是先删除全部表再创建 // super.onUpgrade(db, oldVersion, newVersion); } }
2、GreenDaoCompatibleUpdateHelper文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.text.TextUtils; import android.util.Log; import androidx.annotation.NonNull; import org.greenrobot.greendao.AbstractDao; import org.greenrobot.greendao.database.Database; import org.greenrobot.greendao.database.StandardDatabase; import org.greenrobot.greendao.internal.DaoConfig; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Created by WJY. * Date: 2020/8/4 * Time: 13:55 * Description:兼容旧表性质的 greenDao 数据库升级,不会造成旧表的数据丢失 */ public class GreenDaoCompatibleUpdateHelper { public interface GreenDaoCompatibleUpdateCallBack { void onFinalSuccess(); void onFailedLog(String errorMsg); } private static GreenDaoCompatibleUpdateCallBack callBack; @SuppressWarnings("all") public void compatibleUpdate(SQLiteDatabase sqliteDatabase, Class<? extends AbstractDao<?, ?>>... daoClasses) { StandardDatabase db = new StandardDatabase(sqliteDatabase); if (!generateNewTablesIfNotExists_withNoExchangeData(db, daoClasses)) /** 创建之前旧表中不存在的新表 */ return; if (!generateTempTables_withExchangeDataFromOldTable(db, daoClasses)) /** 创建中间表 & 把旧表的数据迁移到中间表 */ return; if (!dropAllTables(db, true, daoClasses)) /** 把旧表全部删除 */ return; if (!createAllTables_withNoExchangeData(db, false, daoClasses)) /** 创建所有新表 */ return; restoreData_fromTempTableToNewTable(db, daoClasses); /** 把中间表的数据迁移到新表 & 删除中间表 */ if (callBack != null) callBack.onFinalSuccess(); callBack = null; } @SuppressWarnings("all") public void compatibleUpdate(StandardDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) { if (!generateNewTablesIfNotExists_withNoExchangeData(db, daoClasses)) return; if (!generateTempTables_withExchangeDataFromOldTable(db, daoClasses)) return; if (!dropAllTables(db, true, daoClasses)) return; if (!createAllTables_withNoExchangeData(db, false, daoClasses)) return; restoreData_fromTempTableToNewTable(db, daoClasses); if (callBack != null) callBack.onFinalSuccess(); callBack = null; } public GreenDaoCompatibleUpdateHelper setCallBack(GreenDaoCompatibleUpdateCallBack callBack1) { callBack = callBack1; return this; } @SafeVarargs private static boolean generateNewTablesIfNotExists_withNoExchangeData(StandardDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) { return reflectMethod(db, "createTable", true, daoClasses); } @SafeVarargs private static boolean generateTempTables_withExchangeDataFromOldTable(StandardDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) { try { for (int i = 0; i < daoClasses.length; i++) { DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]); String tableName = daoConfig.tablename; if (tableName.equals("GROUP")){ //group是关键字 最好不要用作表名,但是如果吴用了,用中括号[]括起来就好了,括起来后该字段就被转化为了普通的字符串 //如果误用了其他关键字 则在此处处理其他字段 tableName = "[GROUP]"; } String tempTableName = daoConfig.tablename.concat("_TEMP"); StringBuilder insertTableStringBuilder = new StringBuilder(); insertTableStringBuilder.append("CREATE TEMP TABLE ").append(tempTableName); insertTableStringBuilder.append(" AS SELECT * FROM ").append(tableName).append(";"); Log.e("cacacaca", "daoConfig="+daoConfig.toString()); Log.e("cacacaca", "tableName="+tableName); Log.e("cacacaca", "insertTableStringBuilder.toString()="+insertTableStringBuilder.toString()); db.execSQL(insertTableStringBuilder.toString()); } return true; } catch (Exception e) { if (callBack != null) callBack.onFailedLog("generateTempTables_withExchangeDataFromOldTable ===> " + e.toString()); } return false; } @SafeVarargs private static boolean dropAllTables(StandardDatabase db, boolean ifExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) { return reflectMethod(db, "dropTable", ifExists, daoClasses); } @SafeVarargs private static boolean createAllTables_withNoExchangeData(StandardDatabase db, boolean ifNotExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) { return reflectMethod(db, "createTable", ifNotExists, daoClasses); } /** * dao class already define the sql exec method, so just invoke it */ @SafeVarargs private static boolean reflectMethod(StandardDatabase db, String methodName, boolean isExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) { if (daoClasses.length < 1) { if (callBack != null) callBack.onFailedLog("reflectMethod ===> daoClasses.length < 1"); return false; } try { for (Class cls : daoClasses) { Method method = cls.getDeclaredMethod(methodName, Database.class, boolean.class); method.invoke(null, db, isExists); } // restoreData_fromTempTableToNewTable // ===> // android.database.sqlite.SQLiteConstraintException: NOT NULL constraint failed: MATTER_USER_BEAN.STATUS (code 1299) return true; } catch (Exception e) { e.printStackTrace(); if (callBack != null) callBack.onFailedLog("reflectMethod ===> " + e.toString()); } return false; } /** * 把旧表的数据复制到新表,不存在的字段默认值 * * @param db * @param daoClasses */ @SafeVarargs private static void restoreData_fromTempTableToNewTable(StandardDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) { try { for (int i = 0; i < daoClasses.length; i++) { DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]); String tableName = daoConfig.tablename; if (tableName.equals("GROUP")){ //group是关键字 最好不要用作表名,但是如果吴用了,用中括号[]括起来就好了,括起来后该字段就被转化为了普通的字符串 //如果误用了其他关键字 则在此处处理其他字段 tableName = "[GROUP]"; } String tempTableName = daoConfig.tablename.concat("_TEMP"); // get all columns from tempTable, take careful to use the columns list List<String> columns = getColumns(db, tempTableName); ArrayList<String> properties = new ArrayList<>(columns.size()); for (int j = 0; j < daoConfig.properties.length; j++) { String columnName = daoConfig.properties[j].columnName; if (columns.contains(columnName)) { properties.add(columnName); } } if (properties.size() > 0) { final String columnSQL = "`" + TextUtils.join("`,`", properties) + "`"; StringBuilder insertTableStringBuilder = new StringBuilder(); insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" ("); insertTableStringBuilder.append(columnSQL); insertTableStringBuilder.append(") SELECT "); insertTableStringBuilder.append(columnSQL); insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";"); db.execSQL(insertTableStringBuilder.toString()); } StringBuilder dropTableStringBuilder = new StringBuilder(); dropTableStringBuilder.append("DROP TABLE ").append(tempTableName); db.execSQL(dropTableStringBuilder.toString()); } } catch (Exception e) { if (callBack != null) callBack.onFailedLog("restoreData_fromTempTableToNewTable ===> " + e.toString()); } } private static List<String> getColumns(StandardDatabase db, String tableName) { List<String> columns = null; Cursor cursor = null; try { cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 0", null); if (null != cursor && cursor.getColumnCount() > 0) { columns = Arrays.asList(cursor.getColumnNames()); } } catch (Exception e) { if (callBack != null) callBack.onFailedLog("getColumns ===> " + e.toString()); } finally { if (cursor != null) cursor.close(); if (null == columns) columns = new ArrayList<>(); } return columns; } }
3、DaoSession文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47import java.util.Map; import org.greenrobot.greendao.AbstractDao; import org.greenrobot.greendao.AbstractDaoSession; import org.greenrobot.greendao.database.Database; import org.greenrobot.greendao.identityscope.IdentityScopeType; import org.greenrobot.greendao.internal.DaoConfig; import com.wjy.nfcscanning.NfcInfo; import com.wjy.nfcscanning.db.gen.NfcInfoDao; // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. /** * {@inheritDoc} * * @see org.greenrobot.greendao.AbstractDaoSession */ public class DaoSession extends AbstractDaoSession { private final DaoConfig nfcInfoDaoConfig; private final NfcInfoDao nfcInfoDao; public DaoSession(Database db, IdentityScopeType type, Map<Class<? extends AbstractDao<?, ?>>, DaoConfig> daoConfigMap) { super(db); nfcInfoDaoConfig = daoConfigMap.get(NfcInfoDao.class).clone(); nfcInfoDaoConfig.initIdentityScope(type); nfcInfoDao = new NfcInfoDao(nfcInfoDaoConfig, this); registerDao(NfcInfo.class, nfcInfoDao); } public void clear() { nfcInfoDaoConfig.clearIdentityScope(); } public NfcInfoDao getNfcInfoDao() { return nfcInfoDao; } }
4、DaoMaster文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.util.Log; import org.greenrobot.greendao.AbstractDaoMaster; import org.greenrobot.greendao.database.StandardDatabase; import org.greenrobot.greendao.database.Database; import org.greenrobot.greendao.database.DatabaseOpenHelper; import org.greenrobot.greendao.identityscope.IdentityScopeType; // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. /** * Master of DAO (schema version 1): knows all DAOs. */ public class DaoMaster extends AbstractDaoMaster { public static final int SCHEMA_VERSION = 1; /** Creates underlying database table using DAOs. */ public static void createAllTables(Database db, boolean ifNotExists) { NfcInfoDao.createTable(db, ifNotExists); } /** Drops underlying database table using DAOs. */ public static void dropAllTables(Database db, boolean ifExists) { NfcInfoDao.dropTable(db, ifExists); } /** * WARNING: Drops all table on Upgrade! Use only during development. * Convenience method using a {@link DevOpenHelper}. */ public static DaoSession newDevSession(Context context, String name) { Database db = new DevOpenHelper(context, name).getWritableDb(); DaoMaster daoMaster = new DaoMaster(db); return daoMaster.newSession(); } public DaoMaster(SQLiteDatabase db) { this(new StandardDatabase(db)); } public DaoMaster(Database db) { super(db, SCHEMA_VERSION); registerDaoClass(NfcInfoDao.class); } public DaoSession newSession() { return new DaoSession(db, IdentityScopeType.Session, daoConfigMap); } public DaoSession newSession(IdentityScopeType type) { return new DaoSession(db, type, daoConfigMap); } /** * Calls {@link #createAllTables(Database, boolean)} in {@link #onCreate(Database)} - */ public static abstract class OpenHelper extends DatabaseOpenHelper { public OpenHelper(Context context, String name) { super(context, name, SCHEMA_VERSION); } public OpenHelper(Context context, String name, CursorFactory factory) { super(context, name, factory, SCHEMA_VERSION); } @Override public void onCreate(Database db) { Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION); createAllTables(db, false); } } /** WARNING: Drops all table on Upgrade! Use only during development. */ public static class DevOpenHelper extends OpenHelper { public DevOpenHelper(Context context, String name) { super(context, name); } public DevOpenHelper(Context context, String name, CursorFactory factory) { super(context, name, factory); } @Override public void onUpgrade(Database db, int oldVersion, int newVersion) { Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables"); dropAllTables(db, true); onCreate(db); } } }
5、NfcInfoDao是Make Project后 greenDao数据库自动生成的文件;DaoSession和DaoMaster文件里也有一部分内容是Make Project后 greenDao数据库自动生成的内容。
6、DaoUtils文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65import com.wjy.nfcscanning.MyApplication; import com.wjy.nfcscanning.NfcInfo; import com.wjy.nfcscanning.db.gen.NfcInfoDao; import java.util.ArrayList; import java.util.List; /** * Created by WJY. * Date: 2020/8/5 * Time: 9:03 * Description: 对本地数据库表增删改查操作 */ public class DaoUtils { private volatile static DaoUtils mInstance; private DaoUtils() { } /** * 双重检测锁 */ public static DaoUtils getInstance() { if (mInstance == null) { synchronized (DaoUtils.class) { if (mInstance == null) { mInstance = new DaoUtils(); } } } return mInstance; } //添加数据 public static void insertNfcNum(NfcInfo nfcInfo){ MyApplication.getDaoInstant().getNfcInfoDao().insert(nfcInfo); } /** * 根据扫描出来的的nfc编号查询 库里是否存在此条数据 * @param scanNum 扫描出来的的nfc编号 * @return */ public static boolean hadNfcNum(String scanNum){ List<NfcInfo> infoList = new ArrayList<>(); infoList = MyApplication.getDaoInstant().getNfcInfoDao().queryBuilder().where(NfcInfoDao.Properties.CardScanNum.eq(scanNum)).list(); if (infoList.size() > 0){ return true; }else { return false; } } //查询所以数据 public static List<NfcInfo> queryAllNfcList(){ return MyApplication.getDaoInstant().getNfcInfoDao().queryBuilder().list(); } //清空所有数据 public static void clearAllData(){ MyApplication.getDaoInstant().getNfcInfoDao().deleteAll(); } }
7、MyApplication内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59import android.app.Application; import android.content.Context; import com.wjy.nfcscanning.db.MyGreenDaoDbHelper; import com.wjy.nfcscanning.db.gen.DaoMaster; import com.wjy.nfcscanning.db.gen.DaoSession; import org.greenrobot.greendao.identityscope.IdentityScopeType; /** * Created by WJY. * Date: 2020/8/4 * Time: 19:34 * Description: */ public class MyApplication extends Application { private static MyApplication instance; private static Context context; private static DaoSession mDaoSession; //单例模式中获取唯一的MyApplication实例 public static MyApplication getInstance() { if (null == instance) { instance = new MyApplication(); } return instance; } @Override public void onCreate() { super.onCreate(); context = getApplicationContext(); setupDatabase(); } @Override public void onTerminate() { super.onTerminate(); } /** * 配置数据库 */ private void setupDatabase() { //创建数据库shop.db MyGreenDaoDbHelper helper = new MyGreenDaoDbHelper(this, "nfcnumber.db", null); //获取数据库对象 DaoMaster daoMaster = new DaoMaster(helper.getWritableDatabase()); //获取dao对象管理者 mDaoSession = daoMaster.newSession(IdentityScopeType.None); } public static DaoSession getDaoInstant() { return mDaoSession; } }
8、ExcelUtil 生成Excel文件的工具类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108import android.content.Context; import android.os.Environment; import android.os.StatFs; import android.widget.Toast; 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 WJY. * Date: 2021-03-26 * Time: 15:50 * Description: 生成Excel文件的工具类 */ public class ExcelUtil { //内存地址 public static String root = Environment.getExternalStorageDirectory().getPath(); private static String path = "/mnt/sdcard";//sd卡根目录 public static void writeExcel(Context context, List<NfcInfo> 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(path); // File dir = new File(context.getExternalFilesDir(null).getPath());//这个目录是Android/data/com.wjy.nfcscanning/files 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("NFC卡号", 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++) { NfcInfo nfcInfo = exportOrder.get(i); Label cardFaceNum = new Label(0, i + 1, nfcInfo.getCardFaceNum()); Label cardScanNum = new Label(1, i + 1, nfcInfo.getCardScanNum()); sheet.addCell(cardFaceNum); sheet.addCell(cardScanNum); 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; } }
9、本例子中用到的数据转换 方法,写了个工具类CommonTools
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50import java.text.SimpleDateFormat; import java.util.Date; /** * Created by WJY. * Date: 2021-03-26 * Time: 15:25 * Description:工具类 */ public class CommonTools { //获取当前时间 public static String getCurrentDateAccurate() { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date = new Date(); return simpleDateFormat.format(date); } public static String flipHexStr(String s) { StringBuilder result = new StringBuilder(); for (int i = 0; i <= s.length() - 2; i = i + 2) { result.append(new StringBuilder(s.substring(i, i + 2)).reverse()); } return result.reverse().toString(); } /** * 10进制转16进制 * * @param inarray * @return */ public static String ByteArrayToHexString(byte[] inarray) { int i, j, in; String[] hex = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"}; StringBuilder out = new StringBuilder(); for (j = 0; j < inarray.length; ++j) { in = (int) inarray[j] & 0xff; i = (in >> 4) & 0x0f; out.append(hex[i]); i = in & 0x0f; out.append(hex[i]); } return out.toString(); } }
10、下面就是具体实现的内容,先来个页面看下
布局很随意,也很简单activity_main.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98<?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:padding="16dp" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="16dp" android:textColor="@color/red" android:text="使用说明:请按下面步骤操作"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="14dp" android:textColor="@color/red" android:text=" 1、请先输入第一张卡号(卡号要大于0的数字);"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="14dp" android:textColor="@color/red" android:text=" 2、点击确定按钮;"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="14dp" android:textColor="@color/red" android:text=" 3、开始扫描NFC卡;"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="14dp" android:textColor="@color/red" android:text=" 4、扫描完成后再点击最下方导出数据按钮即可;"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="14dp" android:textColor="@color/red" android:text=" 5、到手机内存根目录中查看文件(文件格式为:时间_nfc_num_excel)。"/> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/tv_cardNum" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="先输入第一张卡面上的号码:" android:textColor="@color/black" android:textSize="16dp" android:layout_centerVertical="true"/> <EditText android:id="@+id/et_cardNum" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="number" android:digits="0123456789" android:layout_toRightOf="@+id/tv_cardNum" android:textSize="16dp" android:textColor="@color/black"/> </RelativeLayout> <Button android:id="@+id/btn_ok" android:layout_width="match_parent" android:layout_height="40dp" android:layout_marginTop="10dp" android:text="确定" android:textSize="16dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:text="射频卡号" android:textSize="18dp" android:textColor="@color/colorPrimary"/> <TextView android:id="@+id/tv_content" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:layout_marginTop="10dp"/> <Button android:id="@+id/btn_exportData" android:layout_width="match_parent" android:layout_height="40dp" android:layout_marginTop="10dp" android:text="导出数据" android:textSize="16dp"/> </LinearLayout>
11、主页面MainActivity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import android.app.PendingIntent; import android.content.Intent; import android.content.IntentFilter; import android.nfc.NfcAdapter; import android.nfc.Tag; import android.nfc.tech.MifareClassic; import android.nfc.tech.NfcA; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import com.wjy.nfcscanning.db.DaoUtils; import java.util.ArrayList; import java.util.Date; import java.util.List; public class MainActivity extends AppCompatActivity { private NfcAdapter mNfcAdapter; private PendingIntent pendingIntent; private IntentFilter[] mFilters; private String[][] mTechLists; private EditText et_cardNum;//第一张卡号 private Button btn_ok;//确定按钮 private TextView tv_content;//显示扫描出来的射频卡号 private Button btn_exportData;//导出数据按钮 private List<NfcInfo> nfcInfoList = new ArrayList<>(); private int cardFaceNum = 0;//nfc卡面上的编号 private String nfcNumber;//扫描出来的卡号 Handler mHandler = new Handler(){ @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); switch (msg.what){ case 0: Toast.makeText(MainActivity.this,"已存在此卡号,不可重复扫描",Toast.LENGTH_SHORT).show(); break; case 1: tv_content.setText(nfcNumber); break; case 2: Toast.makeText(MainActivity.this,"请先输入第一张卡面号,然后点确定按钮",Toast.LENGTH_SHORT).show(); break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView(){ et_cardNum = findViewById(R.id.et_cardNum); btn_ok = findViewById(R.id.btn_ok); tv_content = findViewById(R.id.tv_content); //点击确定按钮保存第一个卡号(后面的卡号在这个卡号上自增1)并生成Excel表格文件,之后扫描射频卡直接将数据存入此表格中 btn_ok.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (TextUtils.isEmpty(et_cardNum.getText().toString().trim())){ Toast.makeText(MainActivity.this,"请先输入第一张卡面号",Toast.LENGTH_SHORT).show(); return; } cardFaceNum = Integer.parseInt(et_cardNum.getText().toString().trim()) - 1; DaoUtils.clearAllData();//清空库中所有数据 重新开始记 } }); //导出数据 btn_exportData = findViewById(R.id.btn_exportData); btn_exportData.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { nfcInfoList = DaoUtils.queryAllNfcList(); if (nfcInfoList.size() > 0) { ExcelUtil.writeExcel(MainActivity.this, nfcInfoList, CommonTools.getCurrentDateAccurate()+"_nfc_num_excel"); }else { Toast.makeText(MainActivity.this,"请先扫描卡号再导出数据",Toast.LENGTH_SHORT).show(); } } catch (Exception e) { e.printStackTrace(); } } }); mNfcAdapter = NfcAdapter.getDefaultAdapter(this); if (mNfcAdapter == null) { Toast.makeText(this,"该设备不支持NFC",Toast.LENGTH_SHORT).show(); return; } if (!mNfcAdapter.isEnabled()) { Toast.makeText(this,"NFC功能没有打开,请打开",Toast.LENGTH_SHORT).show(); } pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0); IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED); ndef.addCategory("****/*//*//**//*//**//**//**//*"); mFilters = new IntentFilter[]{ndef};// 过滤器 // 允许扫描的标签类型 mTechLists = new String[][]{ new String[]{MifareClassic.class.getName()}, new String[]{NfcA.class.getName()}}; } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); String action = intent.getAction(); if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) { Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); //获取 Tag 读取 ID 得到字节数组 转字符串 转码 得到卡号(默认16进制 这请自便) Long cardNo = Long.parseLong(CommonTools.flipHexStr(CommonTools.ByteArrayToHexString(tag.getId())), 16); nfcNumber = cardNo.toString(); Log.e("onNewIntent","nfcNumber="+nfcNumber); if (cardFaceNum == 0){ mHandler.sendEmptyMessage(2); }else { boolean hadNum = DaoUtils.hadNfcNum(nfcNumber); if (hadNum) { //如果存在此条数据,则提示不可重复扫描 mHandler.sendEmptyMessage(0); } else { mHandler.sendEmptyMessage(1); cardFaceNum++; //入库 NfcInfo nfcInfo = new NfcInfo(); nfcInfo.setCardFaceNum(cardFaceNum+""); nfcInfo.setCardScanNum(nfcNumber); DaoUtils.insertNfcNum(nfcInfo); } } } } @Override protected void onResume() { super.onResume(); if (mNfcAdapter != null) { if (mNfcAdapter.isEnabled()) { mNfcAdapter.enableForegroundDispatch(this, pendingIntent, null, null); } } } @Override protected void onPause() { super.onPause(); if (mNfcAdapter != null) { mNfcAdapter.disableForegroundDispatch(this); } } }
12、NfcInfo扫描结果实体类存储表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57import org.greenrobot.greendao.annotation.Entity; import org.greenrobot.greendao.annotation.Id; import org.greenrobot.greendao.annotation.Property; import org.greenrobot.greendao.annotation.Generated; /** * Created by WJY. * Date: 2021-03-26 * Time: 15:57 * Description:nfc扫描结果实体类存储表 */ @Entity public class NfcInfo { @Id(autoincrement = true) private Long id; @Property private String cardFaceNum;//卡面上的编号 @Property private String cardScanNum;//扫描出来的编号 @Generated(hash = 1745392942) public NfcInfo(Long id, String cardFaceNum, String cardScanNum) { this.id = id; this.cardFaceNum = cardFaceNum; this.cardScanNum = cardScanNum; } @Generated(hash = 1219438208) public NfcInfo() { } public String getCardFaceNum() { return cardFaceNum; } public void setCardFaceNum(String cardFaceNum) { this.cardFaceNum = cardFaceNum; } public String getCardScanNum() { return cardScanNum; } public void setCardScanNum(String cardScanNum) { this.cardScanNum = cardScanNum; } public Long getId() { return this.id; } public void setId(Long id) { this.id = id; } }
到这就结束了,扫描NFC射频卡号保存本地并可以将数据导出Excel表查看。需要的朋友按照流程去做吧。上面贴出的是全部源码,目前没有上传demo源码工程文件,有需要的就勤劳点去上面粘贴吧,后面看情况再传整个工程源码。
最后
以上就是专注铃铛最近收集整理的关于扫描NFC卡,获取卡号存储到本地并可以以Excel形式导出数据的全部内容,更多相关扫描NFC卡内容请搜索靠谱客的其他文章。
发表评论 取消回复