精华内容
参与话题
问答
  • Android二维码识别与生成

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

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

            首先,什么是二维码?

            “二维条码/二维码(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二维码扫描Demo 程序源码

    万次下载 热门讨论 2013-08-08 08:24:25
    此为示例代码,详细讲解请参考: http://blog.csdn.net/guolin_blog/article/details/9526247
  • Android 实现生成二维码

    千次阅读 2018-12-06 11:34:24
    注入开源库 implementation '...   public class QRCodeUtil { private static int IMAGE_HALFWIDTH = 50;//宽度值,影响中间图片大小 ... * 生成二维码,默认大小为500*500 * * @param t...

    注入开源库  implementation 'com.google.zxing:core:3.3.0'

     

    public class QRCodeUtil {
    
        private static int IMAGE_HALFWIDTH = 50;//宽度值,影响中间图片大小
    
        /**
         * 生成二维码,默认大小为500*500
         *
         * @param text 需要生成二维码的文字、网址等
         * @return bitmap
         */
        public static Bitmap createQRCode(String text) {
            return createQRCode(text, 150);
        }
        /**
         * 生成二维码
         *
         * @param text 需要生成二维码的文字、网址等
         * @param size 需要生成二维码的大小()
         * @return bitmap
         */
        public static Bitmap createQRCode(String text, int size) {
            try {
                Hashtable<EncodeHintType, Object> hints = new Hashtable<>();
                hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
                hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
                hints.put(EncodeHintType.MARGIN, 1);
                BitMatrix bitMatrix = new QRCodeWriter().encode(text,
                        BarcodeFormat.QR_CODE, size, size, hints);
                int[] pixels = new int[size * size];
                for (int y = 0; y < size; y++) {
                    for (int x = 0; x < size; x++) {
                        if (bitMatrix.get(x, y)) {
                            pixels[y * size + x] = 0xff000000;
                        } else {
                            pixels[y * size + x] = 0xffffffff;
                        }
                    }
                }
                Bitmap bitmap = Bitmap.createBitmap(size, size,
                        Bitmap.Config.ARGB_8888);
                bitmap.setPixels(pixels, 0, size, 0, 0, size, size);
                return bitmap;
            } catch (WriterException e) {
                e.printStackTrace();
                return null;
            }
        }
        /**
         * 生成带logo的二维码,默认二维码的大小为500,logo为二维码的1/5
         *
         * @param text    需要生成二维码的文字、网址等
         * @param mBitmap logo文件
         * @return bitmap
         */
        public static Bitmap createQRCodeWithLogo(String text, Bitmap mBitmap) {
            return createQRCodeWithLogo(text, 500, mBitmap);
        }
    
        /**
         * 生成带logo的二维码,logo默认为二维码的1/5
         *
         * @param text    需要生成二维码的文字、网址等
         * @param size    需要生成二维码的大小()
         * @param mBitmap logo文件
         * @return bitmap
         */
        public static Bitmap createQRCodeWithLogo(String text, int size, Bitmap mBitmap) {
            try {
                IMAGE_HALFWIDTH = size / 10;
                Hashtable<EncodeHintType, Object> hints = new Hashtable<>();
                hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
                /*
                 * 设置容错级别,默认为ErrorCorrectionLevel.L
                 * 因为中间加入logo所以建议你把容错级别调至H,否则可能会出现识别不了
                 */
                hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
                //设置空白边距的宽度
                hints.put(EncodeHintType.MARGIN, 1); //default is 4
                // 图像数据转换,使用了矩阵转换
                BitMatrix bitMatrix = new QRCodeWriter().encode(text,
                        BarcodeFormat.QR_CODE, size, size, hints);
    
                int width = bitMatrix.getWidth();//矩阵高度
                int height = bitMatrix.getHeight();//矩阵宽度
                int halfW = width / 2;
                int halfH = height / 2;
    
                Matrix m = new Matrix();
                float sx = (float) 2 * IMAGE_HALFWIDTH / mBitmap.getWidth();
                float sy = (float) 2 * IMAGE_HALFWIDTH
                        / mBitmap.getHeight();
                m.setScale(sx, sy);
                //设置缩放信息
                //将logo图片按martix设置的信息缩放
                mBitmap = Bitmap.createBitmap(mBitmap, 0, 0,
                        mBitmap.getWidth(), mBitmap.getHeight(), m, false);
    
                int[] pixels = new int[size * size];
                for (int y = 0; y < size; y++) {
                    for (int x = 0; x < size; x++) {
                        if (x > halfW - IMAGE_HALFWIDTH && x < halfW + IMAGE_HALFWIDTH
                                && y > halfH - IMAGE_HALFWIDTH
                                && y < halfH + IMAGE_HALFWIDTH) {
                            //该位置用于存放图片信息
                            //记录图片每个像素信息
                            pixels[y * width + x] = mBitmap.getPixel(x - halfW
                                    + IMAGE_HALFWIDTH, y - halfH + IMAGE_HALFWIDTH);
                        } else {
                            if (bitMatrix.get(x, y)) {
                                pixels[y * size + x] = 0xff000000;
                            } else {
                                pixels[y * size + x] = 0xffffffff;
                            }
                        }
                    }
                }
                Bitmap bitmap = Bitmap.createBitmap(size, size,
                        Bitmap.Config.ARGB_8888);
                bitmap.setPixels(pixels, 0, size, 0, 0, size, size);
                return bitmap;
            } catch (WriterException e) {
                e.printStackTrace();
                return null;
            }
        }
    
    
        /**
         * 生成二维码Bitmap  此方法与上面的createQRCodeWithLogo方法效果一样(设置Bitmap两种写方法)
         *
         * @param context
         * @param data     文本内容
         * @param logoBm   二维码中心的Logo图标(可以为null)
         * @return 合成后的bitmap
         */
        public static Bitmap createQRImage(Context context, String data, Bitmap logoBm) {
            try {
                if (data == null || "".equals(data)) {
                    return null;
                }
                int widthPix = ((Activity) context).getWindowManager().getDefaultDisplay()
                        .getWidth();
                widthPix = widthPix / 5 * 3;
                int heightPix = widthPix;
    
                //配置参数
                Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();
                hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
                //容错级别
                hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
                //设置空白边距的宽度
                hints.put(EncodeHintType.MARGIN, 1); //default is 4
    
                // 图像数据转换,使用了矩阵转换
                BitMatrix bitMatrix = new QRCodeWriter().encode(data, BarcodeFormat.QR_CODE, widthPix, heightPix, hints);
                int[] pixels = new int[widthPix * heightPix];
                // 下面这里按照二维码的算法,逐个生成二维码的图片,
                // 两个for循环是图片横列扫描的结果
                for (int y = 0; y < heightPix; y++) {
                    for (int x = 0; x < widthPix; x++) {
                        if (bitMatrix.get(x, y)) {
                            pixels[y * widthPix + x] = 0xff000000;
                        } else {
                            pixels[y * widthPix + x] = 0xffffffff;
                        }
                    }
                }
                // 生成二维码图片的格式,使用ARGB_8888
                Bitmap bitmap = Bitmap.createBitmap(widthPix, heightPix, Bitmap.Config.ARGB_8888);
                bitmap.setPixels(pixels, 0, widthPix, 0, 0, widthPix, heightPix);
    
                if (logoBm != null) {
                    bitmap = addLogo(bitmap, logoBm);
                }
    
                return bitmap;
                //必须使用compress方法将bitmap保存到文件中再进行读取。直接返回的bitmap是没有任何压缩的,内存消耗巨大!
                //return bitmap != null && bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(filePath));
            } catch (Exception 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.restore();
            } catch (Exception e) {
                bitmap = null;
                e.getStackTrace();
            }
    
            return bitmap;
        }
    }

     

    展开全文
  • android二维码生成示例

    2017-08-28 15:38:33
    最简单android二维码生成示例
  • 拍照或从相册选择图片是我们日常开发中经常使用到的,可以说是必须掌握的东西。

        拍照或从相册选择图片是我们日常开发中经常使用到的,可以说是必须掌握的东西。上一篇我介绍了如何生成自定义二维码《Android生成自定义二维码》,其中logo和代替黑色色块的图片都是写死的,所以现在我们就来实现拍照或者从相册选取图片这个功能。


    先看效果图:

           

           


    拍照

    • 启动相机程序
      拍照可以直接启动系统的相机程序,代码如下
    Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
    intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
    startActivityForResult(intent, TAKE_PHOTO);
    

        这里我们利用一个隐式Intent来启动相机程序,其中action类型:android.media.action.IMAGE_CAPTURE 表示启动相机应用并请求一张图片。创建了Intent对象,还需指定图片的保存路径,调用Intent的putExtra()方法并传入保存路径即可,最后调用startActivityForResult启动活动,重写onActivityResult()方法就能得到返回值。

    • 指定保存路径

        上面的intent中指定了保存路径,也就是代码中的imageUri。首先需要创建一个File对象用来存放图片,并使用getExternalCacheDir()方法将图片放入SD卡当前应用包名下的缓存目录中。
        接着需要将File对象转换成Uri对象,需要进行版本判断,7.0以下直接调用Uri的fromFile方法,得到的是图片本地真实路径。7.0以上直接使用真实路径会被认为不安全,会抛出异常,所以需要使用特殊的内容提供器FileProvider,它是ContentProvider的一个子类,作用便是将受限的Uri转换为可共享的Uri。
        既然使用内容提供器,当然需要进行注册了,AndroidManifest.xml中加入:

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.example.xch.generateqrcode.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
    

        meta-data标签中的内容是用来添加一个共享目录的,引用了一个resource资源,所以需要在 res/xml 目录下新建这个 xml 文件,用于存放应用需要共享的目录文件。

    <?xml version="1.0" encoding="utf-8"?>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <!--path设为空值表示将整个sd卡共享-->
        <external-path
            name="my_images" path="" />
    </paths>
    

        接下来在代码中调用FileProvider的getUriForFile()方法生成Uri对象,需要传入三个参数,第一个参数是上下文,第二个参数便是 Manifest 文件中注册 FileProvider 时设置的 authorities 属性值,第三个参数为要共享的文件,也就是之前创建的File对象。

        完整代码

    /**
     * 拍照
     */
    private void takePhoto() {
        // 创建File对象,用于存储拍照后的图片
        File outputImage = new File(getExternalCacheDir(), "output_image.jpg");
        try {
            if (outputImage.exists()) {
                outputImage.delete();
            }
            outputImage.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (Build.VERSION.SDK_INT < 24) {
            imageUri = Uri.fromFile(outputImage);
        } else {
            imageUri = FileProvider.getUriForFile(MainActivity.this, "com.example.xch.generateqrcode.fileprovider", outputImage);
        }
        // 启动相机程序
        Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
        startActivityForResult(intent, TAKE_PHOTO);
    }
    

        获取拍照结果:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case TAKE_PHOTO:
                if (resultCode == RESULT_OK) {
                    try {
                        // 读取拍照结果
                        logoBitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                break;
            default:
                break;
        }
    }
    

        读取拍照结果利用ContentResolver的openInputStream()方法,并传入刚才图片的保存路径即可获取图片字节流,再利用BitmapFactory的decodeStream()方法转为Bitmap格式,如果担心OOM,可先进行压缩。

        最后别忘了在AndroidManifest.xml中加入权限

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    

    从相册选择图片

    • 打开相册

    同样用Intent,action类型为android.intent.action.GET_CONTENT ,并给intent设置type为图片,如下:

    Intent intent = new Intent("android.intent.action.GET_CONTENT");
    intent.setType("image/*");
    startActivityForResult(intent, CHOOSE_PHOTO);
    
    • 获取结果

    在回调onActivityResult即可对返回结果进行处理

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case CHOOSE_PHOTO:
                if (resultCode == RESULT_OK) {
                    // 判断手机系统版本号
                    if (Build.VERSION.SDK_INT >= 19) {
                        // 4.4及以上系统使用这个方法处理图片
                        handleImageOnKitKat(data);
                    } else {
                        // 4.4以下系统使用这个方法处理图片
                        handleImageBeforeKitKat(data);
                    }
                }
                break;
            default:
                break;
        }
    }
    

        由于Android 4.4开始,不再返回图片真实的Uri,而是一个封装过的Uri,因此需要分别进行处理。Android 4.4之前直接调用getImagePath()方法通过Uri和selection就可获取真实路径,如下

    /**
     * 4.4版本以前,直接获取真实路径
     * @param data
     */
    private void handleImageBeforeKitKat(Intent data) {
        Uri uri = data.getData();
        String imagePath = getImagePath(uri, null);
        //显示图片
        displayImage(imagePath);
    }
    
    private String getImagePath(Uri uri, String selection) {
        String path = null;
        // 通过Uri和selection来获取真实的图片路径
        Cursor cursor = getContentResolver().query(uri, null, selection, null, null);
        if (cursor != null) {
            if (cursor.moveToFirst()) {
                path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
            }
            cursor.close();
        }
        return path;
    }
    

        Android 4.4之后,需要解析封装过的Uri,如果Uri是document类型,则通过document id处理,如果是content类型的Uri,则使用普通方式处理,如果是file类型的Uri,直接获取图片路径即可。获取到路径后即可显示,如下

    private void handleImageOnKitKat(Intent data) {
        String imagePath = null;
        Uri uri = data.getData();
        if (DocumentsContract.isDocumentUri(this, uri)) {
            // 如果是document类型的Uri,则通过document id处理
            String docId = DocumentsContract.getDocumentId(uri);
            if ("com.android.providers.media.documents".equals(uri.getAuthority())) {
                String id = docId.split(":")[1]; // 解析出数字格式的id
                String selection = MediaStore.Images.Media._ID + "=" + id;
                imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
            } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) {
                Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId));
                imagePath = getImagePath(contentUri, null);
            }
        } else if ("content".equalsIgnoreCase(uri.getScheme())) {
            // 如果是content类型的Uri,则使用普通方式处理
            imagePath = getImagePath(uri, null);
        } else if ("file".equalsIgnoreCase(uri.getScheme())) {
            // 如果是file类型的Uri,直接获取图片路径即可
            imagePath = uri.getPath();
        }
        displayImage(imagePath); // 根据图片路径显示图片
    }
    

        显示图片

    /**
     * 显示图片
     * @param imagePath 图片路径
     */
    private void displayImage(String imagePath) {
        if (imagePath != null) {
           logoBitmap = BitmapFactory.decodeFile(imagePath);
           // 显示图片
           picture_logo.setImageBitmap(logoBitmap);
        } else {
            Toast.makeText(this, "获取图片失败", Toast.LENGTH_SHORT).show();
        }
    }
    

    动态权限申请

       由于涉及了敏感权限 WRITE_EXTERNAL_STORAGE ,所以需要进行权限的动态申请,如下

    //动态权限申请
    if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
    } else {
        //打开相册
        openAlbum();
    }
    
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case 1:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    //打开相册
                    openAlbum();
                } else {
                    Toast.makeText(this, "你拒绝了权限申请,可能无法打开相册哟", Toast.LENGTH_SHORT).show();
                }
                break;
            default:
        }
    }
    

        上面的代码就是动态申请权限的流程,首先判断用户是不是已经给我们权限授权了,使用ContextCompat.checkSelfPermission()方法,第一个参数是Context,第二个参数是具体的权限名称,如果等于PackageManager.PERMISSION_GRANTED表明已授权,不等于就是没有授权。
        如果已授权就直接做后面的操作,如果没有授权,需要调用ActivityCompat.requestPermissions()方法申请授权,第一个参数是当前Activity实例,第二个参数是权限数组,第三个是请求码。
        用户的选择将会回调到onRequestPermissionsResult()方法中,授权结果封装在grantResults参数中,如果被授权,则打开相册,否则提示用户未打开权限无法使用。



    源码已更新至GitHub,地址:https://github.com/yangxch/GenerateQRCode


    更多技术干货,欢迎关注我的公众号:ChaoYoung
      

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

    万次阅读 多人点赞 2018-08-29 14:37:59
    前面说过两种二维码扫描方式,现在说二维码生成,使用Google开源库Zxing,项目地址:https://github.com/ZBar/ZBar,我们只需要里面的jar包:core:x.x.x.jar,建议用core:3.3.0。 1.引入jar包 app build....

    本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 ChaoYoung 即可关注。

        前面说过两种二维码扫描方式,现在说二维码生成,使用Google开源库Zxing,Zxing的项目地址:https://github.com/ZBar/ZBar,我们只需要里面的jar包:core:x.x.x.jar,建议用core:3.3.0。

    划重点:

    • 一般二维码
    • 彩色二维码
    • 带logo二维码或带logo的彩色二维码
    • 黑色色块用图片代替的二维码

    1.引入jar包
    • app build.gradle中加入依赖
    dependencies {
        ...
        implementation 'com.google.zxing:core:3.3.0'
    }
    
    • 如果依赖同步失败,下载jar包导入libs下,并在app build.gradle中加入如下代码并Sync Now
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    

    2.How ?

      先看简单的二维码生成

    /**
     * 生成简单二维码
     *
     * @param content                字符串内容
     * @param width                  二维码宽度
     * @param height                 二维码高度
     * @param character_set          编码方式(一般使用UTF-8)
     * @param error_correction_level 容错率 L:7% M:15% Q:25% H:35%
     * @param margin                 空白边距(二维码与边框的空白区域)
     * @param color_black            黑色色块
     * @param color_white            白色色块
     * @return BitMap
     */
    public static Bitmap createQRCodeBitmap(String content, int width,int height,
                                            String character_set,String error_correction_level,
                                            String margin,int color_black, int color_white) {
        // 字符串内容判空
        if (TextUtils.isEmpty(content)) {
            return null;
        }
        // 宽和高>=0
        if (width < 0 || height < 0) {
            return null;
        }
        try {
            /** 1.设置二维码相关配置 */
            Hashtable<EncodeHintType, String> hints = new Hashtable<>();
            // 字符转码格式设置
            if (!TextUtils.isEmpty(character_set)) {
                hints.put(EncodeHintType.CHARACTER_SET, character_set);
            }
            // 容错率设置
            if (!TextUtils.isEmpty(error_correction_level)) {
                    hints.put(EncodeHintType.ERROR_CORRECTION, error_correction_level);
                }
            // 空白边距设置
            if (!TextUtils.isEmpty(margin)) {
                hints.put(EncodeHintType.MARGIN, margin);
            }
            /** 2.将配置参数传入到QRCodeWriter的encode方法生成BitMatrix(位矩阵)对象 */
            BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
    
            /** 3.创建像素数组,并根据BitMatrix(位矩阵)对象为数组元素赋颜色值 */
            int[] pixels = new int[width * height];
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    //bitMatrix.get(x,y)方法返回true是黑色色块,false是白色色块
                    if (bitMatrix.get(x, y)) {
                        pixels[y * width + x] = color_black;//黑色色块像素设置
                    } else {
                        pixels[y * width + x] = color_white;// 白色色块像素设置
                    }
                }
            }
            /** 4.创建Bitmap对象,根据像素数组设置Bitmap每个像素点的颜色值,并返回Bitmap对象 */
            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;
        }
     }
    

    主要步骤:

    • 设置二维码相关配置,包括传入的二维码长宽、容错率和空白边距大小。
    • 将配置参数传入到QRCodeWriter的encode方法并生成BitMatrix(位矩阵)对象。
    • 位矩阵对象中bitMatrix.get(x, y)方法可判断是黑色色块还是白色色块,根据不同色块给数组元素赋我们传入的颜色值
    • 根据像素数组每个像素点的颜色值创建Bitmap对象并返回,即二维码

    主要参数介绍:

    • character_set
      字符集/字符转码格式,通常使用UTF-8,格式不对可能导致乱码。传null时,默认使用 “ISO-8859-1”

    • error_correction_level
      容错率,也就是纠错水平,二维码破损一部分也能扫码就归功于容错率,容错率可分为L、 M、 Q、 H四个等级,其分别占比为:L:7% M:15% Q:25% H:35%。传null时,默认使用 “L”
      当然容错率越高,二维码能存储的内容也随之变小。

    • margin
      二维码和边框的空白区域宽度

    • color_black、color_white
      黑色色块和白素色块,我们常见的二维码一般是黑白两色的,也就是这两个色块,可以自己传入两个颜色,so,彩色二维码不就实现了。

    调用生成:

    createQRCodeBitmap(content, 800, 800,"UTF-8","H", "1", Color.BLACK, Color.WHITE);
    

              

     黑色色块用绿色(Color.GREEN)、蓝色(Color.BLUE)、黄色(Color.YELLOW)…:

    createQRCodeBitmap(content, 800, 800,"UTF-8","H", "1", Color.GREEN, Color.WHITE);
    ...
    

      


    3.添加logo

        二维码生成当然少不了添加logo了,添加logo就是图片合成,将logo图片以一定比例合成在原二维码图片上。
        直接看代码

    /**
     * 向二维码中间添加logo图片(图片合成)
     *
     * @param srcBitmap 原图片(生成的简单二维码图片)
     * @param logoBitmap logo图片
     * @param logoPercent 百分比 (用于调整logo图片在原图片中的显示大小, 取值范围[0,1] )                    
     * @return
     */
     private static Bitmap addLogo(Bitmap srcBitmap,  Bitmap logoBitmap, float logoPercent){
        if(srcBitmap == null){
            return null;
        }
        if(logoBitmap == null){
            return srcBitmap;
        }
        //传值不合法时使用0.2F
        if(logoPercent < 0F || logoPercent > 1F){
            logoPercent = 0.2F;
        }
    
        /** 1. 获取原图片和Logo图片各自的宽、高值 */
        int srcWidth = srcBitmap.getWidth();
        int srcHeight = srcBitmap.getHeight();
        int logoWidth = logoBitmap.getWidth();
        int logoHeight = logoBitmap.getHeight();
    
        /** 2. 计算画布缩放的宽高比 */
        float scaleWidth = srcWidth * logoPercent / logoWidth;
        float scaleHeight = srcHeight * logoPercent / logoHeight;
    
        /** 3. 使用Canvas绘制,合成图片 */
        Bitmap bitmap = Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        canvas.drawBitmap(srcBitmap, 0, 0, null);
        canvas.scale(scaleWidth, scaleHeight, srcWidth/2, srcHeight/2);
        canvas.drawBitmap(logoBitmap, srcWidth/2 - logoWidth/2, srcHeight/2 - logoHeight/2, null);
    
        return bitmap;
    }
    

    参数说明:

    • srcBitmap:之前生成的简单二维码图片
    • logoBitmap:logo图片
    • logoPercent :logo图片所占的百分比,建议使用0.2F,百分比过大可能导致二维码扫描失败。

        这里就是一个简单的图片合成,首先获取原图片和logo图片各自的宽高,通过我们传入的百分比计算画布缩放的宽高比,然后使用Canvas绘制,合成最终带logo的二维码。

        为了方便,我们在最开始生成简单二维码的方法中传入logo图片和百分比,在生成Bitmap时添加logo,如下:

    /**
     *
     * @param content                字符串内容
     * @param width                  二维码宽度
     * @param height                 二维码高度
     * @param character_set          编码方式(一般使用UTF-8)
     * @param error_correction_level 容错率 L:7% M:15% Q:25% H:35%
     * @param margin                 空白边距(二维码与边框的空白区域)
     * @param color_black            黑色色块
     * @param color_white            白色色块
     * @param logoBitmap             logo图片
     * @param logoPercent            logo所占百分比
     * @return
     */
     public static Bitmap createQRCodeBitmap(String content, int width, int height, String character_set, 
                                             String error_correction_level,String margin, int color_black, 
                                             int color_white,Bitmap logoBitmap, float logoPercent) {
        // 字符串内容判空
        if (TextUtils.isEmpty(content)) {
            return null;
        }
        // 宽和高>=0
        if (width < 0 || height < 0) {
            return null;
        }
        try {
            /** 1.设置二维码相关配置,生成BitMatrix(位矩阵)对象 */
            Hashtable<EncodeHintType, String> hints = new Hashtable<>();
            // 字符转码格式设置
            if (!TextUtils.isEmpty(character_set)) {
                hints.put(EncodeHintType.CHARACTER_SET, character_set);
            }
            // 容错率设置
            if (!TextUtils.isEmpty(error_correction_level)) {
                hints.put(EncodeHintType.ERROR_CORRECTION, error_correction_level);
            }
            // 空白边距设置
            if (!TextUtils.isEmpty(margin)) {
                hints.put(EncodeHintType.MARGIN, margin);
            }
            /** 2.将配置参数传入到QRCodeWriter的encode方法生成BitMatrix(位矩阵)对象 */
            BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
    
            /** 3.创建像素数组,并根据BitMatrix(位矩阵)对象为数组元素赋颜色值 */
            int[] pixels = new int[width * height];
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    //bitMatrix.get(x,y)方法返回true是黑色色块,false是白色色块
                    if (bitMatrix.get(x, y)) {
                        pixels[y * width + x] = color_black;//黑色色块像素设置
                    } else {
                        pixels[y * width + x] = color_white;// 白色色块像素设置
                    }
                }
            }
    
            /** 4.创建Bitmap对象,根据像素数组设置Bitmap每个像素点的颜色值,并返回Bitmap对象 */
            Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
    
            /** 5.为二维码添加logo图标 */
            if(logoBitmap != null){
                return addLogo(bitmap, logoBitmap, logoPercent);
            }
            return bitmap;
        } catch (WriterException e) {
            e.printStackTrace();
            return null;
        }
    }
    

    调用:

    createQRCodeBitmap(content, 800, 800,"UTF-8","H", "1", Color.BLACK, Color.WHITE,logoBitmap,0.2F);
    

    logo图片需要转为Bitmap格式传入,将项目资源下的图片转为Bitmap可使用如下方法

    Resources res = getResources();
    Bitmap logoBitmap= BitmapFactory.decodeResource(res,R.mipmap.logo);
    

    效果图:
     

    4.使用图片代替黑色色块

    什么意思呢,看图
     

        就是将二维码的黑色色块使用图片代替,视觉效果就是你的图片在黑色色块部分被显示。
    实现方法:
        首先通过Bitmap的createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter)方法从当前位图,按一定的比例创建一个新的位图,该方法需要传入四个参数,第一个参数就是当前图片,第二个和第三个参数是新位图长宽(这里传入二维码的长宽,保证图的大小一样),最后一个参数直接传false。

    Bitmap bitmap_black = Bitmap.createScaledBitmap(bitmap_black, width, height, false);
    

        创建好新位图后,在根据BitMatrix(位矩阵)对象为数组元素赋颜色值时,将黑色色块换为新位图的像素。

    if (bitMatrix.get(x, y)) {// 黑色色块像素设置
        if(bitmap_black != null) {//图片不为null,则将黑色色块换为新位图的像素。
           pixels[y * width + x] = bitmap_black.getPixel(x, y);
        } else {
           pixels[y * width + x] = color_black;
        }
    } else {
         pixels[y * width + x] = color_white;// 白色色块像素设置
    }
    

    注:尽量选用深色图片,否则影响扫描精度。

    代码如下:

    /**
     *  生成自定义二维码
     *
     * @param content                字符串内容
     * @param width                  二维码宽度
     * @param height                 二维码高度
     * @param character_set          编码方式(一般使用UTF-8)
     * @param error_correction_level 容错率 L:7% M:15% Q:25% H:35%
     * @param margin                 空白边距(二维码与边框的空白区域)
     * @param color_black            黑色色块
     * @param color_white            白色色块
     * @param logoBitmap             logo图片(传null时不添加logo)
     * @param logoPercent            logo所占百分比
     * @param bitmap_black           用来代替黑色色块的图片(传null时不代替)
     * @return
     */
    public static Bitmap createQRCodeBitmap(String content, int width, int height, String character_set, String error_correction_level,
                                                String margin, int color_black, int color_white, Bitmap logoBitmap, float logoPercent, Bitmap bitmap_black) {
        // 字符串内容判空
        if (TextUtils.isEmpty(content)) {
            return null;
        }
        // 宽和高>=0
        if (width < 0 || height < 0) {
            return null;
        }
        try {
            /** 1.设置二维码相关配置,生成BitMatrix(位矩阵)对象 */
            Hashtable<EncodeHintType, String> hints = new Hashtable<>();
            // 字符转码格式设置
            if (!TextUtils.isEmpty(character_set)) {
                hints.put(EncodeHintType.CHARACTER_SET, character_set);
            }
            // 容错率设置
            if (!TextUtils.isEmpty(error_correction_level)) {
                    hints.put(EncodeHintType.ERROR_CORRECTION, error_correction_level);
            }
            // 空白边距设置
            if (!TextUtils.isEmpty(margin)) {
                hints.put(EncodeHintType.MARGIN, margin);
            }
            /** 2.将配置参数传入到QRCodeWriter的encode方法生成BitMatrix(位矩阵)对象 */
            BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
    
            /** 3.创建像素数组,并根据BitMatrix(位矩阵)对象为数组元素赋颜色值 */
            if (bitmap_black != null) {
                //从当前位图按一定的比例创建一个新的位图
                bitmap_black = Bitmap.createScaledBitmap(bitmap_black, width, height, false);
            }
            int[] pixels = new int[width * height];
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    //bitMatrix.get(x,y)方法返回true是黑色色块,false是白色色块
                    if (bitMatrix.get(x, y)) {// 黑色色块像素设置
                        if (bitmap_black != null) {//图片不为null,则将黑色色块换为新位图的像素。
                            pixels[y * width + x] = bitmap_black.getPixel(x, y);
                        } else {
                            pixels[y * width + x] = color_black;
                        }
                    } else {
                        pixels[y * width + x] = color_white;// 白色色块像素设置
                    }
                }
            }
    
            /** 4.创建Bitmap对象,根据像素数组设置Bitmap每个像素点的颜色值,并返回Bitmap对象 */
            Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
    
            /** 5.为二维码添加logo图标 */
            if (logoBitmap != null) {
                return addLogo(bitmap, logoBitmap, logoPercent);
            }
            return bitmap;
        } catch (WriterException e) {
            e.printStackTrace();
            return null;
        }
    }
    

        源码已上传至GitHub:https://github.com/yangxch/GenerateQRCode
        生成二维码方法我放到了一个工具类,调用时如果不需要logo或不需要用图片代替黑色色块,传nulll 即可。
        当然图片不能直接写死,可以拍照或相册选择,生成二维码还可保存至手机或分享给他人,实现方式后续更新,敬请关注。
                 

    更新如下:
    Android生成二维码–拍照或从相册选取图片
    Android生成二维码–保存和分享二维码图片



    微信扫一扫下方二维码即可关注我的公众号

    展开全文
  • 二维码的应用非常广泛,用途也很多,网上也有很多实用的精简包可以引入,本篇主要记录了不用引入新的module,自己集成生成二维码功能的过程。 一、引入依赖 首先在libs文件目录下放进jar包zxing.jar,然后引入...
  • Android二维码功能实现,在程序内嵌入ZXing项目

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

    千次阅读 多人点赞 2020-03-03 16:37:31
    做过Android二维码扫描的童鞋可能会遇到过,很多二维码识别不了,或者识别速度慢。一般造成这个识别不出来的原因,大概以下几点: Android手机配置不一样,手机像素高低也有不同,有的手机还不支持自动对焦 环境...
  • 二维码扫描功能,查了许多资料,感觉都不是太理想,然后就自己从新写一个喽
  • Android 二维码的集成及优化

    千次阅读 2017-04-27 17:35:50
    Android 两种扫码效率高的扫码工具对比:QrCodeScanner和QrCodeScan前景介绍Android二维码扫描大多采用Zxing和Zbar扫码库,但是两个扫码库都各有缺点
  • 在简版zxing基础上美化了下,实现了竖屏、拉伸处理、扫描框大小和扫描线移动、开灯,生成我的二维码,里面相册导入图片解码,有点问题,相册与分享,暂时没效果。相册解码我是又导入了一个解码类,但是hity类型有点...
  • Android 二维码的扫码功能实现(一)

    万次阅读 2017-10-29 12:47:32
    前言最近一段时间研究了一下,android二维码扫码功能的实现。有些体会,在此总结一下。 实现二维码的扫码功能,在网上搜了一下,能够发现大部分都提到说使用Google开源的Zxing,现在基本上都是使用的Zxing来做的。...
  • Android 二维码扫描框架

    千次阅读 2018-10-26 17:40:34
    Android 好用的框架与UI效果demo收集 1.zxing 条码图像处理库 github https://github.com/zxing/zxing 二次封装的简单易用 https://github.com/yipianfengye/android-zxingLibrary ...
  • Android二维码登录原理及生成与解析

    千次阅读 2017-09-22 14:06:58
    1.二维码原理   很重要的一部分知识:二维码一共有 40 个尺寸。官方叫版本 Version。Version 1 是 21 x 21 的矩阵,Version 2 是 25 x 25 的矩阵,Version 3 是 29 的尺寸,每增加一个 version,就会增加 4 的...
  • Android 二维码扫描,识别率非常高

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

    千次阅读 2014-01-20 15:43:57
    二维码的定义:二维码 (2-dimensional bar code),是用某种特定的几何图形按一定规律在平面(二维方向上) 分布的黑白相间的图形记录数据符号信息的。 在许多种类的二维条码中,常用的码制有:Data Matrix, Maxi ...
  • 现在二维码已经非常普及了,那么二维码的扫描与处理也成为了Android开发中的一个必要技能。网上有很多关于Android...所以我自己写了这篇文章,把Android二维码的开发来从头捋一下。 本例界面如下图所示,代码链接在

空空如也

1 2 3 4 5 ... 20
收藏数 34,578
精华内容 13,831
关键字:

android二维码