这是一个使用支持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() {@Overridepublic void onFinalSuccess() {Log.e("MyGreenDaoDbHelper", "进行数据库升级 ===> 成功");}@Overridepublic void onFailedLog(String errorMsg) {Log.e("MyGreenDaoDbHelper", "升级失败日志 ===> " + errorMsg);}}).compatibleUpdate(db);Log.e("MyGreenDaoDbHelper", "进行数据库升级--完成");}}@Overridepublic 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;}@SafeVarargsprivate static boolean generateNewTablesIfNotExists_withNoExchangeData(StandardDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {return reflectMethod(db, "createTable", true, daoClasses);}@SafeVarargsprivate 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;}@SafeVarargsprivate static boolean dropAllTables(StandardDatabase db, boolean ifExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {return reflectMethod(db, "dropTable", ifExists, daoClasses);}@SafeVarargsprivate 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*/@SafeVarargsprivate 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*/@SafeVarargsprivate 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 listList<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);}@Overridepublic 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);}@Overridepublic 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;}@Overridepublic void onCreate() {super.onCreate();context = getApplicationContext();setupDatabase();}@Overridepublic void onTerminate() {super.onTerminate();}/*** 配置数据库*/private void setupDatabase() {//创建数据库shop.dbMyGreenDaoDbHelper 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/filesfile = 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"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="16dp"android:textColor="@color/red"android:text="使用说明:请按下面步骤操作"/><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="14dp"android:textColor="@color/red"android:text="        1、请先输入第一张卡号(卡号要大于0的数字);"/><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="14dp"android:textColor="@color/red"android:text="        2、点击确定按钮;"/><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="14dp"android:textColor="@color/red"android:text="        3、开始扫描NFC卡;"/><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="14dp"android:textColor="@color/red"android:text="        4、扫描完成后再点击最下方导出数据按钮即可;"/><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="14dp"android:textColor="@color/red"android:text="        5、到手机内存根目录中查看文件(文件格式为:时间_nfc_num_excel)。"/><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><TextViewandroid: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"/><EditTextandroid: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><Buttonandroid:id="@+id/btn_ok"android:layout_width="match_parent"android:layout_height="40dp"android:layout_marginTop="10dp"android:text="确定"android:textSize="16dp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="10dp"android:text="射频卡号"android:textSize="18dp"android:textColor="@color/colorPrimary"/><TextViewandroid:id="@+id/tv_content"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:layout_marginTop="10dp"/><Buttonandroid: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(){@Overridepublic 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;}}};@Overrideprotected 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() {@Overridepublic 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() {@Overridepublic 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()}};}@Overrideprotected 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);}}}}@Overrideprotected void onResume() {super.onResume();if (mNfcAdapter != null) {if (mNfcAdapter.isEnabled()) {mNfcAdapter.enableForegroundDispatch(this, pendingIntent, null, null);}}}@Overrideprotected 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;@Propertyprivate String cardFaceNum;//卡面上的编号@Propertyprivate 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形式导出数据相关推荐

  1. ic 卡获取帐号apdu指令_非接触IC卡片APDU指令系统介绍..docx

    CPU卡APDU指令介绍 APDU指令格式 根据7816-4规范,从终端发出的命令和卡片响应的信息必须遵从以下4种格式. 情形1: 命令 : CLA INS P1 P2 00 响应 : SW1 SW2 ...

  2. ic 卡获取帐号apdu指令_《全球行动》携手京东校园送福利 1000元京东卡等你拿

    7月22日,RTS手游<全球行动>正式和大家见面了,这款游戏一经面世就受到了玩家们的广泛关注,并且登上了IOS AppStore排行榜的第一名.为了让更多玩家感受到RTS手游<全球行 ...

  3. android获取内部存储卡里的文件,获取Android内部存储(外部SD卡)和扩展存储卡(TF卡)的路径...

    获取Android内部存储(外部SD卡)和扩展存储卡(TF卡)的路径 获取Android内部存储(外部SD卡)和扩展存储卡(TF卡)的路径 sd卡存储外部存储卡路径外部存储卡外部存储卡路径内置存储卡外 ...

  4. C# 服务端(API)连接 安卓设备 读取 IC卡(仅读取ID),RFID卡(包含ID卡),获取卡号(10进制)

    处理安卓设备NFC读卡 C#WebAPI做服务端 APP使用 uni-app开发, 走了不少弯路,这里记录一下 获取 卡10进制ID的方法 商米POS.安卓移动设备(手机)通过NFC功能 读取 IC卡 ...

  5. Android NFC读MifareClassic卡获取卡片ID 类型 扇区 存储空间

    1.首先要在AndroidManifest.xml中声明如下配置信息: 为了能够使用Android手机的NFC功能,需要在Manifest文件中添加相应的权限: 详细配置请参考-->Androi ...

  6. MTK平台获取本机的SIM卡中IMSI号

    要求在MTK的代码上获得本机上SIM卡的手机号码,查了很多资料后,结果是无法获得,原因是手机号码不是直接存储在SIM卡的,而是信号通过移动商(移动.联通或电信)时绑定的:IMSI是sim卡的一个属性号 ...

  7. MTK平台 获取本机的SIM卡中IMSI号

    公司MTK平台做开发,老板要求在MTK的代码上获得本机上SIM卡的手机号码,查了很多资料后,结果是无法获得,原因是手机号码不是直接存储在SIM卡的,而是信号通过移动商(移动.联通或电信)时绑定的:IM ...

  8. 存储控制器wwn号_查看HBA卡的WWN号

    AIX:    1,获得AIX主机连接的光纤设备: # lsdev -Cc adapter -S a | grep fcs fcs0      Available 09-08 FC Adapter f ...

  9. 存储控制器wwn号_在Linux/Unix平台查看HBA卡的WWN号 和状态

    对于不同版本的unix系统 查看你hba io状态的总结 AIX: 1,获得AIX主机连接的光纤设备: # lsdev -Cc adapter -S a | grep fcs fcs0     Ava ...

最新文章

  1. 场景法设计测试用例atm_软件测试零基础入门:常用八大测试用例设计方法,含案例...
  2. 24. Swap Nodes in Pairs 链表每2个点翻转一次
  3. Visual C++——《可视化编程技术》实验报告——资源的应用—反弹的球
  4. DCMTK:xmlParser的测试程序
  5. DWZ(JUI)的lookupGroup增加回调函数
  6. 19.7 主动模式和被动模式 19.8 添加监控主机 19.9 添加自定义模板 19.10 处理图形中的乱码 19.11 自动发现...
  7. iOS GZWaterfall任何形式的瀑布流
  8. jenkins使用Role Strategy管理用户权限
  9. mysql学生管理系统背景_学生基本系统管理
  10. hysys动态模拟教程_(转载)HYSYS-过程模拟软件-稳态模拟-第一部分(一)
  11. 奋斗在制造业----CAE行业感想
  12. 微信小程序之点点考勤经验总结
  13. 机器学习中的忒修斯之船:那些“愚弄”专家的著名悖论
  14. NFT协议标准梳理:除了ERC721和ERC1155,还有哪些?
  15. OAuth2 (二)
  16. 王子与公主的爱情故事新结局(转)
  17. android手机自带软件,为什么安卓手机的预装软件这么多
  18. postman测试接口成功,实际发请求时失败
  19. 期刊论文中公式和编号左右对齐
  20. 基于op07的k型热电偶测量电路_k型热电偶测温电路图解析

热门文章

  1. 磁盘管理器显示状态良好 计算机不显示,win7系统打开磁盘管理显示显示状态良好(有危险)的解决方法...
  2. 授人以渔:分享我的算法学习经验
  3. An assembly specified in the application dependencies manifest
  4. 部落冲突-建筑大师基地军队建筑介绍(兵营、建筑大师训练营、星空实验室、战争机器)
  5. 初识linux之进程
  6. 《Adobe After Effects CC 2017 经典教程》资源下载地址
  7. 时代变了,互联网与房产开发商能否找到真爱
  8. idea 2018.1破解激活方法,有效期至2099年
  9. python二次函数_python数据分析之曲线拟合:二次函数拟合
  10. 设计模式之桥接模式:如何实现抽象协议与不同实现的绑定?