概述
这是一个使用支持NFC的手机扫描NFC射频卡获取到卡号,使用greenDao数据库做本地存储,扫完之后可以将数据以Excel表格形式导出查看数据。不是什么新技术,内容比较简单,这里就主要贴源码了,没有讲解,不过对于有用的人还是比较实用的,方便查看和拿过来就能使用。下面就是整个工程的接入流程,按照步骤就能做出一个实用的工具了。
先做一下准备工作:
生成Excel文件会用到一个jar包文件,下载地址:https://download.csdn.net/download/u013184970/16155721
这里例子用到NFC功能,还有本都存储读写功能,所以要在AndroidManifest.xml文件里加入下面的权限
<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里加入如下内容
apply 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内容如下
// 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文件内容
import 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文件
import 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文件
import 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文件
import 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文件
import 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内容
import 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文件的工具类
import 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
import 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
<?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
import 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扫描结果实体类存储表
import 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卡,获取卡号存储到本地并可以以Excel形式导出数据所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复