小工具知识网

当前位置: 首页 > 行业知识 > Android之 Zxing二维码详解

行业知识

Android之 Zxing二维码详解

2026-11-09 11:46:43 来源:互联网转载

一 简介

1.1 ZXing

目前Android扫描二维码,条形码主要用google官方的工具Zxing,支持扫码,相册解码,生成带logo的二维码等功能 

Zxing github 示例地址:https://github.com/zxing/zxing

1.2 ZBar

由于zxing是基于java编写的,扫码速度和解析上可能没那么快,但大部分场合足够用。也有基于c/c++的库zbar,需要编译通过才能用,下面是官网,有兴趣的可以编译试试:

ZBar官网:http://zbar.sourceforge.net/

ZBar GitHub地址:https://github.com/ZBar/ZBar

1.3  华为ScanKit

目前体验最好的华为统一扫码SDK,基本可以做到秒扫和快速识别,支持多码识别和二维码生成。但该服务必须在华为开发者联盟平台注册应用,配置包名和服务json

https://gitee.com/hms-core/hms-scan-demo华为官方demo示例 gitee地址:https://gitee.com/hms-core/hms-scan-demo

二 Zxing使用

2.1 依赖远程zxing库

dependencies {  //zxing的core库  implementation "com.google.zxing:core:3.5.1"   //zxing  implementation "com.google.zxing:zxing-parent:3.5.1"  //zxing  implementation 'com.journeyapps:zxing-android-embedded:4.1.0'}

或直接使用下面库,目前识别比较快的Zxing库

implementation 'com.journeyapps:zxing-android-embedded:4.3.0'

2.2 添加权限

 

2.3 调用SDK的扫码页面并返回结果

/** * 跳转到扫码界面扫码 */private void goScan(){    Intent intent = new Intent(MainActivity.this, CaptureActivity.class);    startActivityForResult(intent, REQUEST_CODE_SCAN);}
@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {    super.onActivityResult(requestCode, resultCode, data);    // 扫描二维码/条码回传    if (requestCode == REQUEST_CODE_SCAN && resultCode == RESULT_OK) {        if (data != null) {            //返回的文本内容            String content = data.getStringExtra(DECODED_CONTENT_KEY);            //返回的BitMap图像            Bitmap bitmap = data.getParcelableExtra(DECODED_BITMAP_KEY);        }    }}

三 自定义扫码页面

3.1 效果图

3.2 activity_scan.xml

                                                        

3.3 ScanActivity.java

public class ScanActivity extends Activity {    public static final int REQUEST_CODE_PHOTO = 0X1113;    private static final String TAG = "ScanActivity";    private Camera camera = null;    private Camera.Parameters parameters = null;    private boolean isPreview = false;    private FrameCallback frameCallback = new FrameCallback();    private int width = 1920;    private int height = 1080;    private double defaultZoom = 1.0;    private SurfaceHolder surfaceHolder;    private SurfaceCallBack surfaceCallBack;    private CommonHandler handler;    private boolean isShow;    private FrameLayout rim;    private SurfaceView surfaceView;    private ImageView backImg;    private TextView tvDecode;    private ImageView imgBtn;    private int shortSize;    private int longSize;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Window window = getWindow();        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);        requestWindowFeature(Window.FEATURE_NO_TITLE);        setContentView(R.layout.activity_common);        rim = (FrameLayout) findViewById(R.id.rim);        surfaceView = (SurfaceView) findViewById(R.id.surfaceView);        backImg = (ImageView) findViewById(R.id.back_img);        tvDecode = (TextView) findViewById(R.id.tv_decode);        imgBtn = (ImageView) findViewById(R.id.img_btn);        checkCameraPermission();    }    //权限请求    public final int REQUEST_CAMERA_PERMISSION = 1;    private String cameraPermission = Manifest.permission.CAMERA;    private void checkCameraPermission() {        //检查是否有相机权限        if (ContextCompat.checkSelfPermission(this, cameraPermission) != PackageManager.PERMISSION_GRANTED) {            //没权限,请求权限            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},                    REQUEST_CAMERA_PERMISSION);        } else {            //有权限            createSurfaceView();        }    }    //权限请求回调    @Override    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {        switch (requestCode) {            case REQUEST_CAMERA_PERMISSION:                if (grantResults != null && grantResults.length > 0                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {                    //用户同意权限                    createSurfaceView();                } else {                    // 权限被用户拒绝了,可以提示用户,关闭界面等等。                    Toast.makeText(this, "拒绝权限,请去设置里面手动开启权限", Toast.LENGTH_SHORT).show();                }                break;        }    }    /**     * 创建预览     */    private void createSurfaceView() {        surfaceCallBack = new SurfaceCallBack();        adjustSurface(surfaceView);        surfaceHolder = surfaceView.getHolder();        isShow = false;        setBackOperation();        setPictureScanOperation();        setDecodeSelectOperation();    }    private void adjustSurface(SurfaceView cameraPreview) {        FrameLayout.LayoutParams paramSurface = (FrameLayout.LayoutParams) cameraPreview.getLayoutParams();        if (getSystemService(Context.WINDOW_SERVICE) != null) {            WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);            Display defaultDisplay = windowManager.getDefaultDisplay();            Point outPoint = new Point();            defaultDisplay.getRealSize(outPoint);            int sceenWidth = outPoint.x;            int sceenHeight = outPoint.y;            shortSize = Math.min(sceenWidth, sceenHeight);            longSize = shortSize;            //横屏            paramSurface.width = shortSize;            paramSurface.height = shortSize;//            float rate;//            if (sceenWidth / (float) 1080 > sceenHeight / (float) 1920) {//                rate = sceenWidth / (float) 1080;//                int targetHeight = (int) (1920 * rate);//                paramSurface.width = FrameLayout.LayoutParams.MATCH_PARENT;//                paramSurface.height = targetHeight;//                int topMargin = (int) (-(targetHeight - sceenHeight) / 2);//                if (topMargin = maxZoom) {            return allZoomRatios.size() - 1;        }        for (int i = 1; i = (zoomValue * 100) && allZoomRatios.get(i - 1)  200) {                    preTime = currentTime;                    beepManager.playBeepSoundAndVibrate();                    restart(DEFAULT_ZOOM);                    stopPreview();                    Intent intent = new Intent(ScanActivity.this, ResultActivity.class);                    intent.putExtra("result", result);                    intent.putExtra("time", message.arg1);                    startActivity(intent);                }            }        }        public void quit() {            try {                stopPreview();                decodeHandle.getLooper().quit();                decodeThread.join(500);            } catch (InterruptedException e) {                Log.w(TAG, e);            }        }        public void restart(double zoomValue) {            callbackFrame(decodeHandle, zoomValue);        }        /**         * Call the MultiProcessor API in synchronous mode.         */        private String decodeSyn(int width, int height, byte[] data) {            String result="";            switch (decodeType){                case 1://Zxing                    result = ZxingUtils.decode(width, height, data);                    break;                case 2://商米                    result = SunmiUtils.decode(width, height, data);                    break;                case 3://华为                    result = HuaweiHmsUtils.decode(width, height, data, ScanActivity.this);                    break;            }            return result;        }    }}

3.4  解析工具类

public class ZxingUtils {    public static String decode(int width, int height, byte[] data) {        Map hints = new EnumMap(DecodeHintType.class);        hints.put(DecodeHintType.CHARACTER_SET, "utf-8");        Result rawResult = null;        PlanarYUVLuminanceSource source = buildLuminanceSource(data, width, height);        MultiFormatReader multiFormatReader = null;        if (source != null) {            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));            try {                multiFormatReader = new MultiFormatReader();                multiFormatReader.setHints(hints);                rawResult = multiFormatReader.decodeWithState(bitmap);            } catch (ReaderException re) {                // continue            } finally {                multiFormatReader.reset();            }        }        if(rawResult==null){            return null;        }else {            return rawResult.getText();        }    }    /**     * A factory method to build the appropriate LuminanceSource object based on the format     * of the preview buffers, as described by Camera.Parameters.     *     * @param data   A preview frame.     * @param width  The width of the image.     * @param height The height of the image.     * @return A PlanarYUVLuminanceSource instance.     */    public static PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {        Rect rect = new Rect(0,0,width,height);        if (rect == null) {            return null;        }        // Go ahead and assume it's YUV rather than die.        return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,                rect.width(), rect.height(), false);    }}

四 相册选区图片解析二维码

4.1 选择相册图片

private void setPictureScanOperation() {        imgBtn = findViewById(R.id.img_btn);        imgBtn.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {               Intent pickIntent = new Intent(Intent.ACTION_PICK,                        MediaStore.Images.Media.EXTERNAL_CONTENT_URI);                pickIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");               ZxingActivity.this.startActivityForResult(pickIntent, REQUEST_CODE_PHOTO);            }        });    }

4.2 本地图片文件转换成可解码二维码的 Bitmap

/**     * 将本地图片文件转换成可解码二维码的 Bitmap。为了避免图片太大,这里对图片进行了压缩。     *     * @param picturePath 本地图片文件路径     */    public 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) {                bitmap = showContent(bitmap, content, textSize);            }            return bitmap;        } catch (Exception e) {            e.printStackTrace();        }        return null;    }    /**     * 显示条形的内容     *     * @param barcodeBitmap 已生成的条形码的位图     * @param content       条形码包含的内容     * @param textSize      字体大小,单位px     * @return 返回生成的新条形码位图     */    private static Bitmap showContent(Bitmap barcodeBitmap, String content, int textSize) {        if (TextUtils.isEmpty(content) || null == barcodeBitmap) {            return null;        }        Paint paint = new Paint();        paint.setColor(Color.BLACK);        paint.setAntiAlias(true);        paint.setStyle(Paint.Style.FILL);        paint.setTextSize(textSize);        paint.setTextAlign(Paint.Align.CENTER);        int textWidth = (int) paint.measureText(content);        Paint.FontMetrics fm = paint.getFontMetrics();        int textHeight = (int) (fm.bottom - fm.top);        float scaleRateX = barcodeBitmap.getWidth() * 1.0f / textWidth;        if (scaleRateX  

5.4 异步调用QRCodeEncoder生成二维码

 private void updateQrcode(){        new GenerateQrcodeTask().execute(content);    }    public class GenerateQrcodeTask extends AsyncTask {        @Override        protected Bitmap doInBackground(String... strings) {            Bitmap logoBitmap=null;            if(logo!=0){                logoBitmap = BitmapFactory.decodeResource(getResources(), logo);            }            int borderColor22=TextUtils.isEmpty(borderColor)?0:Color.parseColor(borderColor);            return QRCodeEncoder.syncEncodeQRCode(strings[0], (int) StatuesBarUtils.dp2px(mContext, 300f), Color.parseColor(qianColor),Color.parseColor(beinColor),logoBitmap, border , borderColor22);        }        @Override        protected void onPostExecute(Bitmap bitmap) {            super.onPostExecute(bitmap);            qrBitmap=bitmap;            if (bitmap != null) {                mDataBinding.vImage.setImageBitmap(qrBitmap);            } else {                Toast.makeText(mContext, "生成維碼失敗", Toast.LENGTH_SHORT).show();            }        }    }

5.5 保存二维码,FileUtil.java工具类

public class FileUtil {    public static String saveToImage(Bitmap bitmap) {        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {            return MediaStore.Images.Media.insertImage(MyApp.getInstance().getContentResolver(), bitmap, "", "");        } else {            try {                File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "qrcode");                if (!dir.exists()) {                    dir.mkdirs();                }                File picFile = new File(dir, System.currentTimeMillis() + ".png");                FileOutputStream fos = new FileOutputStream(picFile);                bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);                fos.flush();                fos.close();                return picFile.getAbsolutePath();            } catch (Exception e) {                e.printStackTrace();            }        }        return null;    }

 异步调用FileUtil,保存二维码

public class SaveBitmapTask extends AsyncTask {        @Override        protected String doInBackground(Bitmap... bitmaps) {            return FileUtil.saveToImage(bitmaps[0]);        }        @Override        protected void onPostExecute(String result) {            super.onPostExecute(result);            if (result != null) {                Intent intent = new Intent(mContext, QrBarcodeEditResultActivity.class);                intent.putExtra("filePath", result);                startActivity(intent);            } else {                Toast.makeText(mContext, "保存二維碼失敗", Toast.LENGTH_SHORT).show();            }        }    }

5.6 核心源码解析,我们知道bitmap是一个保存所有像素内容的容器,那二维码原理即是把内容解析到每一个像素里面。如下

  /** * * @param content         要生成的二维码图片内容 * @param size            图片宽高,单位为px * @param foregroundColor 二维码图片的前景色 * @param backgroundColor 二维码图片的背景色 * @param logo            二维码图片的logo * @param borderColor     边框颜色 */public static Bitmap syncEncodeQRCode(String content, int size, int foregroundColor, int backgroundColor, Bitmap logo ,int border , int borderColor) {int borderWidth= (int) dp2px(MyApp.getInstance(),4);size-=borderWidth*2;try {//内容解析到字节矩阵BitMatrix matrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, size, size, HINTS);//设置宽高像素数组int[] pixels = new int[size * size];//遍历记录图片每个像素信息for (int y = 0; y  

上一篇:宝塔面板怎么安装gd?

下一篇:PHP实战源码解析,轻松入门必备指南