android二维码_android二维码扫描 - CSDN
精华内容
参与话题
  • Android二维码识别与生成

    千次阅读 2016-09-02 20:04:09
    最近几年二维码是越来越火了,特别是随着移动端的便利性,走到哪里都是扫一扫。二维码支付、二维码扫描登录、二维码扫描关注加好友.....越来越多的应用也都加上了二维码扫描的功能,作为移动开发者,对这些新奇的...

            最近几年二维码是越来越火了,特别是随着移动端的便利性,走到哪里都是扫一扫。二维码支付、二维码扫描登录、二维码扫描关注加好友.....越来越多的应用也都加上了二维码扫描的功能,作为移动开发者,对这些新奇的东西当然要尝试一下了。在查阅了一下网上的资料后,自己算是对二维码的扫描和生生成有了个初步的了解,写个笔记,以后想集成进项目时,也会方便很多。

            首先,什么是二维码?

            “二维条码/二维码(2-dimensional bar code)是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的;在代码编制上巧妙地利用构成计算机内部逻辑基础的“0”、“1”比特流的概念,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图象输入设备或光电扫描设备自动识读以实现信息自动处理:它具有条码技术的一些共性:每种码制有其特定的字符集;每个字符占有一定的宽度;具有一定的校验功能等。同时还具有对不同行的信息自动识别功能、及处理图形旋转变化点。 ”

           百科的解释如此。既然有二维码,那么肯定有一维码。相信你们对一维码认识的更早,因为一维码。最为常见的就是各种包装商品或者书籍后面的条码。当然本项目也是支持一维码的识别的。

           二维码在不同的语言和平台下有不同的支持库,Android当然是选择java支持库了,这个项目是google的大神开发的ZXing库。github地址是:https://github.com/zxing/zxing。

           相信大家都和我一样,下载后,都在考虑那怎么样集成进去项目里试一试了?因为这个库文件还是很大的。我们选择自己想要的即可。

           首先你得找到zxing.jar包,并添加到项目库中,其次你需要这几个包和里面的文件,包名我改了点。然后将对应的资源也引进去,注意其是放在values文件夹下的。


              其中carmera包,是对相机的初始化和一些配置处理;decoding是对相机扫描回的结果的分析处理;view是扫描框和接口处理回调。

             项目里还有其他2个包,里面结构如下:

              

                其中MainActivity是程序主入口,CustomScanViewActivity是自定义扫描窗口的FragmentActivity,CaptureFragment是自定义扫描窗口的view,在这进行相机的初始化,和处理扫描结果。扫描成功后将扫描的结果和二维码当作参数传递到handleDecode方法里进行处理,这里采用的是接口回调的方式,进行处理:

     /**
         * 处理扫描结果
         *
         * @param result
         * @param barcode
         */
        public void handleDecode(Result result, Bitmap barcode) {
            inactivityTimer.onActivity();
            playBeepSoundAndVibrate();
    
            if (result == null || TextUtils.isEmpty(result.getText())) {
                if (analyzeCallback != null) {
                    analyzeCallback.onAnalyzeFailed();
                }
            } else {
                if (analyzeCallback != null) {
                    analyzeCallback.onAnalyzeSuccess(barcode, result.getText());
                }
            }
        }

    接口函数

     /**
         * 解析二维码结果接口函数
         */
        public interface AnalyticCallback{
    
            public void onAnalyzeSuccess(Bitmap mBitmap, String result);
    
            public void onAnalyzeFailed();
        }
    解析方法

    /**
         * 解析二维码图片方法
         * @param mBitmap
         * @param analyzeCallback
         */
        public static void analyticBitmap(Bitmap mBitmap, AnalyticCallback analyzeCallback) {
    
            MultiFormatReader multiFormatReader = new MultiFormatReader();
    
            // 解码的参数
            Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(2);
            // 可以解析的编码类型
            Vector<BarcodeFormat> decodeFormats = new Vector<BarcodeFormat>();
            if (decodeFormats == null || decodeFormats.isEmpty()) {
                decodeFormats = new Vector<BarcodeFormat>();
    
                // 这里设置可扫描的类型
                decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);//条形码
                decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);//二维码
                decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);//其他码
            }
            hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
         
            // 设置解析配置参数
            multiFormatReader.setHints(hints);
    
            // 开始对图像资源解码
            Result rawResult = null;
            try {
             
                int width = mBitmap.getWidth();
                int height = mBitmap.getHeight();
                int[] pixels = new int[width * height];
                mBitmap.getPixels(pixels, 0, width, 0, 0, width, height);
                RGBLuminanceSource source = new RGBLuminanceSource(width, height, pixels);
            	rawResult = multiFormatReader.decode(new BinaryBitmap(new HybridBinarizer(source)));
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            if (rawResult != null) {
                if (analyzeCallback != null) {
                    analyzeCallback.onAnalyzeSuccess(mBitmap, rawResult.getText());
                }
            } else {
                if (analyzeCallback != null) {
                    analyzeCallback.onAnalyzeFailed();
                }
            }
        }

     由上面可以看出,我把一维码也添加进去了,也就支持扫条形码了。当然还可以扫描本地的二维码图片,首先我们要打开本地的图库,这里有2个方法,但是有个方法有点小bug,他打不开我自己的截图,因为他打开的是手机里的图库(相册)应用,而截图不在图库里,所以不能添加(我华为荣耀手机是这样的,不知道其他型号手机是否是这样),为此我采用了另外一个方法,列出系统图片文件夹表,让用户自己选择。

    方法一:

       Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
       intent.addCategory(Intent.CATEGORY_OPENABLE);
       intent.setType("image/*");//打开的是相册 
       startActivityForResult(intent, REQUEST_IMAGE);
    然后在onActivityResult里进行接收处理

          Uri uri = data.getData();
          ContentResolver mContentResolver= getContentResolver();
          Bitmap mBitmap = MediaStore.Images.Media.getBitmap(mContentResolver, uri);//根据给定的图片uri,将其转换为bitmap
    通过给定的uri转换为Bitmap,接着调用我们的接口函数

           QRCodeUtils.analyticBitmap(mBitmap, new QRCodeUtils.AnalyticCallback() {
    		                        @Override
    		                     public void onAnalyzeSuccess(Bitmap mBitmap, String result) {
    		                            Toast.makeText(MainActivity.this, "解析结果: " + result,1).show();
    		                         
    		                        }
    
    		                        @Override
    		                     public void onAnalyzeFailed() {
    		                           Toast.makeText(MainActivity.this, "解析图片失败", 1).show();
    		                        	
    		                        }
    		                    });
    但是通过上面的那个打开本地图库的方法找不到截图文件,于是我采用了这个方法

          Intent intent = new Intent(Intent.ACTION_PICK,
                            android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);//打开的是所有图片文件夹列表
                    startActivityForResult(intent, REQUEST_IMAGE);
    处理时换成这样的

               Cursor cursor = getContentResolver().query(data.getData(), null, null, null, null);
        				if (cursor.moveToFirst()) {
        					photo_path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
        				    Log.e("图片路径-----》", photo_path);
        				   
        				}
        				cursor.close();
                 Bitmap mBitmap= getDecodeAbleBitmap(photo_path);
    通过uri去查询ContntResolver,返回的是个coursor对象,然后将其类似于读取数据库一样读出来,得到路径名,接着将本地图片文件转换为bitmao,为避免图片过大,先进行压缩处理。

     private static Bitmap getDecodeAbleBitmap(String picturePath) {
            try {
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeFile(picturePath, options);
                int sampleSize = options.outHeight / 400;
                if (sampleSize <= 0)
                    sampleSize = 1;
                options.inSampleSize = sampleSize;
                options.inJustDecodeBounds = false;
    
                return BitmapFactory.decodeFile(picturePath, options);
            } catch (Exception e) {
                return null;
            }
        }

    得到bitmap对象后,接下来的步骤就一样了,调用工具类进行解析。

    如果对扫描框想更改大小、刷新速度或者外观的,可以在viewfinderView中进行修改

    /*
     * Copyright (C) 2008 ZXing authors
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.example.zxing.view;
    
    import android.content.Context;
    import android.content.res.Resources;
    import android.content.res.TypedArray;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.PixelFormat;
    import android.graphics.Point;
    import android.graphics.Rect;
    import android.graphics.drawable.Drawable;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.View;
    
    import com.example.qrcodedemo.R;
    import com.google.zxing.ResultPoint;
    import com.example.zxing.camera.CameraManager;
    
    import java.util.Collection;
    import java.util.HashSet;
    
    /**
     * 自定义组件实现,扫描功能
     */
    public final class ViewfinderView extends View {
        /**
         * 刷新界面的时间
         */
    	private static final long ANIMATION_DELAY = 10L;
    	private static final int OPAQUE = 0xFF;
    
    	private final Paint paint;
    	private Bitmap resultBitmap;
    	private final int maskColor;
    	private final int resultColor;
    	private final int resultPointColor;
    	private Collection<ResultPoint> possibleResultPoints;
    	private Collection<ResultPoint> lastPossibleResultPoints;
    	
    	// 扫描框边角颜色
    	private int innercornercolor;
    	// 扫描框边角长度
    	private int innercornerlength;
    	// 扫描框边角宽度
    	private int innercornerwidth;
    	
    	// 扫描线移动的y
    	private int scanLineTop;
    	// 扫描线移动速度
    	private int SCAN_VELOCITY;
    	// 扫描线
    	Bitmap scanLight;
    	
    	public ViewfinderView(Context context, AttributeSet attrs) {
    		super(context, attrs);
    
    		paint = new Paint();
    		Resources resources = getResources();
    		maskColor = resources.getColor(R.color.viewfinder_mask);
    		resultColor = resources.getColor(R.color.result_view);
    		resultPointColor = resources.getColor(R.color.possible_result_points);
    		possibleResultPoints = new HashSet<ResultPoint>(5);
    
    		scanLight = BitmapFactory.decodeResource(resources,
    				R.drawable.scan_light);//扫描线
    
    		initInnerRect(context, attrs);
    	}
    
    	/**
    	 * 初始化内部框的大小
    	 * 
    	 * @param context
    	 * @param attrs
    	 */
    	private void initInnerRect(Context context, AttributeSet attrs) {
    		TypedArray ta = context.obtainStyledAttributes(attrs,
    				R.styleable.innerrect);
    
    		// 扫描框距离顶部
    		int innerMarginTop = ta.getInt(R.styleable.innerrect_inner_margintop,
    				-1);
    		if (innerMarginTop != -1) {
    			CameraManager.FRAME_MARGINTOP = dip2px(context, innerMarginTop);
    		}
    
    		// 扫描框的宽度
    		int innerrectWidth = ta.getInt(R.styleable.innerrect_inner_width, 210);
    		CameraManager.FRAME_WIDTH = dip2px(context, innerrectWidth);
    
    		// 扫描框的高度
    		int innerrectHeight = ta
    				.getInt(R.styleable.innerrect_inner_height, 210);
    		CameraManager.FRAME_HEIGHT = dip2px(context, innerrectHeight);
    
    		// 扫描框边角颜色
    		innercornercolor = ta.getColor(
    				R.styleable.innerrect_inner_corner_color,
    				Color.parseColor("#45DDDD"));
    		// 扫描框边角长度
    		innercornerlength = ta.getInt(
    				R.styleable.innerrect_inner_corner_length, 65);
    		// 扫描框边角宽度
    		innercornerwidth = ta.getInt(R.styleable.innerrect_inner_corner_width,
    				15);
    
    		// 扫描bitmap
    		Drawable drawable = ta
    				.getDrawable(R.styleable.innerrect_inner_scan_bitmap);
    		if (drawable != null) {
    		}
    
    		// 扫描控件
    		scanLight = BitmapFactory.decodeResource(getResources(), ta
    				.getResourceId(R.styleable.innerrect_inner_scan_bitmap,
    						R.drawable.scan_light));
    		// 扫描速度
    		SCAN_VELOCITY = ta.getInt(R.styleable.innerrect_inner_scan_speed, 10);
    
    		ta.recycle();
    	}
    
    	@Override
    	public void onDraw(Canvas canvas) {
    		Rect frame = CameraManager.get().getFramingRect();
    		if (frame == null) {
    			return;
    		}
    		int width = canvas.getWidth();
    		int height = canvas.getHeight();
    
    		// Draw the exterior (i.e. outside the framing rect) darkened
    		paint.setColor(resultBitmap != null ? resultColor : maskColor);
    		canvas.drawRect(0, 0, width, frame.top, paint);
    		canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
    		canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1,
    				paint);
    		canvas.drawRect(0, frame.bottom + 1, width, height, paint);
    
    		if (resultBitmap != null) {
    			// Draw the opaque result bitmap over the scanning rectangle
    			paint.setAlpha(OPAQUE);
    			canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint);
    		} else {
    
    			drawFrameBounds(canvas, frame);
    
    			drawScanLight(canvas, frame);
    
    			Collection<ResultPoint> currentPossible = possibleResultPoints;
    			Collection<ResultPoint> currentLast = lastPossibleResultPoints;
    			if (currentPossible.isEmpty()) {
    				lastPossibleResultPoints = null;
    			} else {
    				possibleResultPoints = new HashSet<ResultPoint>(5);
    				lastPossibleResultPoints = currentPossible;
    				paint.setAlpha(OPAQUE);
    				paint.setColor(resultPointColor);
    				for (ResultPoint point : currentPossible) {
    					canvas.drawCircle(frame.left + point.getX(), frame.top
    							+ point.getY(), 6.0f, paint);
    				}
    			}
    			if (currentLast != null) {
    				paint.setAlpha(OPAQUE / 2);
    				paint.setColor(resultPointColor);
    				for (ResultPoint point : currentLast) {
    					canvas.drawCircle(frame.left + point.getX(), frame.top
    							+ point.getY(), 3.0f, paint);
    				}
    			}
    
    			postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top,
    					frame.right, frame.bottom);
    		}
    	}
    
    
    
    	/**
    	 * 绘制移动扫描线
    	 *
    	 * @param canvas
    	 * @param frame
    	 */
    	private void drawScanLight(Canvas canvas, Rect frame) {
    
    		if (scanLineTop == 0) {
    			scanLineTop = frame.top;
    		}
    
    		if (scanLineTop >= frame.bottom - 30) {
    			scanLineTop = frame.top;
    		} else {
    			scanLineTop += SCAN_VELOCITY;
    		}
    		Rect scanRect = new Rect(frame.left, scanLineTop, frame.right,
    				scanLineTop + 30);
    		canvas.drawBitmap(scanLight, null, scanRect, paint);
    	}
    
    
    
    	/**
    	 * 绘制取景框边框
    	 *
    	 * @param canvas
    	 * @param frame
    	 */
    	private void drawFrameBounds(Canvas canvas, Rect frame) {
    
    		paint.setColor(innercornercolor);
    		paint.setStyle(Paint.Style.FILL);
    
    		int corWidth = innercornerwidth;
    		int corLength = innercornerlength;
    
    		// 左上角
    		canvas.drawRect(frame.left, frame.top, frame.left + corWidth, frame.top
    				+ corLength, paint);
    		canvas.drawRect(frame.left, frame.top, frame.left + corLength,
    				frame.top + corWidth, paint);
    		  // 右上角
    		canvas.drawRect(frame.right - corWidth, frame.top, frame.right,
    				frame.top + corLength, paint);
    		canvas.drawRect(frame.right - corLength, frame.top, frame.right,
    				frame.top + corWidth, paint);
    		 // 左下角
    		canvas.drawRect(frame.left, frame.bottom - corLength, frame.left
    				+ corWidth, frame.bottom, paint);
    		canvas.drawRect(frame.left, frame.bottom - corWidth, frame.left
    				+ corLength, frame.bottom, paint);
    		  // 右下角
    		canvas.drawRect(frame.right - corWidth, frame.bottom - corLength,
    				frame.right, frame.bottom, paint);
    		canvas.drawRect(frame.right - corLength, frame.bottom - corWidth,
    				frame.right, frame.bottom, paint);
    	}
    
    	public void drawViewfinder() {
    		resultBitmap = null;
    		invalidate();
    	}
    
    	public void addPossibleResultPoint(ResultPoint point) {
    		possibleResultPoints.add(point);
    	}
    
    	/**
         * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
         */
    	public static int dip2px(Context context, float dpValue) {
    		final float scale = context.getResources().getDisplayMetrics().density;
    		return (int) (dpValue * scale + 0.5f);
    	}
    
    }

    至此,扫描二维码和解析本地图片二维码已介绍完了,接下来是如何生成二维码的介绍。

    生成二维码采用的是这个方法

        BitMatrix bitMatrix = new QRCodeWriter().encode(text, BarcodeFormat.QR_CODE, width, height, hints);
    这个方法得到的是一个bit二维矩阵,然后通过2个for循环,将其放置进一个一维的数组中

               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;
                        }
                    }
                }
    最后生成二维码bitmap

              //生成二维码图片的格式,使用ARGB_8888
                Bitmap bitmap = Bitmap.createBitmap(QR_WIDTH, QR_HEIGHT, Bitmap.Config.ARGB_8888);
                bitmap.setPixels(pixels, 0, QR_WIDTH, 0, 0, QR_WIDTH, QR_HEIGHT);
    

    如果想对扫描的界面想增加点自己的按钮或者功能的,因为采用的是fragment,也可以替换,实现定制扫描的UI界面

    1,在新的activity中定义我们的布局

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/activity_second"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <Button
            android:id="@+id/second_button1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="取消"
            android:layout_marginTop="20dp"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"
            android:layout_marginBottom="10dp"
            android:layout_gravity="bottom|center_horizontal"
            />
    
        <FrameLayout
            android:id="@+id/fl_my_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            ></FrameLayout>
    
    </FrameLayout>
            启动id为fl_my_container的FrameLayout就是我们需要替换的扫描组件,也就是说我们会将我们定义的扫描Fragment替换到id为fl_my_container的FrameLayout的位置。而上面的button是我们添加的一个额外的控件,在这里你可以添加任意的控件,各种UI效果等。具体可以看下面在Activity的初始化过程。

           在Activity中执行Fragment的初始化操作

     protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_second);
            /**
             * 执行扫面Fragment的初始化操作
             */
            CaptureFragment captureFragment = new CaptureFragment();
            // 为二维码扫描界面设置定制化界面
            QRCodeUtils.setFragmentArgs(captureFragment, R.layout.my_camera);
    
            captureFragment.setAnalyzeCallback(analyzeCallback);
            /**
             * 替换我们的扫描控件
             */                                                                                                                                                        getSupportFragmentManager().beginTransaction().replace(R.id.fl_my_container, captureFragment).commit();
        }
    注意:因为用到了fragment,假如你集成的是activity,那你要导入v4包,否则在调用getSupportFragmentManager时,会找不到调用方法。

    而此activity此时要改成要继承于FragmentActivity。

    在上面的onCreate方法中,我们调用了QRCodeUtils.setFragmentArgs(captureFragment, R.layout.my_camera);方法。该方法主要用于修改扫描界面扫描框与透明框相对位置的,与若不调用的话,其会显示默认的组件效果,而如果调用该方法的话,可以修改扫描框与透明框的相对位置等UI效果,我们可以看一下my_camera布局文件的实现。

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >
    
    
        <SurfaceView
            android:id="@+id/preview_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            />
    
    
        <com.example.zxing.view.ViewfinderView
        android:id="@+id/viewfinder_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:inner_width="180"
        app:inner_height="180"
        app:inner_margintop="120"
        app:inner_corner_color="@color/scan_corner_color"
        app:inner_corner_length="60"
        app:inner_corner_width="10"
        app:inner_scan_bitmap="@drawable/scan_image"
        app:inner_scan_speed="10"
        />
    
    
    </FrameLayout>
    
    上面我们自定义的扫描控件的布局文件,下面我们看一下默认的扫描框的布局文件:
    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >
    
        <SurfaceView
            android:id="@+id/preview_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center" />
    
        <com.example.zxing.view.ViewfinderView
            android:id="@+id/viewfinder_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    
    </FrameLayout>

    好了,项目介绍到这里了,总体来说,效果功能都实现了,待实际需求时,可以直接集成更改即可。源码地址:点我下载

    参考别人的github项目,发现别人的扫描速度稍微快一点,以后有待研究。https://github.com/bingoogolapple/BGAQRCode-Android


    注:解决关于扫描时出现图片拉伸的问题。

    如果是直接引用的zxing包里面的camera文件时,可能会出现扫描的二维码在扫描框内出现拉伸问题,因为Zxing包里的二维码扫描默认是横屏扫描的,改为竖屏后出现比例问题,所以要修正过来。

    可以在camera包里面的CameraConfigurationManager.java文件里的

    void initFromCameraParameters(Camera camera)方法

    在 Log.d(TAG, "Screen resolution: " + screenResolution);这句之后增加

            Point screenResolutionForCamera = new Point();  
            screenResolutionForCamera.x = screenResolution.x;  
            screenResolutionForCamera.y = screenResolution.y;  
            // preview size is always something like 480*320, other 320*480  
            if (screenResolution.x < screenResolution.y) {  
            screenResolutionForCamera.x = screenResolution.y;  
            screenResolutionForCamera.y = screenResolution.x;  
            }  
    然后将
    cameraResolution = getCameraResolution(parameters, screenResolution);  
    注释掉,改为:

    cameraResolution = getCameraResolution(parameters, screenResolutionForCamera); 
    这样就可以了,解决图片拉伸的问题。

    8/15日更新
    增加二维码生成识别容错率

    在我们生产二维码的时候,有时二维码中间放logo的话,识别起来比不放困难点,但是logo又不能太小,否则就起不到宣传认识效果,太大了后识别就稍微困难点了。一方面我们的图片大小要适中,另一方面又要增加二维码的识别容错率,这样可以加快识别速度。

    默认的二维码容错率是ErrorCorrectionLevel.L

    在QRCodeWriter源码中(位置是com.google.zxing.qrcode)可以看到有默认值

    ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
        int quietZone = QUIET_ZONE_SIZE;
        if (hints != null) {
          ErrorCorrectionLevel requestedECLevel = (ErrorCorrectionLevel) hints.get(EncodeHintType.ERROR_CORRECTION);
          if (requestedECLevel != null) {
            errorCorrectionLevel = requestedECLevel;
          }
          Integer quietZoneInt = (Integer) hints.get(EncodeHintType.MARGIN);
          if (quietZoneInt != null) {
            quietZone = quietZoneInt;
          }
        }
    可以看出他是通过一个hashtable.get获得的,为此,我们可以通过put去修改,将我们要设置的值传进去。
                Hashtable hints = new Hashtable();
                hints.put(EncodeHintType.CHARACTER_SET, "utf-8");//设置编码格式,否则中文会识别不了
                hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);//容错率默认是7%,现改为30%
    后面的BitMatrix不用变。





    如果上面有什么纰漏或者对上面有什么疑问,欢迎留言探讨!!!








    展开全文
  • 玩转Android二维码生成与识别

    万次阅读 多人点赞 2016-05-26 15:29:27
    二维码,我们也称作QRCode,QR表示quick response即快速响应,在很多App中我们都能见到二维码的身影,最常见的莫过于微信了。那么今天我们就来看看怎么样在我们自己的App中集成二维码的扫描与生成功能。OK,废话不多...

    二维码,我们也称作QRCode,QR表示quick response即快速响应,在很多App中我们都能见到二维码的身影,最常见的莫过于微信了。那么今天我们就来看看怎么样在我们自己的App中集成二维码的扫描与生成功能。OK,废话不多说,我们就开始做吧。

    二维码的使用我主要想分为两部分来给大家介绍,一部分就是二维码的生成,这里的知识点都很简单,还有一部分是二维码的识别,这里稍微麻烦一些,不过细心来做其实也很简单。二维码的开发使用我们大多都是使用Google提供的zxing这个类库,使用这个类库我们需要先下载核心jar包,下载地址,如果我们只想生成二维码那么这个就够了,但是如果我们还想做二维码的识别,那么我们需要在刚才的基础上继续添加GitHub上的开源项目,这个我们在后面再说。

    1.二维码的生成

    先来看一张效果图:


    1.1  准备工作

    如果我们只做二维码的生成,那么只需要添加核心jar包即可,如下:


    1.2  二维码生成

    OK,添加完jar包之后我们就可以开始写二维码生成代码了,二维码本身就是一张Bitmap图片,所以我们这里主要就是看怎么样来生成这张图片,我在主界面添加一个按钮和一个ImageView,当点击按钮时生成一张二维码图片显示在ImageView上。布局如下:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="org.mobiletrain.qrwriter.MainActivity">
    
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="generate"
            android:text="生成二维码"/>
    
        <ImageView
            android:id="@+id/iv"
            android:layout_width="256dp"
            android:layout_height="256dp"
            android:layout_centerInParent="true"/>
    </RelativeLayout>

    当我点击按钮时生成二维码图片,那我们就来看看生成二维码图片的核心代码:

        private Bitmap generateBitmap(String content,int width, int height) {
            QRCodeWriter qrCodeWriter = new QRCodeWriter();
            Map<EncodeHintType, String> hints = new HashMap<>();
            hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
            try {
                BitMatrix encode = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
                int[] pixels = new int[width * height];
                for (int i = 0; i < height; i++) {
                    for (int j = 0; j < width; j++) {
                        if (encode.get(j, i)) {
                            pixels[i * width + j] = 0x00000000;
                        } else {
                            pixels[i * width + j] = 0xffffffff;
                        }
                    }
                }
                return Bitmap.createBitmap(pixels, 0, width, width, height, Bitmap.Config.RGB_565);
            } catch (WriterException e) {
                e.printStackTrace();
            }
            return null;
        }

    首先这个方法接收三个参数,这三个参数分别表示生成二维码的文本内容(你要把哪一个文本用二维码图片表示出来),第二个和第三个参数分别表示生成的二维码图片的宽和高。在这里,我们首先要获得一个QRCodeWriter实例,该实例中有一个方法叫做encode,通过该方法对文本内容进行编码,该方法共有五个参数,第一个参数表示生成二维码的文本内容,第二个参数表示编码格式,第三个参数表示生成的二维码的宽度,第四个参数表示生成的二维码的高度,第五个参数可选,可以用来设置文本的编码,encode方法的返回值是一个BitMatrix,你可以把BitMatrix理解成一个二维数组,这个二维数组的每一个元素都表示一个像素点是否有数据。OK,接下来我们需要定义一个int数组用来存放Bitmap中所有像素点的颜色,可是我们又怎么知道每一个像素点是什么颜色呢?这个时候就需要我们遍历BitMatrix了,如果BitMatrix上的点表示 该点有数据,那么对应在Bitmap上的像素点就是黑色,否则就是白色。BitMatrix中的get方法的返回值为一个boolean类型,true表示该点有数据,false表示该点没有数据。通过两个嵌套的for循环将BitMatrix遍历一遍,然后给pixels数组都赋上值,OK,pixels数组有值之后,接下来调用Bitmap的createBitmap方法创建一个Bitmap出来就可以了,createBitmap方法共接收6个参数,第一个参数表示Bitmap中所有像素点的颜色,第二个参数表示像素点的偏移量,第三个参数表示Bitmap每行有多少个像素点,第四个参数表示生成的Bitmap的宽度,第五个参数表示生成的Bitmap的高度,第六个参数表示生成的Bitmap的色彩模式,因为二维码只有黑白两种颜色,所以我们可以不用考虑透明度,直接使用RGB_565即可。OK,这样的话我们就获取到了二维码的图片了,最后我们再来看看点击事件:

        public void generate(View view) {
            Bitmap qrBitmap = generateBitmap("http://www.csdn.net",400, 400);
            iv.setImageBitmap(qrBitmap);
        }

    效果图如下:


    1.3  给二维码中心添加Logo

    OK,如果你没有特殊的需求那么这样就OK了,但是我们见到的大多数二维码的正中心都有一个Logo,那么这个效果要怎么实现呢?这里就是图片绘制的内容了,我封装了一个方法专门来解决这个问题,代码如下:

        private Bitmap addLogo(Bitmap qrBitmap, Bitmap logoBitmap) {
            int qrBitmapWidth = qrBitmap.getWidth();
            int qrBitmapHeight = qrBitmap.getHeight();
            int logoBitmapWidth = logoBitmap.getWidth();
            int logoBitmapHeight = logoBitmap.getHeight();
            Bitmap blankBitmap = Bitmap.createBitmap(qrBitmapWidth, qrBitmapHeight, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(blankBitmap);
            canvas.drawBitmap(qrBitmap, 0, 0, null);
            canvas.save(Canvas.ALL_SAVE_FLAG);
            float scaleSize = 1.0f;
            while ((logoBitmapWidth / scaleSize) > (qrBitmapWidth / 5) || (logoBitmapHeight / scaleSize) > (qrBitmapHeight / 5)) {
                scaleSize *= 2;
            }
            float sx = 1.0f / scaleSize;
            canvas.scale(sx, sx, qrBitmapWidth / 2, qrBitmapHeight / 2);
            canvas.drawBitmap(logoBitmap, (qrBitmapWidth - logoBitmapWidth) / 2, (qrBitmapHeight - logoBitmapHeight) / 2, null);
            canvas.restore();
            return blankBitmap;
        }

    addLogo这个方法接收两个参数,第一个参数就是我们在1.2节中生成的二维码的Bitmap图片,第二个参数就是我们的logo图片,在该方法中我先获取到两张Bitmap各自的宽高,然后创建一个新的空白的Bitmap,这个新的空白的Bitmap的宽高和二维码的宽高一致,然后创建一个Canvas对象,创建Canvas对象的时候将blankBitmap传入,这样我一会绘制的东西相当于都是绘制在了blankBitmap上了。canvas的drawBitmap方法接收四个参数,第一个是你要绘制的Bitmap对象,第二个和第三个是你要绘制的Bitmap的左上角的坐标,第四个参数是一个画笔,一般情况下我们给一个null就可以了,如果你要设置重复模式等等效果的时候可以不给null。我们使用drawBitmap方法先将原本的二维码图片绘制出来,绘制完成之后,调用canvas的save方法,将当前的绘制状态保存下来,然后对画布进行缩放,缩小画布之后我们来绘制Logo,一帮情况下logo的宽高为二维码原图宽高的1/5(中心logo图片不宜过大,否则会影响到二维码的识别),所以我们先通过一个while循环获得缩放比例,然后调用canvas的scale方法对画布进行缩放,前两个参数表示宽高的缩放比例,大于1表示放大,小于1表示缩小,后两个参数表示缩放的中心点。缩放完成之后我们就可以绘制logo了,logo绘制完成之后,调用canvas的restore方法将画布恢复为原来的状态,最后将blankBitmap返回。在点击事件中调用这个方法即可,代码如下:

        public void generate(View view) {
            Bitmap qrBitmap = generateBitmap("http://www.csdn.net",400, 400);
            Bitmap logoBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
            Bitmap bitmap = addLogo(qrBitmap, logoBitmap);
            iv.setImageBitmap(bitmap);
        }

    效果图如下:

    OK,至此,我们的二维码生成就说完了,就是这么简单。

    2.二维码的识别

    二维码的识别是一个稍微麻烦的事情,一般情况下,我们直接使用GitHub上的开源项目zxing即可,这个项目就是在我们之前的那个核心包的基础上完成的(下载地址)。当然,如果你需要自己定义相关的页面等等也都可以,这个我们到后面再说。这里我们先来看看怎么把GitHub上的开源项目引入到我们的项目中来。

    导入工程我们主要分为如下几个步骤:

    1.创建一个新的Project,命名为QRReader(这一步不是必须的,可以根据你的项目需求来)

    2.下载ZXing项目

    3.在新的Project中创建一个新的Module,在创建的过程中选择Android Library,如下图:

    4.在文件夹中打开我们的项目,找到第三步创建的Library,将第二步下载的ZXing项目中的android和android-core两个文件夹合并到library中,如下图:


    其中android文件夹中主要合并如下几个项目文件:

    合并完成之后再将我们之前下载的核心jar包夜拷贝到我们library的libs文件夹中,然后我们再来看看我们的library:

    5.在我们的项目中引用这个module,然后对项目进行编译。

    6.编译之后项目会报错,这个时候需要我们将library中所有的switch语句改为if...else if ...else if的形式。

    7.完成第6步之后还是会报错,这个时候需要我们将library的清单文件中Application节点的icon属性删除,再编译就没有任何问题了。

    OK,经过上面7个步骤这个开源项目就被我们成功的引入到我们自己的项目中了,在这个开源项目中有一个CaptureActivity,这个Activity专门用来扫描二维码,所以我们只需要在自己的项目中直接来启动这个Activity就可以了,另外,由于默认情况下CaptureActivity是启动项,所以我们要在library的清单文件中删除CaptureActivity作为启动项的配置。

    OK,现在我的项目中有一个按钮,点击这个按钮我就可以扫描二维码,代码如下:

        public void go(View view) {
            startActivity(new Intent(this, CaptureActivity.class));
        }

    OK,至此,一个简陋的二维码扫描就完成了,大家有木有觉得很麻烦啊?麻烦也就这一次,因为我把ZXing当作library使用的时候系统会自动生成一个aar包,有了这个aar包,以后的开发就会变得非常简单了,那么这个aar包在哪里呢?如下图:

    对应的文件夹路径大家自己去找,有了这个aar包之后,如果我再需要使用二维码扫描功能的时候就只需要如下几个简单的步骤:

    1.创建项目

    2.创建Module,在创建Module时选择Import .JAR/.AAR Package,然后选择刚刚的aar包

    3.在我的app中引用这个Module即可。



    最后,我将我自己生成的aar包提供给大家,可以直接在项目中使用,非常方便,不过建议还是大家自己尝试生成一个aar包,整个过程还是非常有意思。


    zxinglibrary-release.aar下载


    以上。


    展开全文
  • 现在大街小巷、各大网站都有二维码的踪迹,不管是IOS、Android、WP都有相关支持的软件。之前我就想了解二维码是如何工作,最近因为工作需要使用相关技术,所以做了初步了解。今天主要是讲解如何使用ZXing库,生成和...

      今天讲一下目前移动领域很常用的技术——二维码。现在大街小巷、各大网站都有二维码的踪迹,不管是IOS、Android、WP都有相关支持的软件。之前我就想了解二维码是如何工作,最近因为工作需要使用相关技术,所以做了初步了解。今天主要是讲解如何使用ZXing库,生成和识别二维码。这篇文章实用性为主,理论性不会讲解太多,有兴趣可以自己查看源码。

    1、ZXing库介绍

      这里简单介绍一下ZXing库。ZXing是一个开放源码的,用Java实现的多种格式的1D/2D条码图像处理库,它包含了联系到其他语言的端口。Zxing可以实现使用手机的内置的摄像头完成条形码的扫描及解码。该项目可实现的条形码编码和解码。目前支持以下格式:UPC-A,UPC-E、EAN-8,EAN-13、39码、93码。ZXing是个很经典的条码/二维码识别的开源类库,以前在功能机上,就有开发者使用J2ME运用ZXing了,不过要支持JSR-234规范(自动对焦)的手机才能发挥其威力。

      下面是ZXing的demo运行,我这里创建了一个二维码,内容是我博客的网址,大伙可以用微信的扫一扫功能,试一下。就可以直接打开我博客。

     (PS:新建的QQ群,有兴趣可以加入一起讨论:Android群:322599434)

    2、ZXing库主要类

      下面给大家介绍一下,ZXing库里面主要的类以及这些类的作用:

    • CaptureActivity。这个是启动Activity 也就是扫描器。
    • CaptureActivityHandler 解码处理类,负责调用另外的线程进行解码。
    • DecodeThread 解码的线程。
    • com.google.zxing.client.android.camera 包,摄像头控制包。
    • ViewfinderView 自定义的View,就是我们看见的拍摄时中间的框框了。

     

    3、使用ZXing生成二维码

      下面针对二维码生成和解析做个简单介绍,至于详细的使用方法,建议大家还是自己看看源码,使用起来很简单,不过这个开源项目的代码,值得好好看看。首先给出二维码生成的方法:

    //Edited by mythou
    //http://www.cnblogs.com/mythou/
      //要转换的地址或字符串,可以是中文
        public void createQRImage(String url)
        {
            try
            {
                //判断URL合法性
                if (url == null || "".equals(url) || url.length() < 1)
                {
                    return;
                }
                Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>();
                hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
                //图像数据转换,使用了矩阵转换
                BitMatrix bitMatrix = new QRCodeWriter().encode(url, BarcodeFormat.QR_CODE, QR_WIDTH, QR_HEIGHT, hints);
                int[] pixels = new int[QR_WIDTH * QR_HEIGHT];
                //下面这里按照二维码的算法,逐个生成二维码的图片,
                //两个for循环是图片横列扫描的结果
                for (int y = 0; y < QR_HEIGHT; y++)
                {
                    for (int x = 0; x < QR_WIDTH; x++)
                    {
                        if (bitMatrix.get(x, y))
                        {
                            pixels[y * QR_WIDTH + x] = 0xff000000;
                        }
                        else
                        {
                            pixels[y * QR_WIDTH + x] = 0xffffffff;
                        }
                    }
                }
                //生成二维码图片的格式,使用ARGB_8888
                Bitmap bitmap = Bitmap.createBitmap(QR_WIDTH, QR_HEIGHT, Bitmap.Config.ARGB_8888);
                bitmap.setPixels(pixels, 0, QR_WIDTH, 0, 0, QR_WIDTH, QR_HEIGHT);
                //显示到一个ImageView上面
                sweepIV.setImageBitmap(bitmap);
            }
            catch (WriterException e)
            {
                e.printStackTrace();
            }
        }

      上面就是二维码生成的方法接口,如果你只是使用者方法,很简单,只要传入一个URL即可,就像我截图里面一样,传入一个合法的网址即可。或者像现在一些移动APP的推广,把APP下载地址转为二维码,只要扫一下就可以下载相应的APP。这个也是目前比较流行的APP的推广方式。

      上面代码做的事情不多,主要是调用ZXing库里面QRCodeWriter().encode的方法对我们传进去的URL进行编码,具体如何编码,这个我这里就不详细说,有兴趣可以看ZXing的源码。文章最后会给出ZXing的源码和例子代码。

    4、扫描二维码获取信息

      扫描获取二维码信息的工作稍微复杂一些,主要是需要编写Camera的使用,这个跟我们一般使用Camera一样,需要使用Surfaceview作为预览,这一部我这里就不说了,这个应该不是太复杂。对于使用过Camera做预览的朋友,应该是挺简单的事情。获取二维码数据的关键处理是在Camera的自动对焦回调函数哪里,调用ZXing的解码接口。

    //Edited by mythou
    //http://www.cnblogs.com/mythou/
      private void restartPreviewAndDecode() {
        if (state == State.SUCCESS) {
          state = State.PREVIEW;
          CameraManager.get().requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
          CameraManager.get().requestAutoFocus(this, R.id.auto_focus);
          activity.drawViewfinder();
        }
      }

      这里稍微多说一句,由于解码需要一定时间,所以ZXing的解码调用,都是使用了Handler作为线程通信机制,解码的工作都是放在独立线程里面使用的,如果你直接在主线程解码,恐怕ANR问题是避免不了。

    //Edited by mythou
    //http://www.cnblogs.com/mythou/
    public void handleMessage(Message message) {
        switch (message.what) {
          case R.id.auto_focus:
            //Log.d(TAG, "Got auto-focus message");
            // When one auto focus pass finishes, start another. This is the closest thing to
            // continuous AF. It does seem to hunt a bit, but I'm not sure what else to do.
            if (state == State.PREVIEW) {
              CameraManager.get().requestAutoFocus(this, R.id.auto_focus);
            }
            break;
          case R.id.restart_preview:
            Log.d(TAG, "Got restart preview message");
            restartPreviewAndDecode();
            break;
          case R.id.decode_succeeded:
        //解码成功,获取到界面的结果和原来的二维码数据
            Log.d(TAG, "Got decode succeeded message");
            state = State.SUCCESS;
            Bundle bundle = message.getData();
            Bitmap barcode = bundle == null ? null :
                (Bitmap) bundle.getParcelable(DecodeThread.BARCODE_BITMAP);
            activity.handleDecode((Result) message.obj, barcode);
            break;
          case R.id.decode_failed:
            // We're decoding as fast as possible, so when one decode fails, start another.
            state = State.PREVIEW;
            CameraManager.get().requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
            break;
          case R.id.return_scan_result:
            Log.d(TAG, "Got return scan result message");
            activity.setResult(Activity.RESULT_OK, (Intent) message.obj);
            activity.finish();
            break;
          case R.id.launch_product_query:
            Log.d(TAG, "Got product query message");
            String url = (String) message.obj;
            Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
            activity.startActivity(intent);
            break;
        }
      }

      上面是解码的线程处理不同状态的时候需要注意的地方,我们这里只看获取图像成功的地方,成功获取图片解码的实在DecodeThread里面实现,DecodeThread里面解码成功后,会把数据序列化,然后保存到Bundle里面,我们可以直接通过Bundle的序列化,获取到图片数据。同时会把解码后的结果保存到MSG里面,然后就可以根据实际情况进行处理,例如上面代码,解码成功后,会调用一个处理函数:

    //Edited by mythou
    //http://www.cnblogs.com/mythou/
      public void handleDecode(final Result obj, Bitmap barcode)
        {
            inactivityTimer.onActivity();
            playBeepSoundAndVibrate();
            AlertDialog.Builder dialog = new AlertDialog.Builder(this);
            if (barcode == null)
            {
                dialog.setIcon(null);
            }
            else
            {
    
                Drawable drawable = new BitmapDrawable(barcode);
                dialog.setIcon(drawable);
            }
            dialog.setTitle("扫描结果");
            dialog.setMessage(obj.getText());
            dialog.setNegativeButton("确定", new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick(DialogInterface dialog, int which)
                {
                    //用默认浏览器打开扫描得到的地址
                    Intent intent = new Intent();
                    intent.setAction("android.intent.action.VIEW");
                    Uri content_url = Uri.parse(obj.getText());
                    intent.setData(content_url);
                    startActivity(intent);
                    finish();
                }
            });
            dialog.setPositiveButton("取消", new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick(DialogInterface dialog, int which)
                {
                    finish();
                }
            });
            dialog.create().show();
        }

      上面就是整个二维码的解码流程,里面因为涉及很多Camera的使用,所以你如果需要使用二维码识别,需要注意一下你的程序需要申请下面的权限,一般的Camera使用以及Camera的自动对焦等。

    //Edited by mythou
    //http://www.cnblogs.com/mythou/
    <uses-permission android:name="android.permission.CAMERA"></uses-permission>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />

    5、结语

      上面就是生成和识别二维码的关键流程和代码,有兴趣的朋友可以自己查看ZXing的源码,里面有很多图像分析的知识可以学习。具体使用也可以参考我下面给出的Demo。二维码对于现在移动开发来说很是很常用的技术,所以有空可以了解一下,说不定什么时候就用上了。另外,ZXing库除了二维码外,其实对于条形码也是支持的,只是我这里没有介绍。有需要的自己去看看源码即可。

    ZXing开源项目Google Code地址:https://code.google.com/p/zxing/

    ZXingDemo下载:ZXingDemo2013-8-25.rar

    转载自:http://www.cnblogs.com/mythou/p/3280023.html 

    展开全文
  • Android二维码功能实现,在程序内嵌入ZXing项目

    万次阅读 多人点赞 2016-10-19 11:31:40
    最近二维码真是越来越火了,随便电视上、网络上、商场里,到处都是二维码。而内嵌二维码扫描功能的软件也越来越多,QQ、微信、UC浏览器等等应用都可以对着二维码扫一扫,感觉我们自己的应用里不加上二维码扫描功能,...

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9526247


    最近二维码真是越来越火了,随便电视上、网络上、商场里,到处都是二维码。而内嵌二维码扫描功能的软件也越来越多,QQ、微信、UC浏览器等等应用都可以对着二维码扫一扫,感觉我们自己的应用里不加上二维码扫描功能,都跟不上时代潮流了。所以今天我就将带着大家一起,在我们自己的程序里加入二维码扫描的功能。


    不过,二维码功能如果真要做起来还是非常复杂的,从零开始实现不太现实,比较好的做法就是借助现有的开源项目。目前在二维码这一领域名气最大的开源项目就是ZXing了(Zebra Crossing),它提供了多个平台的二维码扫描解决方案,开源项目地址是 https://code.google.com/p/zxing/ 。


    虽说网上已经有现成的开源项目了,不过关于ZXing的文档和教程好像还比较少,因此还是有不少朋友并不知道在项目中该如何引入ZXing的,这里我就带着大家一步步地实现,相信每个人在看完本篇文章后都可以在自己的项目中实现二维码扫描功能。


    首先,我们需要下载ZXing项目所依赖的Jar包的源码。


    下载地址是 http://repo1.maven.org/maven2/com/google/zxing/core/2.2/core-2.2-sources.jar 。


    然后我们再来下载ZXing项目,下载地址是 https://zxing.googlecode.com/files/ZXing-2.2.zip


    建议使用迅雷下载,因为Google Code和Maven的访问在国内不稳定,经常出现断联的情况,使用迅雷可以保证文件的完整性。


    另外,经过我的测试,在ZXing项目中直接导入core-2.2的Jar包是无法正常运行的,所以我们只能通过将core-2.2的源码加入到ZXing项目中来实现。下载好以上两个文件后,先解压core-2.2-sources.jar文件,解压之后的目录结构如下图所示:




    然后解压ZXing-2.2这个压缩包,里面可以看到各种平台下的ZXing项目源码,我们进入到android文件夹的src目录下,将core-2.2-sources中的源码拷贝进来。拷贝之后android文件夹下的目录结构如下图所示:




    这样准备工作已经完成了,现在我们新建一个Android项目ScannerTest,项目使用Android 4.0的API。


    然后将上图中src目录下的所有文件全部复制,粘贴到我们ScannerTest项目的src目录下,完成后目录结构如下图所示:




    拷贝完了代码,现在该拷贝资源了,展开ZXing项目android文件夹下的res目录,将drawable文件夹、layout文件夹、menu文件夹、raw文件夹、values文件夹以及xml文件夹中的内容都拷贝到ScannerTest项目的res目录下,注意有冲突的部分要小心解决,比如两个values文件夹中都有string.xml文件,要将它们的内容进行合并,不能只是简单地覆盖。


    然后我们还需要将AndroidManifest中的内容进行合并,注意ZXing Android项目下的AndroidManifest在声明Activity时用的都是简写,而现在由于项目包名变了,再使用简写会出现找不到活动的情况,因此所有的简写都要改成完整类名,例如.CaptureActivity要改成com.google.zxing.client.android.CaptureActivity。另外ZXing Android项目下的主活动是CaptureActivity,这里我们需要将主活动的声明删除掉,因为ScannerTest项目中主活动是MainActivity。合并后的AndroidManifest中的代码如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.scannertest"
        android:versionCode="1"
        android:versionName="1.0" >
    
        <uses-permission android:name="android.permission.CAMERA" />
        <uses-permission android:name="android.permission.INTERNET" />
        <uses-permission android:name="android.permission.VIBRATE" />
        <uses-permission android:name="android.permission.FLASHLIGHT" />
        <uses-permission android:name="android.permission.READ_CONTACTS" />
        <uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS" />
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
        <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    
        <uses-sdk
            android:minSdkVersion="14"
            android:targetSdkVersion="17" />
    
        <uses-feature
            android:name="android.hardware.camera"
            android:required="false" />
        <uses-feature
            android:name="android.hardware.camera.front"
            android:required="false" />
        <uses-feature
            android:name="android.hardware.camera.autofocus"
            android:required="false" />
        <uses-feature
            android:name="android.hardware.camera.flash"
            android:required="false" />
        <uses-feature android:name="android.hardware.screen.landscape" />
        <uses-feature
            android:name="android.hardware.wifi"
            android:required="false" />
        <uses-feature android:name="android.hardware.touchscreen" />
    
        <supports-screens
            android:anyDensity="true"
            android:largeScreens="true"
            android:normalScreens="true"
            android:smallScreens="true"
            android:xlargeScreens="true" />
    
        <application
            android:allowBackup="true"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >
            <activity
                android:name="com.example.scannertest.MainActivity"
                android:label="@string/app_name" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
            <activity
                android:name="com.google.zxing.client.android.CaptureActivity"
                android:clearTaskOnLaunch="true"
                android:configChanges="orientation|keyboardHidden"
                android:screenOrientation="landscape"
                android:stateNotNeeded="true"
                android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
                android:windowSoftInputMode="stateAlwaysHidden" >
                <intent-filter>
                    <action android:name="com.google.zxing.client.android.SCAN" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
                <intent-filter>
                    <action android:name="android.intent.action.VIEW" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <category android:name="android.intent.category.BROWSABLE" />
                    <data
                        android:host="zxing.appspot.com"
                        android:path="/scan"
                        android:scheme="http" />
                </intent-filter>
                <intent-filter>
                    <action android:name="android.intent.action.VIEW" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <category android:name="android.intent.category.BROWSABLE" />
                    <data
                        android:host="www.google.com"
                        android:path="/m/products/scan"
                        android:scheme="http" />
                </intent-filter>
                <intent-filter>
                    <action android:name="android.intent.action.VIEW" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <category android:name="android.intent.category.BROWSABLE" />
                    <data
                        android:host="www.google.co.uk"
                        android:path="/m/products/scan"
                        android:scheme="http" />
                </intent-filter>
                <intent-filter>
                    <action android:name="android.intent.action.VIEW" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <category android:name="android.intent.category.BROWSABLE" />
                    <data
                        android:host="scan"
                        android:path="/"
                        android:scheme="zxing" />
                </intent-filter>
            </activity>
            <activity
                android:name="com.google.zxing.client.android.PreferencesActivity"
                android:label="@string/preferences_name"
                android:stateNotNeeded="true" >
            </activity>
            <activity
                android:name="com.google.zxing.client.android.encode.EncodeActivity"
                android:stateNotNeeded="true" >
                <intent-filter>
                    <action android:name="com.google.zxing.client.android.ENCODE" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
                <intent-filter>
                    <action android:name="android.intent.action.SEND" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <data android:mimeType="text/x-vcard" />
                </intent-filter>
                <intent-filter>
                    <action android:name="android.intent.action.SEND" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <data android:mimeType="text/plain" />
                </intent-filter>
            </activity>
            <activity
                android:name="com.google.zxing.client.android.book.SearchBookContentsActivity"
                android:configChanges="orientation|keyboardHidden"
                android:label="@string/sbc_name"
                android:screenOrientation="landscape"
                android:stateNotNeeded="true" >
                <intent-filter>
                    <action android:name="com.google.zxing.client.android.SEARCH_BOOK_CONTENTS" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
            </activity>
            <activity
                android:name="com.google.zxing.client.android.share.ShareActivity"
                android:screenOrientation="user"
                android:stateNotNeeded="true"
                android:theme="@android:style/Theme.Light" >
                <intent-filter>
                    <action android:name="com.google.zxing.client.android.SHARE" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
            </activity>
            <activity
                android:name="com.google.zxing.client.android.history.HistoryActivity"
                android:label="@string/history_title"
                android:stateNotNeeded="true" >
                <intent-filter>
                    <action android:name="android.intent.action.VIEW" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
            </activity>
            <activity
                android:name="com.google.zxing.client.android.share.BookmarkPickerActivity"
                android:label="@string/bookmark_picker_name"
                android:stateNotNeeded="true" >
                <intent-filter>
                    <action android:name="android.intent.action.PICK" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
            </activity>
            <activity
                android:name="com.google.zxing.client.android.share.AppPickerActivity"
                android:configChanges="orientation"
                android:label="@string/app_picker_name"
                android:stateNotNeeded="true" >
                <intent-filter>
                    <action android:name="android.intent.action.PICK" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
            </activity>
            <activity
                android:name="com.google.zxing.client.android.HelpActivity"
                android:screenOrientation="user" >
                <intent-filter>
                    <action android:name="android.intent.action.VIEW" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>

    完成到这一步之后,你会发现项目中还是有很多的错误。不用担心,剩下的错误全部都是由于找不到R文件所造成的。这是因为ZXing项目中所引用的R文件都是com.google.zxing.client.android包下的R,而现在我们拷贝到ScannerTest项目之后,应该引用com.example.scannertest包下的R文件。我们需要将有错误的文件一个个地修改过来,虽然工作量不少,但都是傻瓜式操作,只要大家有耐心,就一定可以完成。


    现在ScannerTest项目中应该已经没有任何错误了,然后我们还需要对ZXing的代码进行稍微的定制。


    打开CaptureActivity,这个类就是用于扫描二维码的最主要的一个类,其中有一个handleDecode()方法,当二维码扫描完成之后会把结果回调到这个方法中,我们现在不想使用默认的处理方式,于是修改handleDecode()中的代码,如下所示:

    public void handleDecode(Result rawResult, Bitmap barcode, float scaleFactor) {
    	String result = rawResult.getText();
    	if (!TextUtils.isEmpty(result)) {
    		Intent intent = new Intent();
    		intent.putExtra("scan_result", rawResult.getText());
    		setResult(RESULT_OK, intent);
    	} else {
    		setResult(RESULT_CANCELED);
    	}
    	finish();
    }
    这里我们将扫描出来的结果借助Intent进行返回。


    然后打开或新建activity_main.xml文件做为ScannerTest项目的主布局,在其中添加如下代码:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >
    
        <Button
            android:id="@+id/scan_button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="扫一扫" />
    
        <TextView
            android:id="@+id/scan_result"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    
    </LinearLayout>
    这个布局文件很简单,一个按钮用于开启二维码扫描功能,一个TextView用于显示扫描结果。


    最后打开或新建MainActivity做为ScannerTest项目的主Activity,代码如下所示:

    public class MainActivity extends Activity {
    
    	public static final int SCAN_CODE = 1;
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		Button button = (Button) findViewById(R.id.scan_button);
    		button.setOnClickListener(new OnClickListener() {
    			@Override
    			public void onClick(View v) {
    				Intent intent = new Intent(MainActivity.this, CaptureActivity.class);
    				startActivityForResult(intent, SCAN_CODE);
    			}
    		});
    	}
    
    	@Override
    	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    		switch (requestCode) {
    		case SCAN_CODE:
    			TextView scanResult = (TextView) findViewById(R.id.scan_result);
    			if (resultCode == RESULT_OK) {
    				String result = data.getStringExtra("scan_result");
    				scanResult.setText(result);
    			} else if (resultCode == RESULT_CANCELED) {
    				scanResult.setText("扫描出错");
    			}
    			break;
    		default:
    			break;
    		}
    	}
    
    }
    这个类也很简单,点击按钮时,我们通过startActivityForResult()方法启动CaptureActivity,开始执行二维码扫描,扫描的结果将回调到onActivityResult()方法中,然后在这个方法中取出扫描的结果,并展示在TextView上。


    这样我们所有的编码工作就已经完成了,可以尝试运行一下了。首先看到程序的主界面如下图所示:



    点击扫一扫后可以进行二维码扫描,见下图:




    扫描完成后会将结果返回到主界面,如下图所示:



    不知道大家有没有成功呢?这里我精心给大家准备了一张二维码图片,看看有多少朋友能够成功扫出来。 ^_^




    好了,今天的讲解到此结束,有疑问的朋友请在下面留言。


    源码下载,请点击这里


    关注我的技术公众号,每天都有优质技术文章推送。关注我的娱乐公众号,工作、学习累了的时候放松一下自己。

    微信扫一扫下方二维码即可关注:

            

    展开全文
  • Android生成自定义二维码

    万次阅读 多人点赞 2019-11-13 10:20:38
    前面说过两种二维码扫描方式,现在说二维码生成,使用Google开源库Zxing,项目地址:https://github.com/ZBar/ZBar,我们只需要里面的jar包:core:x.x.x.jar,建议用core:3.3.0。 1.引入jar包 app build....
  • 二维码的应用非常广泛,用途也很多,网上也有很多实用的精简包可以引入,本篇主要记录了不用引入新的module,自己集成生成二维码功能的过程。 一、引入依赖 首先在libs文件目录下放进jar包zxing.jar,然后引入...
  • Android二维码功能实现

    千次阅读 2013-08-15 20:47:03
    最近二维码真是越来越火了,随便电视上、网络上、商场里,到处都是二维码。而内嵌二维码扫描功能的软件也越来越多,QQ、微信、UC浏览器等等应用都可以对着二维码扫一扫,感觉我们自己的应用里不加上二维码扫描功能,...
  • 本文采用ZXing三方框架实现扫描二维码+生成二维码的功能,具体实现参考转载博文
  • 这是一个只有一个二维码扫描界面的demo,方便之后集成使用。原文地址 http://blog.csdn.net/qq_25806863/article/details/72139582CSDN下载地址 http://download.csdn.net/detail/qq_25806863/9842656demo的GitHub...
  • Android 二维码扫描,识别率非常高

    万次阅读 热门讨论 2017-05-23 19:47:02
    这是我在GitHub看到的一个开源二维码码扫描例子,识别率非常高。支出二维码,条形码,以及图片识别。 效果图与示例 apk   点击下载 ZXingDemo.apk或扫描下面的二维码安装 点击下载 ZBarDemo.apk或...
  • android端快速生成二维码和进行二维码扫描

    万次阅读 热门讨论 2016-06-08 23:14:14
    android端快速即成微信扫一扫功能,并且根据自己的需求快速生成二维码
  • Android使用zxing生成二维码

    万次阅读 多人点赞 2020-09-01 15:31:25
    第一步:定义二维码的长宽高及图片控件 第二步:实例化QRCodeWriter后利用for循环将二维码画出来,然后用图片控件加载图片。 源码如下: 布局文件: &lt;Button android:id="@+id/mybutton" ...
  • 我想实现这个功能,扫描二维码,然后android上就会提示是否下载该apk?如何实现?
  • Android apk包生成二维码给他人体验

    千次阅读 2017-08-29 16:38:46
    Android apk包生成二维码给他人体验
  • Zxing本身默认的扫图区域最大只有 360*480 需要拉开很远的距离才能将整个二维码扫描到 因此需要我们自己调整取图大小 在CameraManager.java这个类中进行调整 默认的大小是 以下这4个参数  // private static ...
  • 本人在做公司项目,使用zxing以后发现扫描公司开发产品上的二维码比较慢,想优化扫描速度不知从何入手 , 扫描时候的聚焦速度比较慢,有没有懂路的人指点一下
  • Zxing blogs:  android入门:zxing学习笔记 - 臭蛋 - 博客园 基于google Zxing实现二维码、条形码扫描,仿微信 - 夏安明的专栏 - CSDN.NET ...条码扫描二维码扫描——ZXing android 源码简化 - DotDot - 博客园 二维
  • 我首先做了一个二维码名片,把信息输入进去以后生成了一个二维码,现在需要通过扫描这个二维码获取里面的信息中的电话号码信息,然后是一个发短信的过程。现在的问题就是如何扫描获得这个电话号码?
  • 要做一个Android app上有收款功能收款功能是生成一个二维码,本人以前没有接触过二维码,请问如何生成收款二维码,付款也是同过这个app实现。
  • android 用zxing直接识别本地图片中的二维码的时候,部分二维码需要图片非常大才能识别,正常大小完全识别不出来,请问有人碰到过吗,有解决方案吗.
1 2 3 4 5 ... 20
收藏数 32,423
精华内容 12,969
关键字:

android二维码