編輯:關於Android編程
一、首先說明:藍牙通信必須用手機測試,因為avd裡沒有相關的硬件,會報錯!
好了,看看最後的效果圖:
二、概述:
1.判斷是否支持Bluetooth
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<DeviceBean> 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<DeviceBean>(); mAdapter = new ChatListAdapter(this, mDatas); mBtAdapter = BluetoothAdapter.getDefaultAdapter(); } /** * 列出所有的藍牙設備 */ private void init() { Log.i("tag", "mBtAdapter=="+ mBtAdapter); //根據適配器得到所有的設備信息 Set<BluetoothDevice> 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<DeviceBean> 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<DeviceBean>(); 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; } }
三、相關代碼下載
demo下載:http://xiazai.jb51.net/201701/yuanma/App_BlueTooth_jb51.rar
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持本站。
我們打開QQ空間的時候有個箭頭按鈕點擊之後彈出PopupWindow會根據位置的變化顯示在箭頭的上方還是下方,比普通的PopupWindow彈在屏幕中間顯示好看的多。先看
正文結構型包含以下類型:適配器 Adapter Class/Object 橋接 Bridge 組合 Composite 裝飾 Decorator 外觀 Facade 享元
本文實例講述了Android實現音量調節的方法。分享給大家供大家參考。具體如下:main.xml布局文件:<?xml version=1.0 encodin
今年3月,Google 破天荒提前半年發布了 Android N 開發者預覽版。當然,作為一個不合格的谷粉並沒有第一時間體驗安裝,因為至今仍然能夠回憶起來去年今日此門中(