zoukankan      html  css  js  c++  java
  • ZXing开发详解

    博客转载自:https://blog.csdn.net/skillcollege/article/details/38852183

    什么是Z*?

    在Android平台做过二维码相关模块的肯定都熟知ZXing开源项目,Z*是一个开源Java类库用于解析多种格式的1D/2D条形码。目标是能够对QR编码、Data Matrix、UPC的1D条形码进行解码。 其提供了多种平台下的客户端包括:J2ME、J2SE和Android。其GitHub地址是:传送门

    Z*项目里面代码很多,实现的功能也很多,我们的应用只需要剥离其中的扫描模块即可,再多一点也就是生成二维码的功能;接下来我们就一起来精简ZXing项目,最终形成一个小的Demo案例,当然江湖上已经有过N多种版本的ZXing精简项目,什么横屏改竖屏,绘制扫描界面,开启闪光灯等等,并且许多都是基于ZXing2.3.0来做精简的,后续有许多更新的版本,包括自动对焦,Camera管理,bug修复等等新功能;笔者使用的是ZXing3.1.0版本,这里需要说明的就是我的这版Demo绝对是江湖上面还没有出现的,也算是一点点小小的创新把,那就是去除ZXing项目中恼人的ViewFinderView的绘制,使用XML布局扫描界面,添加扫描动画,精确计算扫描区域

    克隆Z*项目到本地

    1
    git clone https://github.com/zxing/zxing.git

    整理ZXing代码

    打开ZXing项目的文件夹,可以看到如下文件目录:

    其中我们主要关注2个文件夹里的内容: 

    1. core : Z*项目的核心代码,可以新建一个Java工程,然后export成jar来调用。如下图所示:

    免打包即可获得的zxing-3.1.0.jar  猛戳下载 

    2. android : Android示例工程代码,成功运行之后就是一个专业的扫码应用了。如下图所示:

    免引入免整理的zxing原始工程 ZXingRawProject  猛戳下载

    但是这样就让你满足了,那怎么可以说是极致二维码扫描呢,有木有感觉ZXing的扫描框的绘制很不爽啊?自定义的View绘制的很丑,多屏幕适配的时候还经常不兼容,原始项目还是横屏模式的,目前大家都习惯竖屏扫描呢。怎么办?别怕,我来告诉你,我要将ViewFinderView砍掉,使用xml界面布局,添加扫描动画,最终一样准确无误的扫描到二维码数据,只需要对准,是的,毫厘不差的对准就可以了。

    精简Z*代码,打造极致扫描

    1. 去掉Z*中一些和扫描无关的代码,最终留下的代码结构如下图所示,最关键的是你看不到ViewFinderView 了

    2. 布局扫描界面,xml代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    <?xml version="1.0" encoding="utf-8"?> 
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
        android:layout_width="match_parent" 
        android:layout_height="match_parent" 
        android:background="@android:color/transparent" 
        android:orientation="vertical" 
       
        <SurfaceView 
            android:id="@+id/capture_preview" 
            android:layout_width="match_parent" 
            android:layout_height="match_parent" /> 
       
        <RelativeLayout 
            android:id="@+id/capture_container" 
            android:layout_width="match_parent" 
            android:layout_height="match_parent" 
       
            <ImageView 
                android:id="@+id/capture_mask_top" 
                android:layout_width="match_parent" 
                android:layout_height="120dp" 
                android:layout_alignParentTop="true" 
                android:background="@drawable/shadow" /> 
       
            <RelativeLayout 
                android:id="@+id/capture_crop_view" 
                android:layout_width="200dp" 
                android:layout_height="200dp" 
                android:layout_below="@id/capture_mask_top" 
                android:layout_centerHorizontal="true" 
                android:background="@drawable/qr_code_bg" 
       
                <ImageView 
                    android:id="@+id/capture_scan_line" 
                    android:layout_width="match_parent" 
                    android:layout_height="wrap_content" 
                    android:layout_alignParentTop="true" 
                    android:layout_marginBottom="5dp" 
                    android:layout_marginTop="5dp" 
                    android:src="@drawable/scan_line" /> 
            </RelativeLayout> 
       
            <ImageView 
                android:id="@+id/capture_mask_bottom" 
                android:layout_width="match_parent" 
                android:layout_height="wrap_content" 
                android:layout_alignParentBottom="true" 
                android:layout_below="@id/capture_crop_view" 
                android:background="@drawable/shadow" /> 
       
            <ImageView 
                android:id="@+id/capture_mask_left" 
                android:layout_width="wrap_content" 
                android:layout_height="match_parent" 
                android:layout_above="@id/capture_mask_bottom" 
                android:layout_alignParentLeft="true" 
                android:layout_below="@id/capture_mask_top" 
                android:layout_toLeftOf="@id/capture_crop_view" 
                android:background="@drawable/shadow" /> 
       
            <ImageView 
                android:id="@+id/capture_mask_right" 
                android:layout_width="wrap_content" 
                android:layout_height="match_parent" 
                android:layout_above="@id/capture_mask_bottom" 
                android:layout_alignParentRight="true" 
                android:layout_below="@id/capture_mask_top" 
                android:layout_toRightOf="@id/capture_crop_view" 
                android:background="@drawable/shadow" /> 
        </RelativeLayout> 
       
    </RelativeLayout>

    3. 计算截取区域 贴心注解: 如果你没有看上一篇ZBar扫描中关于扫描区域计算的解释,那赶紧回去,咱不能急,看完再来接上,否则你会不理解的!传送门

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    private void initCrop() { 
        int cameraWidth = cameraManager.getCameraResolution().y; 
        int cameraHeight = cameraManager.getCameraResolution().x; 
       
        /** 获取布局中扫描框的位置信息 */ 
        int[] location = new int[2]; 
        scanCropView.getLocationInWindow(location); 
       
        int cropLeft = location[0]; 
        int cropTop = location[1] - getStatusBarHeight(); 
       
        int cropWidth = scanCropView.getWidth(); 
        int cropHeight = scanCropView.getHeight(); 
       
        /** 获取布局容器的宽高 */ 
        int containerWidth = scanContainer.getWidth(); 
        int containerHeight = scanContainer.getHeight(); 
       
        /** 计算最终截取的矩形的左上角顶点x坐标 */ 
        int x = cropLeft * cameraWidth / containerWidth; 
        /** 计算最终截取的矩形的左上角顶点y坐标 */ 
        int y = cropTop * cameraHeight / containerHeight; 
       
        /** 计算最终截取的矩形的宽度 */ 
        int width = cropWidth * cameraWidth / containerWidth; 
        /** 计算最终截取的矩形的高度 */ 
        int height = cropHeight * cameraHeight / containerHeight; 
       
        /** 生成最终的截取的矩形 */ 
        mCropRect = new Rect(x, y, width + x, height + y); 

    5. 完整项目代码: 猛戳下载

    android URL转换二维码

    添加依赖
    compile 'com.google.zxing:core:3.3.0'
    创建工具类:ZXingUtils
    public class ZXingUtils {
    /**
    * 生成二维码 要转换的地址或字符串,可以是中文
    * 不需要logo最后一个参数传null
    * @param url
    * @param width
    * @param height
    * @return
    */
    public static Bitmap createQRImage(String url, final int width, final int height, Bitmap logoBitmap) {
    try {
    // 判断URL合法性
    if (url == null || "".equals(url) || url.length() < 1) {
    return null;
    }
    Hashtable hints = new Hashtable();
    hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
    // 图像数据转换,使用了矩阵转换
    BitMatrix bitMatrix = new QRCodeWriter().encode(url,
    BarcodeFormat.QR_CODE, width, height, hints);
    int[] pixels = new int[width * height];
    // 下面这里按照二维码的算法,逐个生成二维码的图片,
    // 两个for循环是图片横列扫描的结果
    for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
    if (bitMatrix.get(x, y)) {
    pixels[y * width + x] = 0xff000000;
    } else {
    pixels[y * width + x] = 0xffffffff;
    }
    }
    }
    // 生成二维码图片的格式,使用ARGB_8888
    Bitmap bitmap = Bitmap.createBitmap(width, height,
    Bitmap.Config.ARGB_8888);
    bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
    if (logoBitmap != null) {
    bitmap = addLogo(bitmap, logoBitmap);
    }
    return bitmap;
    } catch (WriterException e) {
    e.printStackTrace();
    }
    return null;
    }

    /**
    * 在二维码中间添加Logo图案
    */
    private static Bitmap addLogo(Bitmap src, Bitmap logo) {
    if (src == null) {
    return null;
    }

    if (logo == null) {
    return src;
    }

    //获取图片的宽高
    int srcWidth = src.getWidth();
    int srcHeight = src.getHeight();
    int logoWidth = logo.getWidth();
    int logoHeight = logo.getHeight();

    if (srcWidth == 0 || srcHeight == 0) {
    return null;
    }

    if (logoWidth == 0 || logoHeight == 0) {
    return src;
    }

    //logo大小为二维码整体大小的1/5
    float scaleFactor = srcWidth * 1.0f / 5 / logoWidth;
    Bitmap bitmap = Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888);
    try {
    Canvas canvas = new Canvas(bitmap);
    canvas.drawBitmap(src, 0, 0, null);
    canvas.scale(scaleFactor, scaleFactor, srcWidth / 2, srcHeight / 2);
    canvas.drawBitmap(logo, (srcWidth - logoWidth) / 2, (srcHeight - logoHeight) / 2, null);

    canvas.save(Canvas.ALL_SAVE_FLAG);
    canvas.restore();
    } catch (Exception e) {
    bitmap = null;
    e.getStackTrace();
    }

    return bitmap;
    }
    }
    activity代码
    Bitmap bitmap = ZXingUtils.createQRImage(url, 600, 600,BitmapFactory.decodeResource(getResources(), R.drawable.call));
    imageView.setImageBitmap(bitmap);
    下面的是网上找到的一个工具类
    /**
    *
    * 生成条形码和二维码的工具
    */
    public class ZXingUtils {
    /**
    * 生成二维码 要转换的地址或字符串,可以是中文
    *
    * @param url
    * @param width
    * @param height
    * @return
    */
    public static Bitmap createQRImage(String url, final int width, final int height) {
    try {
    // 判断URL合法性
    if (url == null || "".equals(url) || url.length() < 1) {
    return null;
    }
    Hashtable hints = new Hashtable();
    hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
    // 图像数据转换,使用了矩阵转换
    BitMatrix bitMatrix = new QRCodeWriter().encode(url,
    BarcodeFormat.QR_CODE, width, height, hints);
    int[] pixels = new int[width * height];
    // 下面这里按照二维码的算法,逐个生成二维码的图片,
    // 两个for循环是图片横列扫描的结果
    for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
    if (bitMatrix.get(x, y)) {
    pixels[y * width + x] = 0xff000000;
    } else {
    pixels[y * width + x] = 0xffffffff;
    }
    }
    }
    // 生成二维码图片的格式,使用ARGB_8888
    Bitmap bitmap = Bitmap.createBitmap(width, height,
    Bitmap.Config.ARGB_8888);
    bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
    return bitmap;
    } catch (WriterException e) {
    e.printStackTrace();
    }
    return null;
    }

    /**
    * 生成条形码
    *
    * @param context
    * @param contents
    * 需要生成的内容
    * @param desiredWidth
    * 生成条形码的宽带
    * @param desiredHeight
    * 生成条形码的高度
    * @param displayCode
    * 是否在条形码下方显示内容
    * @return
    */
    public static Bitmap creatBarcode(Context context, String contents,
    int desiredWidth, int desiredHeight, boolean displayCode) {
    Bitmap ruseltBitmap = null;
    /**
    * 图片两端所保留的空白的宽度
    */
    int marginW = 20;
    /**
    * 条形码的编码类型
    */
    BarcodeFormat barcodeFormat = BarcodeFormat.CODE_128;

    if (displayCode) {
    Bitmap barcodeBitmap = encodeAsBitmap(contents, barcodeFormat,
    desiredWidth, desiredHeight);
    Bitmap codeBitmap = creatCodeBitmap(contents, desiredWidth + 2
    * marginW, desiredHeight, context);
    ruseltBitmap = mixtureBitmap(barcodeBitmap, codeBitmap, new PointF(
    0, desiredHeight));
    } else {
    ruseltBitmap = encodeAsBitmap(contents, barcodeFormat,
    desiredWidth, desiredHeight);
    }

    return ruseltBitmap;
    }

    /**
    * 生成条形码的Bitmap
    *
    * @param contents
    * 需要生成的内容
    * @param format
    * 编码格式
    * @param desiredWidth
    * @param desiredHeight
    * @return
    * @throws WriterException
    */
    protected static Bitmap encodeAsBitmap(String contents,
    BarcodeFormat format, int desiredWidth, int desiredHeight) {
    final int WHITE = 0xFFFFFFFF;
    final int BLACK = 0xFF000000;

    MultiFormatWriter writer = new MultiFormatWriter();
    BitMatrix result = null;
    try {
    result = writer.encode(contents, format, desiredWidth,
    desiredHeight, null);
    } catch (WriterException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }

    int width = result.getWidth();
    int height = result.getHeight();
    int[] pixels = new int[width * height];
    // All are 0, or black, by default
    for (int y = 0; y < height; y++) {
    int offset = y * width;
    for (int x = 0; x < width; x++) {
    pixels[offset + x] = result.get(x, y) ? BLACK : WHITE;
    }
    }

    Bitmap bitmap = Bitmap.createBitmap(width, height,
    Bitmap.Config.ARGB_8888);
    bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
    return bitmap;
    }

    /**
    * 生成显示编码的Bitmap
    *
    * @param contents
    * @param width
    * @param height
    * @param context
    * @return
    */
    protected static Bitmap creatCodeBitmap(String contents, int width,
    int height, Context context) {
    TextView tv = new TextView(context);
    LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
    LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
    tv.setLayoutParams(layoutParams);
    tv.setText(contents);
    tv.setHeight(height);
    tv.setGravity(Gravity.CENTER_HORIZONTAL);
    tv.setWidth(width);
    tv.setDrawingCacheEnabled(true);
    tv.setTextColor(Color.BLACK);
    tv.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
    tv.layout(0, 0, tv.getMeasuredWidth(), tv.getMeasuredHeight());

    tv.buildDrawingCache();
    Bitmap bitmapCode = tv.getDrawingCache();
    return bitmapCode;
    }

    /**
    * 将两个Bitmap合并成一个
    *
    * @param first
    * @param second
    * @param fromPoint
    * 第二个Bitmap开始绘制的起始位置(相对于第一个Bitmap)
    * @return
    */
    protected static Bitmap mixtureBitmap(Bitmap first, Bitmap second,
    PointF fromPoint) {
    if (first == null || second == null || fromPoint == null) {
    return null;
    }
    int marginW = 20;
    Bitmap newBitmap = Bitmap.createBitmap(
    first.getWidth() + second.getWidth() + marginW,
    first.getHeight() + second.getHeight(), Config.ARGB_4444);
    Canvas cv = new Canvas(newBitmap);
    cv.drawBitmap(first, marginW, 0, null);
    cv.drawBitmap(second, fromPoint.x, fromPoint.y, null);
    cv.save(Canvas.ALL_SAVE_FLAG);
    cv.restore();

    return newBitmap;
    }

    }

    http://repo1.maven.org/maven2/com/google/zxing

  • 相关阅读:
    IDENT_CURRENT ,@@identity,SCOPE_IDENTITY() 之间对比
    移动无边框窗体
    Winform拖拽改变无边框窗体大小
    配置错误---分析器错误消息: 无法识别的属性“targetFramework”。请注意属性名称区分大小写。
    c# winform中预防窗体重复打开
    C# TreeView 控件的综合使用方法
    c# vs2010 连接access数据库
    解决python pip缓慢
    securecrt密码获取
    linux开启telnet
  • 原文地址:https://www.cnblogs.com/Alex80/p/11624585.html
Copyright © 2011-2022 走看看