扫描NFC卡,获取卡号存储到本地并可以以Excel形式导出数据

这是一个使用支持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源码工程文件,有需要的就勤劳点去上面粘贴吧,后面看情况再传整个工程源码。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时代新人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值