Android的二维码功能实现以及长按识别二维码

来源:互联网 发布:日本黑科技 知乎 编辑:程序博客网 时间:2024/05/21 17:09

一、初步集成zxing

            二维码的识别可是在生活中随处可见的,现在基本上所有APP都有二维码的相关操作,如果识别二维码从头开始开发做起来还是相当复杂和麻烦的,从零开始开发肯定不现实,最好的做法就是借助现有的开源项目,Github上名气最大的就是Zxing了,提供了多个平台的二维码扫码解决方案,开源项目地址是:https://github.com/zxing/zxing,我们今天集成Zxing到我们自己的项目中去,并实现一些二维码生成、扫描、长按识别二维码等相关操作:
       
     1.下载Zxing项目所依赖的jar包:
         下载地址是:http://repo1.maven.org/maven2/com/google/zxing/core/3.2.1/,选择core-3.2.1.jar下载即可,目前是最新的,原来存在将jar包导入项目无法正常运行,只有导入源码,现在是不存在这个问题的,在开源项目的下方也有下载地址,可以随时下载最新的jar包,还要下载一个jar包是android-core-3.2.1.jar,或者将这个jar包里面的只有一个类复制到工程里面也ok


     2.下载Zxing项目:
         下载地址在开源项目上下载即可,100多M,这个很简单,别跟我说你不会。

     下面我们就把zxing加入到我们的项目上去,首先把下载的Zxing项目中的源码拷贝到项目工程中去,下面我以Android Studio演示集成并实现相关功能:
     下载解压过后的样子应该是这个样子:
      
      
         点击Android下面在src看到源码复制到工程里面,在把资源文件拷贝进去,然后就是改一下AndroidMainfest其中的声明Activityde shihou ,因为都是简写,现在包名变了,改一下就好了,然后针对工程的R文件报错一个一个小心翼翼的改掉就OK了,针对有一个类报错BookmarkPickerActivity,其中大概在50行左右的时候,Browser.BOOKMARKS_URI这个报错,我把编译版本改成22就好了,基本上这样就算OK了,就像这样子:


      然后基本运行你就可以开始扫码了。

二、获得Zxing的核心功能

              这个项目的功能多,项目很大,我们需要把最核心的功能抽取出来就好了,网上已经有很多人做出来了,大多数都是基于第一个抽取Zxing的人,但是那个是基于Zxing1.5、2.3做的,我们需要做的就是找一个旧的将这些类用最新的Zxing的代码替换掉就可以了。下面我给出一个基于ZXing3.1封装,包含了最新的jar包和代码,大家可以去下载,是医生提供的,Github中已经包含了前面所提到的所有修改(横竖屏、扭曲变形),用3.1ZXing代码进行了update,同时提供了编码、解码方法,并且将扫码界面抽取成XML(感谢开源作者),方便拓展。下载地址猛戳:
        https://github.com/xuyisheng/ZXingLib

三、实现扫描和长按读取等功能

             先看效果图,录制的时候没有把鼠标录制进入,感觉不是很好,读取出来用的toast显示的,大家将就看吧
         
             实现二维码的生成,如下调用即可:
         
//生成二维码图片,第一个参数是二维码的内容,第二个参数是正方形图片的边长,单位是像素  Bitmap bitmap = null;try {bitmap = BitmapUtil.createQRCode(msg, 400);} catch (WriterException e) {// TODO Auto-generated catch blocke.printStackTrace();}   imageView.setImageBitmap(bitmap);

       然后将生成的二维码放置到相应的位置即可。


       现在微信基本上都有长按二维码识别,这种实现我的思路就是,既然二维码扫描能够识别,那么肯定也是能够解析图片的,那么我们可以长按实现对当前屏幕截屏然后调用相关的类去解析即可:

       我们先看下截屏的代码,都有注释自己看吧:

        

//这种方法状态栏是空白,显示不了状态栏的信息    private void saveCurrentImage()    {        //获取当前屏幕的大小        int width = getWindow().getDecorView().getRootView().getWidth();        int height = getWindow().getDecorView().getRootView().getHeight();        //生成相同大小的图片        Bitmap temBitmap = Bitmap.createBitmap( width, height, Bitmap.Config.ARGB_8888 );        //找到当前页面的根布局        View view =  getWindow().getDecorView().getRootView();        //设置缓存        view.setDrawingCacheEnabled(true);        view.buildDrawingCache();        //从缓存中获取当前屏幕的图片,创建一个DrawingCache的拷贝,因为DrawingCache得到的位图在禁用后会被回收        temBitmap = view.getDrawingCache();        SimpleDateFormat df = new SimpleDateFormat("yyyymmddhhmmss");        time = df.format(new Date());        if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){            file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/screen",time + ".png");            if(!file.exists()){                file.getParentFile().mkdirs();                try {                    file.createNewFile();                } catch (IOException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }            }            FileOutputStream fos = null;            try {                fos = new FileOutputStream(file);                temBitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);                fos.flush();                fos.close();            } catch (FileNotFoundException e) {                e.printStackTrace();            } catch (IOException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }            //禁用DrawingCahce否则会影响性能 ,而且不禁止会导致每次截图到保存的是第一次截图缓存的位图            view.setDrawingCacheEnabled(false);        }    }


         上面将屏幕截图并以当前的时间为名字保存到手机上了,我们解析直接可以将路径传递进入解析就好了,我把相关的类传递上来,都有注释,大家看看吧:

//解析二维码图片,返回结果封装在Result对象中  private com.google.zxing.Result  parseQRcodeBitmap(String bitmapPath){  //解析转换类型UTF-8  Hashtable<DecodeHintType, String> hints = new Hashtable<DecodeHintType, String>();  hints.put(DecodeHintType.CHARACTER_SET, "utf-8");  //获取到待解析的图片  BitmapFactory.Options options = new BitmapFactory.Options();   options.inJustDecodeBounds = true;    Bitmap bitmap = BitmapFactory.decodeFile(bitmapPath,options);    options.inSampleSize = options.outHeight / 400;  if(options.inSampleSize <= 0){  options.inSampleSize = 1; //防止其值小于或等于0  }  options.inJustDecodeBounds = false;  bitmap = BitmapFactory.decodeFile(bitmapPath, options);   //新建一个RGBLuminanceSource对象,将bitmap图片传给此对象  RGBLuminanceSource rgbLuminanceSource = new RGBLuminanceSource(bitmap);  //将图片转换成二进制图片  BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(rgbLuminanceSource));  //初始化解析对象  QRCodeReader reader = new QRCodeReader();  //开始解析  Result result = null;  try {  result = reader.decode(binaryBitmap, hints);  } catch (Exception e) {  // TODO: handle exception  }  return result;  }  

                  按照zxing的解码规则,我们是需要一个BitmapLuminanceSource类的,也是唯一需要去实现的

            BitmapLuminanceSource继承自LuminanceSource这个抽象类,需要实现它的构造方法,其构造方法中需要传入宽高,这两个值指的就是图片的宽和高。getMatrix()方法会返回一个byte数组,这个数组就是图片的像素数组。getRow(int y, byte[] row)如字面的意义,就是得到图片像素数组的一行。其中的y就是需要的哪一个行的像素数组。

            以下是完整的BitmapLuminanceSource类

            

public class RGBLuminanceSource extends LuminanceSource {private byte bitmapPixels[];  protected RGBLuminanceSource(Bitmap bitmap) {  super(bitmap.getWidth(), bitmap.getHeight());  // 首先,要取得该图片的像素数组内容  int[] data = new int[bitmap.getWidth() * bitmap.getHeight()];  this.bitmapPixels = new byte[bitmap.getWidth() * bitmap.getHeight()];  bitmap.getPixels(data, 0, getWidth(), 0, 0, getWidth(), getHeight());  // 将int数组转换为byte数组for (int i = 0; i < data.length; i++) {  this.bitmapPixels[i] = (byte) data[i];  }  }  @Override  public byte[] getMatrix() {  // 返回我们生成好的像素数据  return bitmapPixels;  }  @Override  public byte[] getRow(int y, byte[] row) {  // 这里要得到指定行的像素数据  System.arraycopy(bitmapPixels, y * getWidth(), row, 0, getWidth());  return row;  }  }
      

         上面获取图片像素组内容的byte[]数组是指图片的像素数组,而不是所谓的Bitmap转换成byte数组

         Bitmap对象的getPixels方法可以取得的像素数组,但它得到是int型数组。根据其api文档解释,取得的是color,也就是像素颜色值。每个像素值包含透明度,红色,绿色,蓝色。所以白色就是0xffffffff,黑色就是0xff000000。直接由int型转成byte型,实现上相当于我们这里只取其蓝色值部分。


         getPixels得到的像素数组是一维的,也就是按照图片宽度逐行取像素颜色值录入。如果想得到单行的像素数组内容,通过y*width就可以找该行的第一个像素值,拷贝后面width个就可以得到该行的像素内容。

         最后一个就是getMatrix()方法,它用来返回我们的图像转换成的像素数组。

         最后我们将长按存储的图片路径传递进去即可,但是注意解析的时候开个线程,不然要ANR,基本上实现了我们需要的大部分生成、读取、识别大部分功能。

        稍后上传源码给大家,觉得有用就点个赞吧,上一次由于上传的资源的时候把imageview的大小设置成定值,导致识别不了,现在重新上传是正常的了。。。。。


         源码下载猛戳  源码下载


4 2