編輯:關於Android編程
一、首先說明:藍牙通信必須用手機測試,因為avd裡沒有相關的硬件,會報錯!
好了,看看最後的效果圖:
二、概述:
1.判斷是否支持Bluetooth<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(bluetoothAdapter == null) {
//the device doesn't support bluetooth
} else {
//the device support bluetooth
}
2.如果支持,打開Bluetooth
if(!bluetoothAdapter.isEnable()) {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent,REQUEST_ENABLE_BT);
}
3.監視Bluetooth打開狀態
BroadcastReceiver bluetoothState = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String stateExtra = BluetoothAdapter.EXTRA_STATE;
int state = intent.getIntExtra(stateExtra, -1);
switch(state) {
case BluetoothAdapter.STATE_TURNING_ON:
break;
case BluetoothAdapter.STATE_ON:
break;
case BluetoothAdapter.STATE_TURNING_OFF:
break;
case BluetoothAdapter.STATE_OFF:
break;
}
}
}
registerReceiver(bluetoothState,new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
4.設置本地設備可以被其它設備搜索
Intent discoveryIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
startActivityForResult(discoveryIntent,REQUEST_DISCOVERY);
BroadcastReceiver discovery = new BroadcastReceiver() {
@Override
public void onRecevie(Content context, Intent intent) {
String scanMode = BluetoothAdapter.EXTRA_SCAN_MODE;
String preScanMode = BluetoothAdapter.EXTRA_PREVIOUS_SCAN_MODE;
int mode = intent.getIntExtra(scanMode);
}
}
registerReceiver(discovery,new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
5.搜索設備
開始搜索 bluetoothAdapter.startDiscovery();
停止搜索 bluetoothAdapter.cancelDiscovery();
當發現一個設備時,系統會發出ACTION_FOUND廣播消息,我們可以實現接收這個消息的BroadcastReceiver
BroadcastReceiver deviceFound = new BroadcastReceiver() {
@Override
public void onReceiver(Content content, Intent intent) {
String remoteDeviceName = intent.getStringExtra(BluetoothAdapter.EXTRA_NAME);
BluetoothDevice remoteDevice = intent.getParcelableExtra(BluetoothAdapter.EXTRA_DEVICE);
}
}
registerReceiver(deviceFound, new IntentFilter(BluetoothAdapter.ACTION_FOUND);
6.連接設備
連接兩個藍牙設備要分別實現服務器端(BluetoothServerSocket)和客戶端(BluetoothSocket),這點與J2SE中的
ServerSocket和Socket很類似。
BluetoothServerSocket在服務器端調用方法accept()監聽,當有客戶端請求到來時,accept()方法返回BluetoothSocket,客戶端得到後,兩端便可以通信。通過InputStream和OutputStream來實現數據的傳輸。
accept方法是阻塞的,所以不能放在UI線程中,當用到BluetoothServerSocket和BluetoothSocket時,通常把它們放在各自的新線程中。
三、如何實現
以下是開發中的幾個關鍵步驟:
1)首先開啟藍牙
2)搜索可用設備
3)創建藍牙socket,獲取輸入輸出流
4)讀取和寫入數據
5)斷開連接關閉藍牙
1、因為有頁面切換,這裡我使用了TabHost,但原來的效果不好,沒有動畫,那只好自己復寫了
/**
* 帶有動畫效果的TabHost
*
* @Project App_Bluetooth
* @Package com.android.bluetooth
* @author chenlin
* @version 1.0
* @Date 2013年6月2日
* @Note TODO
*/
public class AnimationTabHost extends TabHost {
private int mCurrentTabID = 0;//當前的tabId
private final long mDuration = 400;//動畫時間
public AnimationTabHost(Context context) {
this(context, null);
}
public AnimationTabHost(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* 切換動畫
*/
@Override
public void setCurrentTab(int index) {
//向右平移
if (index > mCurrentTabID) {
TranslateAnimation translateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF,
-1.0f, Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f);
translateAnimation.setDuration(mDuration);
getCurrentView().startAnimation(translateAnimation);
//向左平移
} else if (index < mCurrentTabID) {
TranslateAnimation translateAnimation = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 1.0f, Animation.RELATIVE_TO_SELF, 0f,
Animation.RELATIVE_TO_SELF, 0f);
translateAnimation.setDuration(mDuration);
getCurrentView().startAnimation(translateAnimation);
}
super.setCurrentTab(index);
//-----方向平移------------------------------
if (index > mCurrentTabID) {
TranslateAnimation translateAnimation = new TranslateAnimation( //
Animation.RELATIVE_TO_PARENT, 1.0f,// RELATIVE_TO_SELF
Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT, 0f);
translateAnimation.setDuration(mDuration);
getCurrentView().startAnimation(translateAnimation);
} else if (index < mCurrentTabID) {
TranslateAnimation translateAnimation = new TranslateAnimation(
Animation.RELATIVE_TO_PARENT, -1.0f, Animation.RELATIVE_TO_PARENT, 0f, Animation.RELATIVE_TO_PARENT, 0f,
Animation.RELATIVE_TO_PARENT, 0f);
translateAnimation.setDuration(mDuration);
getCurrentView().startAnimation(translateAnimation);
}
mCurrentTabID = index;
}
}
2、先搭建好主頁,使用復寫的TabHost滑動,如何滑動,根據狀態,有三種狀態
/**
* 主頁
*
* @Project App_Bluetooth
* @Package com.android.bluetooth
* @author chenlin
* @version 1.0
* @Date 2013年6月2日
*/
@SuppressWarnings("deprecation")
public class BluetoothActivity extends TabActivity {
static AnimationTabHost mTabHost;//動畫tabhost
static String BlueToothAddress;//藍牙地址
static Type mType = Type.NONE;//類型
static boolean isOpen = false;
//類型:
enum Type {
NONE, SERVICE, CILENT
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
initTab();
}
private void initTab() {
//初始化
mTabHost = (AnimationTabHost) getTabHost();
//添加tab
mTabHost.addTab(mTabHost.newTabSpec("Tab1").setIndicator("設備列表", getResources().getDrawable(android.R.drawable.ic_menu_add))
.setContent(new Intent(this, DeviceActivity.class)));
mTabHost.addTab(mTabHost.newTabSpec("Tab2").setIndicator("會話列表", getResources().getDrawable(android.R.drawable.ic_menu_add))
.setContent(new Intent(this, ChatActivity.class)));
//添加監聽
mTabHost.setOnTabChangedListener(new OnTabChangeListener() {
public void onTabChanged(String tabId) {
if (tabId.equals("Tab1")) {
//TODO
}
}
});
//默認在第一個tabhost上面
mTabHost.setCurrentTab(0);
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
Toast.makeText(this, "address:", Toast.LENGTH_SHORT).show();
}
}
3、有了主頁,就開始分別實現兩個列表頁面,一個是尋找設備頁面DeviceActivity.java,另一個是會話頁面ChatActivity.java
1)設備頁面DeviceActivity.java
/**
* 發現的設備列表
* @Project App_Bluetooth
* @Package com.android.bluetooth
* @author chenlin
* @version 1.0
* @Date 2013年6月2日
* @Note TODO
*/
public class DeviceActivity extends Activity {
private ListView mListView;
//數據
private ArrayList mDatas;
private Button mBtnSearch, mBtnService;
private ChatListAdapter mAdapter;
//藍牙適配器
private BluetoothAdapter mBtAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.devices);
initDatas();
initViews();
registerBroadcast();
init();
}
private void initDatas() {
mDatas = new ArrayList();
mAdapter = new ChatListAdapter(this, mDatas);
mBtAdapter = BluetoothAdapter.getDefaultAdapter();
}
/**
* 列出所有的藍牙設備
*/
private void init() {
Log.i("tag", "mBtAdapter=="+ mBtAdapter);
//根據適配器得到所有的設備信息
Set deviceSet = mBtAdapter.getBondedDevices();
if (deviceSet.size() > 0) {
for (BluetoothDevice device : deviceSet) {
mDatas.add(new DeviceBean(device.getName() + "\n" + device.getAddress(), true));
mAdapter.notifyDataSetChanged();
mListView.setSelection(mDatas.size() - 1);
}
} else {
mDatas.add(new DeviceBean("沒有配對的設備", true));
mAdapter.notifyDataSetChanged();
mListView.setSelection(mDatas.size() - 1);
}
}
/**
* 注冊廣播
*/
private void registerBroadcast() {
//設備被發現廣播
IntentFilter discoveryFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
this.registerReceiver(mReceiver, discoveryFilter);
// 設備發現完成
IntentFilter foundFilter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
this.registerReceiver(mReceiver, foundFilter);
}
/**
* 初始化視圖
*/
private void initViews() {
mListView = (ListView) findViewById(R.id.list);
mListView.setAdapter(mAdapter);
mListView.setFastScrollEnabled(true);
mListView.setOnItemClickListener(mDeviceClickListener);
mBtnSearch = (Button) findViewById(R.id.start_seach);
mBtnSearch.setOnClickListener(mSearchListener);
mBtnService = (Button) findViewById(R.id.start_service);
mBtnService.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
BluetoothActivity.mType = Type.SERVICE;
BluetoothActivity.mTabHost.setCurrentTab(1);
}
});
}
/**
* 搜索監聽
*/
private OnClickListener mSearchListener = new OnClickListener() {
@Override
public void onClick(View arg0) {
if (mBtAdapter.isDiscovering()) {
mBtAdapter.cancelDiscovery();
mBtnSearch.setText("重新搜索");
} else {
mDatas.clear();
mAdapter.notifyDataSetChanged();
init();
/* 開始搜索 */
mBtAdapter.startDiscovery();
mBtnSearch.setText("??停止搜索");
}
}
};
/**
* 點擊設備監聽
*/
private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {
public void onItemClick(AdapterView parent, View view, int position, long id) {
DeviceBean bean = mDatas.get(position);
String info = bean.message;
String address = info.substring(info.length() - 17);
BluetoothActivity.BlueToothAddress = address;
AlertDialog.Builder stopDialog = new AlertDialog.Builder(DeviceActivity.this);
stopDialog.setTitle("連接");//標題
stopDialog.setMessage(bean.message);
stopDialog.setPositiveButton("連接", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
mBtAdapter.cancelDiscovery();
mBtnSearch.setText("重新搜索");
BluetoothActivity.mType = Type.CILENT;
BluetoothActivity.mTabHost.setCurrentTab(1);
dialog.cancel();
}
});
stopDialog.setNegativeButton("取消", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
BluetoothActivity.BlueToothAddress = null;
dialog.cancel();
}
});
stopDialog.show();
}
};
/**
* 發現設備廣播
*/
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// 獲得設備信息
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// 如果綁定的狀態不一樣
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
mDatas.add(new DeviceBean(device.getName() + "\n" + device.getAddress(), false));
mAdapter.notifyDataSetChanged();
mListView.setSelection(mDatas.size() - 1);
}
// 如果搜索完成了
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
setProgressBarIndeterminateVisibility(false);
if (mListView.getCount() == 0) {
mDatas.add(new DeviceBean("?沒有發現藍牙設備", false));
mAdapter.notifyDataSetChanged();
mListView.setSelection(mDatas.size() - 1);
}
mBtnSearch.setText("重新搜索");
}
}
};
@Override
public void onStart() {
super.onStart();
if (!mBtAdapter.isEnabled()) {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent, 3);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mBtAdapter != null) {
mBtAdapter.cancelDiscovery();
}
this.unregisterReceiver(mReceiver);
}
}
2)會話頁面ChatActivity.java
/**
* 會話界面
*
* @Project App_Bluetooth
* @Package com.android.bluetooth
* @author chenlin
* @version 1.0
* @Date 2013年3月2日
* @Note TODO
*/
public class ChatActivity extends Activity implements OnItemClickListener, OnClickListener {
private static final int STATUS_CONNECT = 0x11;
private ListView mListView;
private ArrayList mDatas;
private Button mBtnSend;// 發送按鈕
private Button mBtnDisconn;// 斷開連接
private EditText mEtMsg;
private DeviceListAdapter mAdapter;
/* 一些常量,代表服務器的名稱 */
public static final String PROTOCOL_SCHEME_L2CAP = "btl2cap";
public static final String PROTOCOL_SCHEME_RFCOMM = "btspp";
public static final String PROTOCOL_SCHEME_BT_OBEX = "btgoep";
public static final String PROTOCOL_SCHEME_TCP_OBEX = "tcpobex";
// 藍牙服務端socket
private BluetoothServerSocket mServerSocket;
// 藍牙客戶端socket
private BluetoothSocket mSocket;
// 設備
private BluetoothDevice mDevice;
private BluetoothAdapter mBluetoothAdapter;
// --線程類-----------------
private ServerThread mServerThread;
private ClientThread mClientThread;
private ReadThread mReadThread;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chat);
initDatas();
initViews();
initEvents();
}
private void initEvents() {
mListView.setOnItemClickListener(this);
// 發送信息
mBtnSend.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
String text = mEtMsg.getText().toString();
if (!TextUtils.isEmpty(text)) {
// 發送信息
sendMessageHandle(text);
mEtMsg.setText("");
mEtMsg.clearFocus();
// 隱藏軟鍵盤
InputMethodManager manager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
manager.hideSoftInputFromWindow(mEtMsg.getWindowToken(), 0);
} else
Toast.makeText(ChatActivity.this, "發送內容不能為空!", Toast.LENGTH_SHORT).show();
}
});
// 關閉會話
mBtnDisconn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (BluetoothActivity.mType == Type.CILENT) {
shutdownClient();
} else if (BluetoothActivity.mType == Type.SERVICE) {
shutdownServer();
}
BluetoothActivity.isOpen = false;
BluetoothActivity.mType = Type.NONE;
Toast.makeText(ChatActivity.this, "已斷開連接!", Toast.LENGTH_SHORT).show();
}
});
}
private void initViews() {
mListView = (ListView) findViewById(R.id.list);
mListView.setAdapter(mAdapter);
mListView.setFastScrollEnabled(true);
mEtMsg = (EditText) findViewById(R.id.MessageText);
mEtMsg.clearFocus();
mBtnSend = (Button) findViewById(R.id.btn_msg_send);
mBtnDisconn = (Button) findViewById(R.id.btn_disconnect);
}
private void initDatas() {
mDatas = new ArrayList();
mAdapter = new DeviceListAdapter(this, mDatas);
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
/**
* 信息處理
*/
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
String info = (String) msg.obj;
switch (msg.what) {
case STATUS_CONNECT:
Toast.makeText(ChatActivity.this, info, 0).show();
break;
}
if (msg.what == 1) {
mDatas.add(new DeviceBean(info, true));
mAdapter.notifyDataSetChanged();
mListView.setSelection(mDatas.size() - 1);
}else {
mDatas.add(new DeviceBean(info, false));
mAdapter.notifyDataSetChanged();
mListView.setSelection(mDatas.size() - 1);
}
}
};
@Override
public void onResume() {
super.onResume();
if (BluetoothActivity.isOpen) {
Toast.makeText(this, "連接已經打開,可以通信。如果要再建立連接,請先斷開", Toast.LENGTH_SHORT).show();
return;
}
if (BluetoothActivity.mType == Type.CILENT) {
String address = BluetoothActivity.BlueToothAddress;
if (!"".equals(address)) {
mDevice = mBluetoothAdapter.getRemoteDevice(address);
mClientThread = new ClientThread();
mClientThread.start();
BluetoothActivity.isOpen = true;
} else {
Toast.makeText(this, "address is null !", Toast.LENGTH_SHORT).show();
}
} else if (BluetoothActivity.mType == Type.SERVICE) {
mServerThread = new ServerThread();
mServerThread.start();
BluetoothActivity.isOpen = true;
}
}
// 客戶端線程
private class ClientThread extends Thread {
public void run() {
try {
mSocket = mDevice.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
Message msg = new Message();
msg.obj = "請稍候,正在連接服務器:" + BluetoothActivity.BlueToothAddress;
msg.what = STATUS_CONNECT;
mHandler.sendMessage(msg);
mSocket.connect();
msg = new Message();
msg.obj = "已經連接上服務端!可以發送信息。";
msg.what = STATUS_CONNECT;
mHandler.sendMessage(msg);
// 啟動接受數據
mReadThread = new ReadThread();
mReadThread.start();
} catch (IOException e) {
Message msg = new Message();
msg.obj = "連接服務端異常!斷開連接重新試一試。";
msg.what = STATUS_CONNECT;
mHandler.sendMessage(msg);
}
}
};
// 開啟服務器
private class ServerThread extends Thread {
public void run() {
try {
// 創建一個藍牙服務器 參數分別:服務器名稱、UUID
mServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(PROTOCOL_SCHEME_RFCOMM,
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
Message msg = new Message();
msg.obj = "請稍候,正在等待客戶端的連接...";
msg.what = STATUS_CONNECT;
mHandler.sendMessage(msg);
/* 接受客戶端的連接請求 */
mSocket = mServerSocket.accept();
msg = new Message();
msg.obj = "客戶端已經連接上!可以發送信息。";
msg.what = STATUS_CONNECT;
mHandler.sendMessage(msg);
// 啟動接受數據
mReadThread = new ReadThread();
mReadThread.start();
} catch (IOException e) {
e.printStackTrace();
}
}
};
/* 停止服務器 */
private void shutdownServer() {
new Thread() {
public void run() {
if (mServerThread != null) {
mServerThread.interrupt();
mServerThread = null;
}
if (mReadThread != null) {
mReadThread.interrupt();
mReadThread = null;
}
try {
if (mSocket != null) {
mSocket.close();
mSocket = null;
}
if (mServerSocket != null) {
mServerSocket.close();
mServerSocket = null;
}
} catch (IOException e) {
Log.e("server", "mserverSocket.close()", e);
}
};
}.start();
}
/* ?停止客戶端連接 */
private void shutdownClient() {
new Thread() {
public void run() {
if (mClientThread != null) {
mClientThread.interrupt();
mClientThread = null;
}
if (mReadThread != null) {
mReadThread.interrupt();
mReadThread = null;
}
if (mSocket != null) {
try {
mSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
mSocket = null;
}
};
}.start();
}
// 發送數據
private void sendMessageHandle(String msg) {
if (mSocket == null) {
Toast.makeText(this, "沒有連接", Toast.LENGTH_SHORT).show();
return;
}
try {
OutputStream os = mSocket.getOutputStream();
os.write(msg.getBytes());
mDatas.add(new DeviceBean(msg, false));
mAdapter.notifyDataSetChanged();
mListView.setSelection(mDatas.size() - 1);
} catch (IOException e) {
e.printStackTrace();
}
}
// 讀取數據
private class ReadThread extends Thread {
public void run() {
byte[] buffer = new byte[1024];
int bytes;
InputStream is = null;
try {
is = mSocket.getInputStream();
while (true) {
if ((bytes = is.read(buffer)) > 0) {
byte[] buf_data = new byte[bytes];
for (int i = 0; i < bytes; i++) {
buf_data[i] = buffer[i];
}
String s = new String(buf_data);
Message msg = new Message();
msg.obj = s;
msg.what = 1;
mHandler.sendMessage(msg);
}
}
} catch (IOException e1) {
e1.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
@Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
}
@Override
public void onClick(View view) {
}
@Override
protected void onDestroy() {
super.onDestroy();
if (BluetoothActivity.mType == Type.CILENT) {
shutdownClient();
} else if (BluetoothActivity.mType == Type.SERVICE) {
shutdownServer();
}
BluetoothActivity.isOpen = false;
BluetoothActivity.mType = Type.NONE;
}
}
三、相關代碼下載
鏈接:http://pan.baidu.com/s/1eSNRvzG 密碼:awgv
手機中保存並記錄著很多我們個人數據,比如浏覽器記錄、微信賬號、聊天記錄等,這些信息如果被有心人盯上自然後後患無窮。有時候我們會將手機借給好友,或是購新機後在
今天在慕課網看了一個視頻,介紹了幾種圖像處理的方法,其中有一種就是鏡面效果,但是他是通過自定義view的方式實現的,但是算法都大同小異。他的自定義view:package
0、基礎回顧PropertyAnimation,屬性動畫,顧名思義就是利用對象的屬性變化形成動畫的效果。屬性動畫的類可以用Animator這個抽象類來表示,通常使用它的子
Android事件攔截機制Android中事件的傳遞和攔截和View樹結構是相關聯的,在View樹中,分為葉子節點和普通節點,普通節點有子節點只能是ViewGroup,葉