編輯:關於Android編程
在開始之前可以先了解一些NFC的的基礎知識。
感謝Eternal_memory 和 SkySeraph
部分解析
BaseMethodWebActivity是Activity的子類
showToastMessage是單純的Toast通知
DataValues.NFCWriteLength 是長度限制
package com.Base;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import com.main.HZZH.MenuActivity;
import cn.data.bean.DataValues;
import cn.data.bean.PublicMethod;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.MifareClassic;
import android.nfc.tech.MifareUltralight;
import android.nfc.tech.Ndef;
import android.os.Bundle;
import android.os.Parcelable;
import android.provider.Settings;
import android.text.Selection;
import android.text.Spannable;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class NFCActivity extends BaseMethodWebActivity {
private static final DateFormat TIME_FORMAT = SimpleDateFormat
.getDateTimeInstance();
private NfcAdapter mAdapter;
private PendingIntent mPendingIntent;
private NdefMessage mNdefPushMessage;
private Tag intenTag;
private String NdefRecordTypeByte01 = "http://";
private String NdefRecordTypeByte02 = "www.baidu.com";
private String NdefRecordTypeByte03 = "?";
private int NFCDataType = 0;
private final int MifareClassicType = 1,MifareUltralightType = 2,OtherType = 3;
//扇位置
private short sectorAddress = 6;
//修改密碼
private byte[] myKeyA = {'h','9','^','a','-','8'};
private int NFCBarCodeLength = 16*3;
public EditText NFCEditText = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
resolveIntent(getIntent());
// 獲取默認的NFC控制器
mAdapter = NfcAdapter.getDefaultAdapter(this);
//攔截系統級的NFC掃描,例如掃描藍牙
mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
mNdefPushMessage = new NdefMessage(new NdefRecord[] { newTextRecord("",
Locale.ENGLISH, true) });
}
@Override
protected void onResume() {
super.onResume();
if (mAdapter != null) {
//隱式啟動
mAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
mAdapter.enableForegroundNdefPush(this, mNdefPushMessage);
}
}
@Override
protected void onPause() {
super.onPause();
if (mAdapter != null) {
//隱式啟動
mAdapter.disableForegroundDispatch(this);
mAdapter.disableForegroundNdefPush(this);
}
}
// //16進制字符串轉換為String
// private String hexString = "0123456789ABCDEF";
// public String decode(String bytes) {
// if (bytes.length() != 30) {
// return null;
// }
// ByteArrayOutputStream baos = new ByteArrayOutputStream(
// bytes.length() / 2);
// // 將每2位16進制整數組裝成一個字節
// for (int i = 0; i < bytes.length(); i += 2)
// baos.write((hexString.indexOf(bytes.charAt(i)) << 4 | hexString
// .indexOf(bytes.charAt(i + 1))));
// return new String(baos.toByteArray());
// }
// //字符序列轉換為16進制字符串
// private String bytesToHexString(byte[] src) {
// StringBuilder stringBuilder = new StringBuilder("0x");
// if (src == null || src.length <= 0) {
// return null;
// }
// char[] buffer = new char[2];
// for (int i = 0; i < src.length; i++) {
// buffer[0] = Character.forDigit((src[i] >>> 4) & 0x0F, 16);
// buffer[1] = Character.forDigit(src[i] & 0x0F, 16);
// System.out.println(buffer);
// stringBuilder.append(buffer);
// }
// return stringBuilder.toString();
// }
// // 字符序列轉換為16進制字符串
// private String bytesToHexString(byte[] src, boolean isPrefix) {
// StringBuilder stringBuilder = new StringBuilder();
// if (isPrefix == true) {
// stringBuilder.append("0x");
// }
// if (src == null || src.length <= 0) {
// return null;
// }
// char[] buffer = new char[2];
// for (int i = 0; i < src.length; i++) {
// buffer[0] = Character.toUpperCase(Character.forDigit(
// (src[i] >>> 4) & 0x0F, 16));
// buffer[1] = Character.toUpperCase(Character.forDigit(src[i] & 0x0F,
// 16));
// System.out.println(buffer);
// stringBuilder.append(buffer);
// }
// return stringBuilder.toString();
// }
/**
* 驗證扇sector的密碼.
* @param mfc
* @param sector
* @return
*/
private Boolean isKeyMifareClassicEnable(MifareClassic mfc,int sectorIndex){
boolean auth = false;
try {
auth = mfc.authenticateSectorWithKeyA(sectorIndex,
MifareClassic.KEY_DEFAULT);
if(!auth){
auth = mfc.authenticateSectorWithKeyA(sectorIndex,
myKeyA);
}
if(!auth){
auth = mfc.authenticateSectorWithKeyA(sectorIndex,
MifareClassic.KEY_NFC_FORUM);
}
} catch (IOException e) {
Log.e(TAG, "IOException while authenticateSectorWithKey MifareClassic...", e);
}
return auth;
}
/**
* 將數據寫入扇,扇中每塊必須16位
* @param mfc
* @param barCodeByte
* @param sectorIndex
* @return
*/
private Boolean writeMifareClassicBarCode(MifareClassic mfc,byte[] barCodeByte,int sectorIndex){
Boolean flag = false;
int bCount = mfc.getBlockCountInSector(sectorIndex);
//獲取block起始編號
int bIndex = mfc.sectorToBlock(sectorIndex);
int barIndexCount = barCodeByte.length/16 +1;
for(int i=0;i= 0){
for(int j = 0;j < 16;j++){
if(j+i*16 < barCodeByte.length){
bar[j] = barCodeByte[j+i*16];
}else{
bar[j] = 0;
}
}
}
try {
mfc.writeBlock(bIndex, bar);
bIndex++;
if(i == bIndex - 2){
flag = true;
}
} catch (IOException e) {
Log.e(TAG, "IOException while writeMifareClassicBarCode MifareClassic...", e);
}
}
return flag;
}
/**
* 寫入數據的方法
* @param barCode
* @param otherStr
*/
protected void WriteMothed(String barCode,String otherStr){
byte[] data = null;
if(otherStr !=null){
data = otherStr.getBytes(Charset.forName("UTF-8"));
}
if(barCode == null || barCode.length() < 4 || barCode.getBytes().length > NFCBarCodeLength){
showToastMessage("要寫入的數據不正確!");
return ;
}
if(data.length > DataValues.NFCWriteLength){
showToastMessage("非條碼數據不超過"+DataValues.NFCWriteLength+"!");
return ;
}
if (intenTag != null) {
switch (NFCDataType) {
case MifareClassicType:
//MifareClassicl類型寫入
MifareClassic mfc = MifareClassic.get(intenTag);
int otherStrIndex = 0;
try {
int bAllCount = 0;
if(barCode != null){
bAllCount = 1;
}
if(data != null){
bAllCount += data.length/16 + 1;
}
for(int j = sectorAddress;j < 16 && j < bAllCount+sectorAddress;j++){
//在刪去1 塊4、5 寫入數據
mfc.connect();
boolean auth = false;
byte[] KeyValue = null;
if (isKeyMifareClassicEnable(mfc,j)) {
KeyValue = mfc.readBlock(4*j+3);
}
mfc.close();
mfc.connect();
if (isKeyMifareClassicEnable(mfc,j) && KeyValue != null) {
if(j == sectorAddress){
writeMifareClassicBarCode(mfc,barCode.getBytes(Charset.forName("UTF-8")),j);
}else{
int count = 16*3;
byte[] strByte = new byte[count];
for(int z = 0;z < count;z++){
if(otherStrIndex < data.length){
strByte[z] = data[otherStrIndex];
otherStrIndex++;
}else{
break;
}
}
writeMifareClassicBarCode(mfc, strByte, j);
}
//修改鍵值,keyA存放在每扇最後一塊
for(int i = 0;i<6;i++){
KeyValue[i] = myKeyA[i];
}
mfc.writeBlock(4*j+3, KeyValue);
showToastMessage("寫入數據成功!");
mfc.close();
}else{
showToastMessage("塊密碼不正確!");
}
}
} catch (Exception e) {
showToastMessage("寫入數據失敗!");
Log.e(TAG, "IOException while write MifareClassic...", e);
} finally {
try {
mfc.close();
} catch (IOException e) {
Log.e(TAG, "IOException while closing MifareClassic...", e);
}
}
break;
case MifareUltralightType:
MifareUltralight ultralight = MifareUltralight.get(intenTag);
try {
ultralight.connect();
//每頁4個字符2個漢字,共16頁
ultralight.writePage(4, "abcd".getBytes(Charset.forName("US-ASCII")));
ultralight.writePage(5, "efgh".getBytes(Charset.forName("US-ASCII")));
ultralight.writePage(6, "ijkl".getBytes(Charset.forName("US-ASCII")));
ultralight.writePage(7, "mnop".getBytes(Charset.forName("US-ASCII")));
showToastMessage("寫入數據成功!");
} catch (Exception e) {
showToastMessage("寫入數據失敗!");
Log.e(TAG, "IOException while write MifareUltralight...", e);
} finally {
try {
ultralight.close();
} catch (IOException e) {
Log.e(TAG, "IOException while closing MifareUltralight...", e);
}
}
break;
case OtherType:
//新建NdefRecord數組,本例中數組只有一個元素
NdefRecord re = null;
try{
re = createRecord(otherStr+barCode);
}catch (Exception e) {
showToastMessage("新建NdefRecord數組失敗!");
Log.e(TAG, "IOException while create other NdefRecord...", e);
return;
}
NdefRecord[] records = {re};
//新建一個NdefMessage實例
NdefMessage message = new NdefMessage(records);
// 解析TAG獲取到NDEF實例
Ndef ndef = Ndef.get(intenTag);
if(ndef != null ){
// 打開連接
try {
ndef.connect();
ndef.writeNdefMessage(message);
showToastMessage("寫入數據成功!");
} catch (Exception e) {
showToastMessage("寫入數據失敗!");
Log.e(TAG, "IOException while write other...", e);
} finally{
try {
ndef.close();
} catch (Exception e) {
Log.e(TAG, "IOException while closing other...", e);
}
}
}
break;
}
} else {
showToastMessage("設備與nfc卡連接斷開,請重新連接...");
}
}
/**
* 創建NdefRecord實例
* @param str
* @return
* @throws UnsupportedEncodingException
*/
protected NdefRecord createRecord( String str ) throws UnsupportedEncodingException {
NdefRecord textRecord = NdefRecord.createMime(NdefRecordTypeByte01+
NdefRecordTypeByte02+
NdefRecordTypeByte03,
str.getBytes(Charset.forName("US-ASCII")));
// //組裝字符串,准備好你要寫入的信息
// String msg = str;
// //將字符串轉換成字節數組
// byte[] textBytes = msg.getBytes();
// //將字節數組封裝到一個NdefRecord實例中去
// NdefRecord textRecord = new NdefRecord(NdefRecord.TNF_MIME_MEDIA,
// NdefRecordTypeByte.getBytes(), new byte[] {}, textBytes);
return textRecord;
}
private NdefRecord newTextRecord(String text, Locale locale,
boolean encodeInUtf8) {
byte[] langBytes = locale.getLanguage().getBytes(
Charset.forName("UTF-8"));
Charset utfEncoding = encodeInUtf8 ? Charset.forName("UTF-8") : Charset
.forName("UTF-16");
byte[] textBytes = text.getBytes(utfEncoding);
int utfBit = encodeInUtf8 ? 0 : (1 << 7);
char status = (char) (utfBit + langBytes.length);
byte[] data = new byte[1 + langBytes.length + textBytes.length];
data[0] = (byte) status;
System.arraycopy(langBytes, 0, data, 1, langBytes.length);
System.arraycopy(textBytes, 0, data, 1 + langBytes.length,
textBytes.length);
return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT,
new byte[0], data);
}
// private void showWirelessSettingsDialog() {
// AlertDialog.Builder builder = new AlertDialog.Builder(this);
// builder.setMessage("提示");
// builder.setPositiveButton(android.R.string.ok,
// new DialogInterface.OnClickListener() {
// public void onClick(DialogInterface dialogInterface, int i) {
// Intent intent = new Intent(
// Settings.ACTION_WIRELESS_SETTINGS);
// startActivity(intent);
// }
// });
// builder.setNegativeButton(android.R.string.cancel,
// new DialogInterface.OnClickListener() {
// public void onClick(DialogInterface dialogInterface, int i) {
// finish();
// }
// });
// builder.create().show();
// return;
// }
/**
* 初步判斷是什麼類型NFC卡
* @param intent
*/
private void resolveIntent(Intent intent) {
String action = intent.getAction();
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)
|| NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
Parcelable[] rawMsgs = intent
.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
NdefMessage[] msgs;
intenTag = intent
.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if (rawMsgs != null) {
msgs = new NdefMessage[rawMsgs.length];
for (int i = 0; i < rawMsgs.length; i++) {
msgs[i] = (NdefMessage) rawMsgs[i];
}
} else {
// Unknown tag type
byte[] empty = new byte[0];
byte[] id = intent.getByteArrayExtra(NfcAdapter.EXTRA_ID);
byte[] payload = dumpTagData(intenTag).getBytes();
NdefRecord record = new NdefRecord(NdefRecord.TNF_UNKNOWN,
empty, id, payload);
NdefMessage msg = new NdefMessage(new NdefRecord[] { record });
msgs = new NdefMessage[] { msg };
}
// Setup the views
getNFCData(msgs);
}
}
/**
* 解析讀取的數據
* @param p
* @return
*/
private String dumpTagData(Parcelable p) {
StringBuilder sb = new StringBuilder();
Tag tag = (Tag) p;
// String prefix = "android.nfc.tech.";
// sb.append("Technologies: ");
// for (String tech : tag.getTechList()) {
// sb.append(tech.substring(prefix.length()));
// sb.append(", ");
// }
NFCDataType = 3;
// sb.delete(sb.length() - 2, sb.length());
for (String tech : tag.getTechList()) {
if (tech.equals(MifareClassic.class.getName())) {
MifareClassic mifareTag = MifareClassic.get(tag);
String type = "Unknown";
switch (mifareTag.getType()) {
case MifareClassic.TYPE_CLASSIC:
type = "Classic";
break;
case MifareClassic.TYPE_PLUS:
type = "Plus";
break;
case MifareClassic.TYPE_PRO:
type = "Pro";
break;
}
NFCDataType = 1;
try {
//獲取 扇sectorAddress 塊barCdeBlock 的內容
mifareTag.connect();
int bCount;
int bIndex;
if (isKeyMifareClassicEnable(mifareTag,sectorAddress)) {
// 讀取扇區中的塊總數
bCount = mifareTag.getBlockCountInSector(sectorAddress);
//獲取扇區塊的起始編號
bIndex = mifareTag.sectorToBlock(sectorAddress);
for (int i = 0; i < bCount-1; i++) {
byte[] data = mifareTag.readBlock(bIndex);
sb.append(new String(data, Charset.forName("UTF-8")));
bIndex++;
}
} else {
showToastMessage("驗證失敗");
}
} catch (Exception e) {
showToastMessage("讀取數據失敗!");
Log.e(TAG, "IOException while read MifareClassic...", e);
}finally{
try {
mifareTag.close();
} catch (IOException e) {
Log.e(TAG, "IOException while closing MifareClassic...", e);
}
}
}
if (tech.equals(MifareUltralight.class.getName())) {
sb.append('\n');
MifareUltralight mifareUlTag = MifareUltralight.get(tag);
String type = "Unknown";
switch (mifareUlTag.getType()) {
case MifareUltralight.TYPE_ULTRALIGHT:
type = "Ultralight";
break;
case MifareUltralight.TYPE_ULTRALIGHT_C:
type = "Ultralight C";
break;
}
NFCDataType = 2;
sb.append("Mifare Ultralight type: ");
sb.append(type);
}
}
return sb.toString();
}
// private String getHex(byte[] bytes) {
// StringBuilder sb = new StringBuilder();
// for (int i = bytes.length - 1; i >= 0; --i) {
// int b = bytes[i] & 0xff;
// if (b < 0x10)
// sb.append('0');
// sb.append(Integer.toHexString(b));
// if (i > 0) {
// sb.append(" ");
// }
// }
// return sb.toString();
// }
// private long getDec(byte[] bytes) {
// long result = 0;
// long factor = 1;
// for (int i = 0; i < bytes.length; ++i) {
// long value = bytes[i] & 0xffl;
// result += value * factor;
// factor *= 256l;
// }
// return result;
// }
// private long getReversed(byte[] bytes) {
// long result = 0;
// long factor = 1;
// for (int i = bytes.length - 1; i >= 0; --i) {
// long value = bytes[i] & 0xffl;
// result += value * factor;
// factor *= 256l;
// }
// return result;
// }
/**
* 取出NFC掃描的數據
* @param msgs
*/
private void getNFCData(NdefMessage[] msgs) {
if (msgs == null || msgs.length == 0) {
return;
}
//將字節數組轉換成字符串
String str="";
byte[] data = null;
for(NdefMessage obj:msgs){
NdefRecord record[] = obj.getRecords();
for(NdefRecord recordObj:record){
if(data == null){
data = recordObj.getPayload();
}else{
byte[] oldData = data;
data = new byte[oldData.length + recordObj.getPayload().length];
for(int i = 0;i < oldData.length;i++){
data[i] = oldData[i];
}
for(int i = 0;i 3){
NFCEditText.setText(str);
CharSequence text = NFCEditText.getText();
if (text instanceof Spannable) {
Spannable spanText = (Spannable)text;
Selection.setSelection(spanText, text.length());
}
if(NFCEditText.getText().toString().trim().length() > 3){
KeyEvent event = new KeyEvent(KeyEvent.ACTION_UP,KeyEvent.KEYCODE_ENTER);
NFCEditText.dispatchKeyEvent(event);
}
}
}
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if(event.getKeyCode() == KeyEvent.KEYCODE_ENTER){
/*隱藏軟鍵盤*/
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if(inputMethodManager.isActive()){
inputMethodManager.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), 0);
}
return true;
}
return super.dispatchKeyEvent(event);
}
//獲取系統隱式啟動的
@Override
public void onNewIntent(Intent intent) {
setIntent(intent);
resolveIntent(intent);
}
}
其他如下:

1 AndroidMainifest.xml中添加權限
2、添加過濾器

3、添加支持的協議
android.nfc.tech.IsoDep
android.nfc.tech.NfcA
android.nfc.tech.NfcB
android.nfc.tech.NfcF
android.nfc.tech.NfcV
android.nfc.tech.Ndef
android.nfc.tech.NdefFormatable
android.nfc.tech.MifareClassic
android.nfc.tech.MifareUltralight
Android如何適配雙卡
這裡僅以獲取sim卡的IMSI接口(getSubscriberId)和發短信接口(sendTextMessage)為例來詳細講解一下Android5.0-6.0雙卡適配的
Android開發筆記之:在ImageView上繪制圓環的實現方法
繪制圓環其實很簡單,有大概以下三種思路. 這裡先說網上提到的一種方法。思路是先繪制內圓,然後繪制圓環(圓環的寬度就是paint設置的paint.setStrokeWidt
Android 服務(service)的生命周期以及利用bindservice調用服務裡面的方法
服務的生命周期 服務的生命周期跟啟動服務的方法有關: 當采用Context.startService()方法啟動服務,與之有關的生命周期方法 onCreate()
關於ListView的總結
關於ListView網上也有許多的介紹,我選了一些結合到了一起,僅供參考。在android開發中ListView是比較常用的組件官方API地址:http://www.an