天天看點

Android項目實戰(二十六):藍牙連接配接硬體裝置開發規範流程

原文: Android項目實戰(二十六):藍牙連接配接硬體裝置開發規範流程

前言:

  最近接觸藍牙開發,主要是通過藍牙連接配接擷取傳感器硬體裝置的資料,并進行處理。

  網上學習一番,現整理出一套比較标準的 操作流程代碼。

  如果大家看得懂,将來隻需要改下 硬體裝置的MAC碼 和 改下對接收資料的處理 即可。  一切都是套路~~~

現在以一個小型項目需求來學習Android藍牙程式設計

需求: 通過藍牙擷取硬體資料,并顯示在一個随資料即時變化的動态折線圖中。

實作思路:

(1) 配對藍牙裝置  

(2) 連接配接藍牙裝置    ,根據MAC位址,代碼中修改

(3) 接收資料

(4) 處理資料          ,根據硬體廠商提供給你的資料轉換公式,在BluetoothService類中 修改

(5) 傳資料給折線圖,展現實時變化

-----------------------------------------------------------------------

藍牙知識了解:

(1)、MAC位址:每個裝置都有全球唯一的,根據此MAC位址判斷藍牙裝置

(2)、藍牙傳輸資料,通常一秒鐘會傳輸很多個包,每個包的資料情況如下:

  此時,這個包有11個位元組,0x55 是首碼,通常通過他來判斷一個包的開始

                                     SUM是驗證碼,會有一套公式來計算,判斷目前包是不是一個有效的完整的包

              中間的即是資料,然後硬體方面會給我們一套計算公式,可以以此擷取我們要的資料。

  當然每個硬體的包的資料大小都是不同的,有的可能有21個位元組,每個硬體的資料的計算方式也不想同

Android項目實戰(二十六):藍牙連接配接硬體裝置開發規範流程

代碼實作:

一共就三部分,因為代碼篇幅可能較大,不适合一段段代碼講解,直接貼出整個代碼。所有的解釋都在注釋當中。

其中:

(1)、紅色部分是需要大家根據個人硬體情況進行修改的

(2)、紫色部分是根據個人資料情況添加删除修改的。

一:MainActivity

public class MainActivity extends Activity {

    private BluetoothService mBluetoothService; //自定義藍牙服務類
    private BluetoothAdapter mBluetoothAdapter;
    private String mConnectedDeviceName = null; //連接配接裝置的名稱

    //預設是1,因為程式啟動時首先會連接配接一個藍牙
    private int current_pos = 1;

    //hanlder消息辨別 message.what
    public static final int MESSAGE_STATE_CHANGE = 1; // 狀态改變
    public static final int MESSAGE_READ = 2;          // 讀取資料
    public static final int MESSAGE_WRITE = 3;         // 給硬體傳資料,暫不需要,看具體需求
    public static final int MESSAGE_DEVICE_NAME = 4;  // 裝置名字
    public static final int MESSAGE_TOAST = 5;         // Toast

    //傳感器 ,這裡預設同時需要和三個硬體連接配接,分别設定id 1,2,3進行區分,demo中實際隻用到 MAGIKARE_SENSOR_DOWN = 1
    //可以根據情況自行添加删除
    public static final int MAGIKARE_SENSOR_UP = 2;
    public static final int MAGIKARE_SENSOR_DOWN = 1;
    public static final int MAGIKARE_SENSOR_CENTER = 3;

    public static float[] m_receive_data_up;                    //傳感器的資料
    public static float[] m_receive_data_down;                  //傳感器的資料 ,demo中我們隻需要這一個,因為隻有一個硬體裝置,
    public static float[] m_receive_data_center;                //傳感器的資料


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //擷取藍牙擴充卡
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

        // 1、判斷裝置是否支援藍牙功能
        if (mBluetoothAdapter == null) {
            //裝置不支援藍牙功能
            Toast.makeText(this, "目前裝置不支援藍牙功能!", Toast.LENGTH_SHORT).show();
            return;
        }

        // 2、打開裝置的藍牙功能
        if (!mBluetoothAdapter.isEnabled()) {
            boolean enable = mBluetoothAdapter.enable(); //傳回值表示 是否成功打開了藍牙裝置
            if (enable) {
                Toast.makeText(this, "打開藍牙功能成功!", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "打開藍牙功能失敗,請到'系統設定'中手動開啟藍牙功能!", Toast.LENGTH_SHORT).show();
                return;
            }
        }


        // 3、建立自定義藍牙服務對象
        if (mBluetoothService == null) {
            mBluetoothService = new BluetoothService(MainActivity.this, mHandler);
        }
        if (mBluetoothService != null) {
            //根據MAC位址遠端擷取一個藍牙裝置,這裡固定了,實際開發中,需要動态設定參數(MAC位址)
            BluetoothDevice sensor_down = mBluetoothAdapter.getRemoteDevice("20:16:06:15:78:76");
            if (sensor_down != null) {
                //成功擷取到遠端藍牙裝置(傳感器),這裡預設隻連接配接MAGIKARE_SENSOR_DOWN = 1這個裝置
                mBluetoothService.connect(sensor_down, MAGIKARE_SENSOR_DOWN);
            }
        }


    }
    private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what){
                case MESSAGE_READ:
                    try {
                        String str=msg.getData().getString("index");
                        int index=Integer.valueOf(str);
                        switch (index)
                        {
                            //擷取到藍牙傳輸過來的資料
                            case MAGIKARE_SENSOR_UP:
                                m_receive_data_up=msg.getData().getFloatArray("Data");
                                break;
                            //實際隻用到這個case ,因為demo隻連接配接了一個硬體裝置
                            case MAGIKARE_SENSOR_DOWN:
                                m_receive_data_down=msg.getData().getFloatArray("Data");
                                break;
                            case MAGIKARE_SENSOR_CENTER:
                                m_receive_data_center=msg.getData().getFloatArray("Data");
                                break;

                        }
                    } catch (Exception e) {
                        // TODO: handle exception
                    }
                    break;
                case MESSAGE_STATE_CHANGE:
//                    連接配接狀态
                    switch (msg.arg1) {
                        case BluetoothService.STATE_CONNECTED:
                            break;
                        case BluetoothService.STATE_CONNECTING:
                            break;
                        case BluetoothService.STATE_LISTEN:
                            break;
                        case BluetoothService.STATE_NONE:
                            break;
                    }
                    break;
                case MESSAGE_DEVICE_NAME:
                    mConnectedDeviceName = msg.getData().getString("device_name");
                    Log.i("bluetooth","成功連接配接到:"+mConnectedDeviceName);
                    Toast.makeText(getApplicationContext(),"成功連接配接到裝置" + mConnectedDeviceName,Toast.LENGTH_SHORT).show();

                    break;
                case MESSAGE_TOAST:
                    int index=msg.getData().getInt("device_id");
                    Toast.makeText(getApplicationContext(),msg.getData().getString("toast"), Toast.LENGTH_SHORT).show();
                    //當失去裝置或者不能連接配接裝置時,重新連接配接
                    Log.d("Magikare","當失去裝置或者不能連接配接裝置時,重新連接配接");
            
            //重新連接配接硬體裝置
                    if(mBluetoothService!=null)
                    {
                        switch (index) {
                            case MAGIKARE_SENSOR_DOWN:
                    //根據你的硬體的MAC位址寫參數,每一個硬體裝置都有一個MAC位址,此方法是根據MAC位址得到藍牙裝置
                                BluetoothDevice sensor_down = mBluetoothAdapter.getRemoteDevice("20:16:06:15:78:76");
                                if (sensor_down != null)
                                    mBluetoothService.connect(sensor_down, MAGIKARE_SENSOR_DOWN);
                                break;
                            case MAGIKARE_SENSOR_UP:
                                BluetoothDevice sensor_up = mBluetoothAdapter.getRemoteDevice("");  //參數寫你這個裝置的MAC碼
                                if (sensor_up != null)
                                    mBluetoothService.connect(sensor_up, MAGIKARE_SENSOR_UP);
                                break;
                            case MAGIKARE_SENSOR_CENTER:
                                BluetoothDevice center = mBluetoothAdapter.getRemoteDevice("");    //參數寫你這個裝置的MAC碼
                                if (center != null)
                                    mBluetoothService.connect(center, MAGIKARE_SENSOR_CENTER);
                                break;
                        }
                    }

                    break;
            }
            return false;
        }
    });





    public synchronized void onResume() {
        super.onResume();

        if (mBluetoothService != null) {
            if (mBluetoothService.getState() == BluetoothService.STATE_NONE) {
                mBluetoothService.start();
            }
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mBluetoothService != null) mBluetoothService.stop();
    }
  
  
  // 硬體通過藍牙傳輸的byte類型已經轉換為float類型,并且通過handler傳輸到 m_receive_data_down[]數組中,一下操作是擷取這個資料,根據個人情況使用
    //擷取角度
    public float[] GetAngle(int index)
    {
        float[] angles=new float[3];
        if(m_receive_data_up==null
                ||m_receive_data_down==null
                )
        {
            return angles;
        }
        switch (index)
        {
            case  MAGIKARE_SENSOR_DOWN:
                angles[0]=m_receive_data_down[6];
                angles[1]=m_receive_data_down[7];
                angles[2]=m_receive_data_down[8];
                break;
            case MAGIKARE_SENSOR_UP:
                angles[0]=m_receive_data_up[6];
                angles[1]=m_receive_data_up[7];
                angles[2]=m_receive_data_up[8];
                Log.d("安卓 Up 角度",angles[0]+","+angles[1]+","+angles[2]);
                break;
        }
        return angles;
    }
    //擷取角速度
    public static float[] GetAngleSpeed(int index)
    {

        float [] anglespeed=new float[3];

        if(m_receive_data_down==null)
        {

            return anglespeed;
        }
        switch (index)
        {
            case MAGIKARE_SENSOR_DOWN:

                anglespeed[0]=m_receive_data_down[3];
                anglespeed[1]=m_receive_data_down[4];
                anglespeed[2]=m_receive_data_down[5];
                break;
            case MAGIKARE_SENSOR_UP:
                anglespeed[0]=m_receive_data_up[3];
                anglespeed[1]=m_receive_data_up[4];
                anglespeed[2]=m_receive_data_up[5];
                break;
        }
        return  anglespeed;
    }

    public float[] GetQuaternion(int index)
    {
        float[] quaternion=new float[4];

        if(m_receive_data_down==null)
        {
            return quaternion;
        }
        switch (index)
        {
            case  MAGIKARE_SENSOR_DOWN:
                quaternion[0]=m_receive_data_down[23];
                quaternion[1]=m_receive_data_down[24];
                quaternion[2]=m_receive_data_down[25];
                quaternion[3]=m_receive_data_down[26];
                Log.i("saveinfo","m_receive_data_down23"+m_receive_data_down[23]);
                Log.i("saveinfo","m_receive_data_down24"+m_receive_data_down[24]);
                Log.i("saveinfo","m_receive_data_down25"+m_receive_data_down[25]);
                Log.i("saveinfo","m_receive_data_down26"+m_receive_data_down[26]);
                break;
            case MAGIKARE_SENSOR_UP:
                quaternion[0]=m_receive_data_up[23];
                quaternion[1]=m_receive_data_up[24];
                quaternion[2]=m_receive_data_up[25];
                quaternion[3]=m_receive_data_up[26];
                break;
            case MAGIKARE_SENSOR_CENTER:
                quaternion[0]=m_receive_data_center[23];
                quaternion[1]=m_receive_data_center[24];
                quaternion[2]=m_receive_data_center[25];
                quaternion[3]=m_receive_data_center[26];
        }
        return  quaternion;
    }



}      

二、BluetoothService

public class BluetoothService {
  private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
    private Context context;
    //藍牙擴充卡
    private BluetoothAdapter mAdapter;
    private Handler mHandler;

    //目前傳感器裝置的個數,即要開啟的線程個數,用于設定線程數組的大小
    //這裡預設為1,因為我們目前隻需要和一個傳感器連接配接, 比如:你要連接配接兩個硬體裝置,那就設定值為2,這樣就會開啟兩個線程,分别去執行想要操作
    public static final int  SENSEOR_NUM=1;

    private AcceptThread mAcceptThread;// 請求連接配接的監聽程序
    private ConnectThread mConnectThread;// 連接配接一個裝置的程序
    public ConnectedThread[] mConnectedThread=new ConnectedThread[SENSEOR_NUM];// 已經連接配接之後的管理程序

    private int mState;// 目前狀态

    // 指明連接配接狀态的常量
    public static final int STATE_NONE = 0;         //沒有連接配接
    public static final int STATE_LISTEN = 1;       //等待連接配接
    public static final int STATE_CONNECTING = 2;  //正在連接配接
    public static final int STATE_CONNECTED = 3;   //已經連接配接

    public BluetoothService(Context context, Handler mHandler) {
        this.context = context;
        this.mHandler = mHandler;
        mAdapter = BluetoothAdapter.getDefaultAdapter();//擷取藍牙擴充卡
        mState = STATE_NONE ; //目前連接配接狀态:未連接配接
    }

    // 參數 index 是 硬體裝置的id ,随便設的,目的在于當 同時連接配接多個硬體裝置的時候,根據此id進行區分
    public synchronized void connect(BluetoothDevice device, int index) {

        //連接配接一個藍牙時,将該裝置 的藍牙連接配接線程關閉,如果有的話
        //demo  就隻有一個硬體裝置,預設該裝置id 取值index=1;
        if (mConnectedThread[index-1] != null) {
            mConnectedThread[index-1].cancel();
            mConnectedThread[index-1]=null;
        }
        mConnectThread=new ConnectThread(device,index);
        mConnectThread.start();
        setState(STATE_CONNECTING);
    }

    private class ConnectThread extends Thread{
        private final BluetoothSocket mmSocket;
        private final BluetoothDevice mmDevice;
        private int index;
        public ConnectThread(BluetoothDevice device,int index) {
            mmDevice = device;
            this.index=index;
            BluetoothSocket tmp = null;
            try {
                tmp = device.createRfcommSocketToServiceRecord(MY_UUID);// Get a BluetoothSocket for a connection with the given BluetoothDevice
            }
            catch (IOException e) {}
            mmSocket = tmp;
        }

        public void run() {

            setName("ConnectThread");
            //當連接配接成功,取消藍牙擴充卡搜尋藍牙裝置的操作,因為搜尋操作非常耗時
            mAdapter.cancelDiscovery();// Always cancel discovery because it will slow down a connection

            try {
                mmSocket.connect();// This is a blocking call and will only return on a successful connection or an exception
            }
            catch (IOException e) {
                connectionFailed(this.index);
                try {
                    mmSocket.close();
                } catch (IOException e2) {}

                BluetoothService.this.start();// 引用來說明要調用的是外部類的方法 run
                return;
            }

            synchronized (BluetoothService.this) {// Reset the ConnectThread because we're done
                mConnectThread = null;
            }
            connected(mmSocket, mmDevice,index);// Start the connected thread
        }

        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) {
            }
        }
    }

    class ConnectedThread extends Thread{
        private BluetoothSocket mmSocket;
        private InputStream mmInStream;
        private OutputStream mmOutStream;
        private int index;
        private Queue<Byte> queueBuffer = new LinkedList<Byte>();
        private byte[] packBuffer = new byte[11];


        //構造方法
        public ConnectedThread(BluetoothSocket socket,int index) {
            mmSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;
            this.index=index;
            // Get the BluetoothSocket input and output streams
            try {
                tmpIn = socket.getInputStream();
                tmpOut = socket.getOutputStream();
            } catch (IOException e) {}

            mmInStream = tmpIn;
            mmOutStream = tmpOut;
        }

        // 數組大小看你的資料需求,這裡存的是你處理藍牙傳輸來的位元組資料之後實際要用到的資料
        private float [] fData=new float[31];

        @Override
        public void run() {
            byte[] tempInputBuffer = new byte[1024];
            int acceptedLen = 0; //記錄每次讀取資料的資料長度
            byte sHead;
            long lLastTime = System.currentTimeMillis(); //擷取開始時間
            while(true){
                try {
                    acceptedLen = mmInStream.read(tempInputBuffer);//傳回接收的長度
                    //從緩沖區中讀取資料
                    for (int i = 0; i < acceptedLen; i++) {
                        queueBuffer.add(tempInputBuffer[i]);
                    }
                    // 這裡需要按個人硬體資料的情況自行修改了
                    // 如果你的硬體藍牙傳輸 一個包有11個位元組,那queueBuffer.size()>=11
                    // 如果你的硬體藍牙傳輸 一個包有21個位元組,那queueBuffer.size()>=21
                    while (queueBuffer.size()>=11){
                        //傳回隊首并删除,判斷隊首是不是0x55,如果不是,說明不是一個包的資料,跳過,
                        //注意這裡的0x55是你的包的首位元組
                        if (queueBuffer.poll()!=0x55)
                            continue;
                        // 進入到這裡,說明得到一個包的資料了,然後就要根據個人硬體的資料情況,将byte類型的資料轉換為float類型的資料

                        sHead = queueBuffer.poll(); //傳回隊首并删除
        
              // 現在得到的就是你資料部分了,如果有9位位元組代表資料,j<9 ,如果有19位位元組代表資料,j<19
                 
              //将位元組數組存到packBuffer[]資料中,用于byte-->float資料的轉換
                        for (int j = 0; j < 9; j++) {
                            packBuffer[j] = queueBuffer.poll();
                        }
                        switch (sHead) {//
                            case 0x52://角速度
                                fData[3] = ((((short) packBuffer[1]) << 8) | ((short) packBuffer[0] & 0xff)) / 32768.0f * 2000;
                                fData[4] = ((((short) packBuffer[3]) << 8) | ((short) packBuffer[2] & 0xff)) / 32768.0f * 2000;
                                fData[5] = ((((short) packBuffer[5]) << 8) | ((short) packBuffer[4] & 0xff)) / 32768.0f * 2000;
                                fData[17] = ((((short) packBuffer[7]) << 8) | ((short) packBuffer[6] & 0xff)) / 100.0f;
                                break;
                            case 0x53://角度
                                fData[6] = ((((short) packBuffer[1]) << 8) | ((short) packBuffer[0] & 0xff)) / 32768.0f * 180;
                                fData[7] = ((((short) packBuffer[3]) << 8) | ((short) packBuffer[2] & 0xff)) / 32768.0f * 180;
                                fData[8] = ((((short) packBuffer[5]) << 8) | ((short) packBuffer[4] & 0xff)) / 32768.0f * 180;
                                fData[17] = ((((short) packBuffer[7]) << 8) | ((short) packBuffer[6] & 0xff)) / 100.0f;
                                break;
                            case 0x59://四元數
                                fData[23] = ((((short) packBuffer[1]) << 8) | ((short) packBuffer[0] & 0xff)) / 32768.0f;
                                fData[24] = ((((short) packBuffer[3]) << 8) | ((short) packBuffer[2] & 0xff))/32768.0f;
                                fData[25] = ((((short) packBuffer[5]) << 8) | ((short) packBuffer[4] & 0xff))/32768.0f;
                                fData[26] = ((((short) packBuffer[7]) << 8) | ((short) packBuffer[6] & 0xff))/32768.0f;
                                break;
                        }
                    }
                    long lTimeNow = System.currentTimeMillis(); // 擷取收據轉換之後的時間
                    // 如果資料處理後的時間  與 接收到資料的時間 的時間差>80 則發送消息傳輸資料,
                    // 這個時間需要看你硬體一秒鐘發送的包的個數
                    if (lTimeNow - lLastTime > 80) {
                        lLastTime = lTimeNow;
                        Message msg = mHandler.obtainMessage(MainActivity.MESSAGE_READ);
                        Bundle bundle = new Bundle();
                        bundle.putString("index",String.valueOf(this.index));
                        bundle.putFloatArray("Data", fData);
                        msg.setData(bundle);
                        mHandler.sendMessage(msg);
                    }
                } catch (IOException e) {
                    connectionLost(this.index);
                    e.printStackTrace();
                }
            }
        }
        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) {}
        }
    }
    //連接配接失敗
    private void connectionFailed(int index) {
        setState(STATE_LISTEN);
        // Send a failure message back to the Activity
        Message msg = mHandler.obtainMessage(MainActivity.MESSAGE_TOAST);
        Bundle bundle = new Bundle();
        bundle.putString("toast", "未能連接配接裝置"+index);
        bundle.putInt("device_id",index);
        msg.setData(bundle);
        mHandler.sendMessage(msg);
    }
    // 連接配接丢失
    private void connectionLost(int index) {
        setState(STATE_LISTEN);
        Message msg = mHandler.obtainMessage(MainActivity.MESSAGE_TOAST);
        Bundle bundle = new Bundle();
        bundle.putString("toast", "裝置丢失"+index);
        bundle.putInt("device_id",index);
        msg.setData(bundle);
        mHandler.sendMessage(msg);
    }


    //用于 藍牙連接配接的Activity onResume()方法
    public synchronized void start() {
        // Cancel any thread attempting to make a connection
        if (mConnectThread != null) {
            mConnectThread.cancel();
            mConnectThread = null;
        }

        if (mAcceptThread == null) {
            mAcceptThread = new AcceptThread();
            mAcceptThread.start();
        }
        setState(STATE_LISTEN);
    }

    public synchronized void connected(BluetoothSocket socket,BluetoothDevice device,int index) {
        Log.d("MAGIKARE","連接配接到線程"+index);
        // Cancel the thread that completed the connection
        if (mConnectThread != null) {
            mConnectThread.cancel();
            mConnectThread = null;
        }
        // Cancel the accept thread because we only want to connect to one device
        if (mAcceptThread != null) {
            mAcceptThread.cancel();
            mAcceptThread = null;
        }

        // Start the thread to manage the connection and perform transmissions
        mConnectedThread[index-1] = new ConnectedThread(socket,index);

        mConnectedThread[index-1].start();

        // Send the name of the connected device back to the UI Activity
        Message msg = mHandler.obtainMessage(MainActivity.MESSAGE_DEVICE_NAME);
        Bundle bundle = new Bundle();
        bundle.putString("device_name", device.getName()+" "+index);

        msg.setData(bundle);
        mHandler.sendMessage(msg);

        setState(STATE_CONNECTED);
    }

    private synchronized void setState(int state) {
        mState = state;
        // Give the new state to the Handler so the UI Activity can update
        mHandler.obtainMessage(MainActivity.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();
    }

    private class AcceptThread extends Thread {
        // The local server socket
        private final BluetoothServerSocket mmServerSocket;
        //private int index;
        public AcceptThread() {
            BluetoothServerSocket tmp = null;
            // this.index=index;
            // Create a new listening server socket
            try {
                tmp = mAdapter.listenUsingRfcommWithServiceRecord("BluetoothData", MY_UUID);
            }
            catch (IOException e) {}
            mmServerSocket = tmp;
        }

        public void run() {
            new Thread(new Runnable() {
                @Override
                public void run() {

                }
            }).start();

        }

        public void cancel() {

            try {
                if(mmServerSocket!=null) {
                    mmServerSocket.close();
                }
            }
            catch (IOException e) {}
        }
    }
    public synchronized int getState() {
        return mState;
    }


    public synchronized void stop() {
        if (mConnectedThread != null) {
            for(int i=0;i<mConnectedThread.length;i++)
            {
                    mConnectedThread[i].cancel();
            }
            mConnectedThread = null;
        }
        if (mAcceptThread != null) {
            mAcceptThread.cancel();
            mAcceptThread = null;
        }
        setState(STATE_NONE);
    }
}      

三、自定義即時變化的折線圖:

public class MyView extends View {
    /*http://www.cnblogs.com/aibuli/p/950c34f2bc0d02cbd290dd6a8339d42a.html*/
    //坐标軸原點的位置
    private int xPoint=60;
    private int yPoint=260;
    //刻度長度
    private int xScale=8;  //8個機關構成一個刻度
    private int yScale=40;
    //x與y坐标軸的長度
    private int xLength=580;
    private int yLength=480;

    private int MaxDataSize=xLength/xScale;   //橫坐标  最多可繪制的點

    private List<Float> data=new ArrayList<Float>();   //存放 縱坐标 所描繪的點

    private String[] yLabel=new String[yLength/yScale];  //Y軸的刻度上顯示字的集合


    private Handler mh=new Handler(){
        public void handleMessage(android.os.Message msg) {
            if(msg.what==0){                //判斷接受消息類型
                MyView.this.invalidate();  //重新整理View
            }
        };
    };
    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        for (int i = 0; i <yLabel.length; i++) {
            yLabel[i]=(i+1)+"M/s";
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){     //線上程中不斷往集合中增加資料
                    try {
                        Thread.sleep(300);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if(data.size()>MaxDataSize){  //判斷集合的長度是否大于最大繪制長度
                        data.remove(0);  //删除頭資料
                    }
                    // 這裡得到藍牙裝置得到的資料
                    float[] floats = MainActivity.GetAngleSpeed(1);
                    data.add(floats[0]);
                    mh.sendEmptyMessage(0);   //發送空消息通知重新整理
                }
            }
        }).start();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint=new Paint();
        paint.setStyle(Paint.Style.STROKE);
        paint.setAntiAlias(true);
        paint.setColor(Color.RED);
        //繪制Y軸
        canvas.drawLine(xPoint, yPoint-yLength, xPoint, yPoint, paint);
        //繪制Y軸左右兩邊的箭頭
        canvas.drawLine(xPoint, yPoint-yLength, xPoint-3,yPoint-yLength+6, paint);
        canvas.drawLine(xPoint, yPoint-yLength, xPoint+3,yPoint-yLength+6, paint);
        //Y軸上的刻度與文字
        for (int i = 0; i * yScale< yLength; i++) {
            canvas.drawLine(xPoint, yPoint-i*yScale, xPoint+5, yPoint-i*yScale, paint);  //刻度
            canvas.drawText(yLabel[i], xPoint-50, yPoint-i*yScale, paint);//文字
        }
        //X軸
        canvas.drawLine(xPoint, yPoint, xPoint+xLength, yPoint, paint);
        //如果集合中有資料
        if(data.size()>1){
            for (int i = 1; i < data.size(); i++) {  //依次取出資料進行繪制
                canvas.drawLine(xPoint+(i-1)*xScale, yPoint-data.get(i-1)*yScale, xPoint+i*xScale, yPoint-data.get(i)*yScale, paint);
            }
        }

    }
}      

相關知識:

淺談Bluetooth藍牙開發

有問題歡迎留言交流!

繼續閱讀