Android开发学习秘籍笔记(十九)

Posted liguangsunls

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android开发学习秘籍笔记(十九)相关的知识,希望对你有一定的参考价值。

       吼。花了2天最后做出了一个类似于蓝牙串口助手功能的小程序,事实上也是实习公司的要求---有一个蓝牙无线扫描枪,要求终端能够通过蓝牙连接到该设备,而且蓝牙无线扫描枪扫描二维码或者条形码的时候能够将二维码或者条形码的数据输出到TextView中。


效果:

       听效果是不是感觉非常好做。说明下蓝牙扫描器的功能,有2中经常使用的模式--普通模式,SPP模式。 普通模式的话就是相当于蓝牙连接后,扫描器就相当于一个外接的键盘,能够扫码然后将数据输出到EditText(必须获得焦点)。SPP模式则是用于模拟串口通信的,在我看来就相当于开发人员模式。。

      方案一:在界面代码下手脚,是一种投机取巧的方法。

不是要显示在TextVIew上吗。不是要扫码后仅仅能输出到获得焦点的EditText上吗。那我就在界面代码里设置这两个控件。可是EditText我设置其高度为1dp。除了开发者自己知道这里有个EditText之外。使用者是不知道这里还有个EditText的。

然后你就能够直接将EditText中的内容获取下来,在setText到TextView 中去就能够了。


主要代码就是Edittext的setOnKeyListener里设置就好了。

et.setOnKeyListener(new EditText.OnKeyListener()

       {

       @Override

       publicboolean onKey(View v, int keyCode, KeyEvent event)

       {

           tv.setText(et.getText());

           returnfalse;

       }     

       });

       操作简单。思路清晰,简单粗暴!可是问题也非常多,万一你一个界面好多个EditText,比方登陆界面。除了你的那个自己知道别人看不到的EditText,还得有两个EditText,一旦某一个获得光标,你的扫描器就失去了作用,还会吓使用者一跳。所以这样的方案不适合用在须要推送的APP上。


      方案二:蓝牙传输通信,详细使用到的就是安卓传输的流方式,这样的方式就非常符合要求,就在你要数据传输的时候把你的数据截下来,然后我在做对应的操作。我看了非常多的博客,说真的。这些博客都千篇一律。这里放两篇比較经典的吧。

      https://segmentfault.com/a/1190000004899799


http://www.cnblogs.com/wenjiang/p/3200138.html


     详细的解释我会在代码里说。光说理论本人菜的抠脚。首先说说布局,超级简单。一个TextView,一个EditText(測试用的) , 一个Button(能够依据自己的须要去掉)。码就不贴了,到时候直接发项目吧!

     说说Button的点击事件(能够依据自己的需求进行改动)。点击进入一个Activity显示全部的扫描到的蓝牙设备--DeviceListAcitivty

一:获取扫描到的蓝牙设备

     DeviceListActivity里面须要做的事情就是将能扫描到的蓝牙设备显示在ListView中。假设你确定你仅仅要连接一种设备的话而且知道设备的Address的话你大可省略这个操作。这里当学习用。

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;


public class DeviceListActivity extends Activity {
    // 调试用
    private static final String TAG = "DeviceListActivity";
    private static final boolean D = true;

    // 返回时数据标签
    public static String EXTRA_DEVICE_ADDRESS = "设备地址";

    // 成员域
    private BluetoothAdapter mBtAdapter;
    private ArrayAdapter<String> mPairedDevicesArrayAdapter;
    private ArrayAdapter<String> mNewDevicesArrayAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 创建并显示窗体
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);  //设置窗体显示模式为窗体方式
        setContentView(R.layout.device_list);

        // 设定默认返回值为取消
        setResult(Activity.RESULT_CANCELED);

        // 设定扫描按键响应
        Button scanButton = (Button) findViewById(R.id.button_scan);
        scanButton.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                doDiscovery();
                v.setVisibility(View.GONE);
            }
        });

        // 初使化设备存储数组
        mPairedDevicesArrayAdapter = new ArrayAdapter<>(this, R.layout.device_name);
        mNewDevicesArrayAdapter = new ArrayAdapter<>(this, R.layout.device_name);

        // 设置已配队设备列表

        ListView pairedListView = (ListView) findViewById(R.id.paired_devices);
        pairedListView.setAdapter(mPairedDevicesArrayAdapter);
        pairedListView.setOnItemClickListener(mDeviceClickListener);

        // 设置新查找设备列表
        ListView newDevicesListView = (ListView) findViewById(R.id.new_devices);
        newDevicesListView.setAdapter(mNewDevicesArrayAdapter);
        newDevicesListView.setOnItemClickListener(mDeviceClickListener);

        // 注冊接收查找到设备action接收器
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        this.registerReceiver(mReceiver, filter);

        // 注冊查找结束action接收器
        filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        this.registerReceiver(mReceiver, filter);

        // 得到本地蓝牙句柄
        mBtAdapter = BluetoothAdapter.getDefaultAdapter();

        // 得到已配对蓝牙设备列表
        //Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();

        // 加入已配对设备到列表并显示
        // if (pairedDevices.size() > 0) {
        // findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
        //    for (BluetoothDevice device : pairedDevices) {
        //         mPairedDevicesArrayAdapter.add(device.getName() + "\\n" + device.getAddress());
        //     }
        // } else {
        //     String noDevices = "No devices have been paired";
        //     mPairedDevicesArrayAdapter.add(noDevices);
        // }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        // 关闭服务查找
        if (mBtAdapter != null) {
            mBtAdapter.cancelDiscovery();
        }

        // 注销action接收器
        this.unregisterReceiver(mReceiver);
    }

    public void OnCancel(View v){
        finish();
    }
    /**
     * 開始服务和设备查找
     */
    private void doDiscovery() {
        if (D) Log.d(TAG, "doDiscovery()");

        // 在窗体显示查找中信息
        setProgressBarIndeterminateVisibility(true);
        setTitle("查找设备中...");

        // 显示其他设备(未配对设备)列表
        findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);

        // 关闭再进行的服务查找
        if (mBtAdapter.isDiscovering()) {
            mBtAdapter.cancelDiscovery();
        }
        //并又一次開始
        mBtAdapter.startDiscovery();
    }

    // 选择设备响应函数
    private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {
        public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
            // 准备连接设备,关闭服务查找
            mBtAdapter.cancelDiscovery();

            // 得到mac地址
            String info = ((TextView) v).getText().toString();
            String address = info.substring(info.length() - 17);

            // 设置返回数据
            Intent intent = new Intent();
            intent.putExtra(EXTRA_DEVICE_ADDRESS, address);

            // 设置返回值并结束程序
            setResult(Activity.RESULT_OK, intent);
            finish();
        }
    };

    // 查找到设备和搜索完毕action监听器
    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            // 查找到设备action
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                // 得到蓝牙设备
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                // 假设是已配对的则略过,已得到显示,其余的在加入到列表中进行显示
                if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
                    mNewDevicesArrayAdapter.add(device.getName() + "\\n" + device.getAddress());
                }else{  //加入到已配对设备列表
                    mPairedDevicesArrayAdapter.add(device.getName() + "\\n" + device.getAddress());
                }
                // 搜索完毕action
            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
                setProgressBarIndeterminateVisibility(false);
                setTitle("选择要连接的设备");
                if (mNewDevicesArrayAdapter.getCount() == 0) {
                    String noDevices = "没有找到新设备";
                    mNewDevicesArrayAdapter.add(noDevices);
                }
                //   if(mPairedDevicesArrayAdapter.getCount() > 0)
                //  	findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
            }
        }
    };


}


    代码贴在上面。详细的代码解释也写在代码上了,事实上我后面是不打算做这部操作的,由于针对仅仅须要连接一个蓝牙设备的程序来说,当我知道了蓝牙设备的Mac地址我就能够直接找到设备,根本不须要查询。但不得不说回来。 当厂家没有给你蓝牙设备的Mac地址的时候。你就不得不做这步操作了!所以推荐的话还是把这步操作给做了的好,详细怎么操作还是得看项目的要求。至于怎样查找蓝牙设备以及蓝牙设备的状态,上面的代码写的非常详细,结合我之前推荐的两篇文章就非常好懂了。


二:传输数据I/O流

    事实上在做这步操作的时候,我有点操心实现不了,由于对于Socket来说。你至少得写Socket和ServerSocket这两个类,详细的能够看看我之前对Socket的博客http://blog.csdn.net/cuihaoren01/article/details/45458265。但这里蓝牙扫描枪那边你是不可能编程的的。所以不存在ServerSocket的,好像网上大部分的蓝牙传输都是和Ardunio进行传输的,我也不晓得它可不能够进行编程所以就非常操心这样写能不能实现。后面我在GitHub上找到一个蓝牙串口助手的Demo来试试,发现它能够进行数据传送

但因为它写的代码过于复杂也不是一个框架能够直接利用,所以就没有深究。

https://github.com/hzjerry/BluetoothSppPro


    没办法了。实践是检验真理的唯一标准那就仅仅能開始自己測试了,接下来第二篇文章就发挥了作用,可能我不须要实现ServerSocket服务端的编程,在使用蓝牙串口助手測试的时候,你配对上加连接上就能够直接用了。说明我仅仅须要和它(蓝牙枪)能连接上就好了。

连接的做法过程例如以下:

  1. 首先你得获取到你须要连接的设备。

    (这里就须要你得设备的Mac地址)

  2. 你须要建立与服务端通信的Socket。看我之前的博客client都是通过IP和port来获得的通信的socket,服务端是通过accept()的方式获取的,而这里这样的方式被毙了(感觉也不能说毙了。预计是蓝牙枪那边有一个ServerSocket,去accept(),而client有其它的方法拿到通信的Socket。

  3. socket.connect()
 btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(address);

                try{
                    mBluetoothSocket = mBluetoothDevice.
                            createRfcommSocketToServiceRecord
                                    (UUID.fromString(MY_UUID));
                }catch (IOException e){
                    Toast.makeText(MainActivity.this, "配对失败,原因是:" + e.getMessage(), Toast.LENGTH_SHORT)
                            .show();
                    Log.e("过程", "失败的原因是:" + e.getMessage());
                }
                //与设备进行连接
                try{
                    mBluetoothSocket.connect();
                    Toast.makeText(MainActivity.this, "连接"+ mBluetoothDevice.getName()
                            + "成功", Toast.LENGTH_SHORT).show();
                    Log.e("TAGGGAGGAGGHG","连接过程");
                } catch (IOException e) {
                    try{
                        Toast.makeText(MainActivity.this, "连接失败,原因是:" + e.getMessage(), Toast.LENGTH_SHORT).show();
                        mBluetoothSocket.close();
                        mBluetoothSocket = null;
                        Log.e("连接失败", "连接失败的原因是:" + e.getMessage());
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }
                    return;
                }

                //与设备进行传输数据
                try{
                    is = mBluetoothSocket.getInputStream();
                }catch (IOException e){
                    Toast.makeText(MainActivity.this, "接收数据失败", Toast.LENGTH_SHORT).show();
                    return;
                }

                if(ready_receive == false){
                    ReadThread.start();
                    ready_receive = true;
                }else {
                    isReceiving = true;
                }
            }
        });

      这一部分就是连接蓝牙设备的操作,本来这些操作我是放到一个线程中去运行的。可是非常不幸的是会报错,然后我就放到UI线程中去了,结果还过了,按道理耗时操作放到UI线程中不是会爆炸的嘛...临时放一放这个问题。然后另一个线程ReadThread


Thread ReadThread = new Thread(){
        public void run(){
            int num = 0;
            byte[] buffer = new byte[1024];
            byte[] buffer_new = new byte[1024];

            int n = 0;
            isReceiving= true;
            while(true){
                try {
                    while (is.available() == 0){
                        while (isReceiving == false){}
                    }
                    while(true){
                        num = is.read(buffer);
                        n = 0;

                        String s0 = new String(buffer, 0, num);
//                        fmsg += s0;
                        for (int i = 0; i < num; i++ ){
                            if((buffer[i] == 0x0d) && (buffer[i + 1] == 0x0a)){
                                buffer_new[n] = 0x0a;
                                i++;
                            }else{
                                buffer_new[n] = buffer[i];
                            }
                            n++;
                        }
                        String s = new String(buffer_new, 0, n);
                        receive_msg += s;
                        if (is.available() == 0) break;
                    }
                    handler.sendMessage(handler.obtainMessage());
                }catch (IOException e){

                }
            }
        }
    };


这样就完毕了蓝牙串口助手的部分功能,详细的实例见源代码下载。


源代码下载:

已知要连接设备的address,仅仅须要改动MainActivity中address字符串的值就能够了。

http://download.csdn.net/detail/cuihaoren01/9496677

扫描蓝牙设备,选择须要连接的设备,进行传输。

http://download.csdn.net/detail/cuihaoren01/9496711


以上是关于Android开发学习秘籍笔记(十九)的主要内容,如果未能解决你的问题,请参考以下文章

Android开发笔记(一百七十九)避免方法数过多的问题

Android开发笔记(一百七十九)避免方法数过多的问题

Qt武林秘籍学习笔记摘要

Qt武林秘籍学习笔记摘要

Android开发笔记(一百八十九)利用LAME录制MP3音频

Android开发笔记(一百八十九)利用LAME录制MP3音频