站在巨人的肩膀上——二维码扫描功能的实现和优化过程

来源:互联网 发布:jira windows 安装 编辑:程序博客网 时间:2024/04/29 14:06

前段时间,因为开发需求要求加上二维码扫描的功能,所以我也是通过网上一系列的资料学习实现了这个功能,很感谢网上一些大牛提供的学习博客,特别是http://blog.csdn.net/xiaanming/article/details/10163203这篇博客对我的帮助很大,我也是在这个上面做了一些改进和优化,最后终于在我们的项目中加入了二维码扫描的功能。今天就来讲一下我的学习和实现过程:

1.首先,我在这个http://download.csdn.net/detail/xiaanming/5990219网站上下了上面那篇博客中提到的项目源码,它已经在原来的zxing源码上做了很多优化,而我在这个源码的基础上又做了一些处理,基本上解决了一些遗留的问题。     

在这个项目中,其中ViewfinderView这个类是一个自定义控件,也就是扫描页用到的主要的控件,所以就先从这个自定义控件类入手进行修改。

(1)在我运行了这个项目后发现扫描框中总是会出现黄色的小点,其实是扫描点的颜色设置不对,也就是下面这个代码中的“resultPointColor”。

resultPointColor = resources.getColor(R.color.possible_result_points);
把上面这行代码中的possible_result_points设置成透明色即可。

(2)扫描线比较丑,因此我把它修改了成类微信的扫描线,也就是把绘制扫描线的那行代码替换一下,这个问题那篇博客的博主已经提到了,所以就跳过。

(3)在不同机型上扫描框的显示有比较大的不同,为了解决这适配问题,我在这个类加了一个dip转px的方法,代码如下:

<span style="font-size:12px;">public static int dip2px(Context context, float dipValue) {        final float scale = context.getResources().getDisplayMetrics().density;        return (int) (dipValue * scale + 0.5f);    }</span>

然后在一些直接使用数字加上这个方法,例如四个边角的宽度:

    <span style="font-size:12px;">/**     * 四个绿色边角对应的宽度     */    private static final int CORNER_WIDTH = 10;</span>
改成下面这样:

    /**     * 四个绿色边角对应的宽度     */    private final int CORNER_WIDTH = dip2px(getContext(), 10);
(4)因为原来的代码其实只是画了四个绿色的直角,边框是由于除扫描框其他的区域灰色比较深造成的,所以我自己画了四个边框,代码如下,很容易理解:

// 画四个边框,左上右下paint.setColor(Color.GRAY);canvas.drawLine(frame.left, frame.top, frame.left, frame.bottom, paint);canvas.drawLine(frame.left, frame.top, frame.right, frame.top, paint);canvas.drawLine(frame.right - 1, frame.top, frame.right - 1, frame.bottom, paint);canvas.drawLine(frame.left, frame.bottom - 1, frame.right, frame.bottom - 1, paint);

(5)因为我是想做成类似微信的扫描框,然后发现这个扫描框有点太大了,在代码中有这样一段注释和代码:

//中间的扫描框,你要修改扫描框的大小,去CameraManager里面修改Rect frame = CameraManager.get().getFramingRect();

根据这句提示,我修改了CameraManager中的getFramingRect()方法,代码如下:

public Rect getFramingRect() {    Point screenResolution = configManager.getScreenResolution();    if (framingRect == null) {      if (camera == null) {        return null;      }      int width = screenResolution.x * 5 / 9;      int height = screenResolution.y * 3 / 10;      int leftOffset = (screenResolution.x - width) / 2;      int topOffset = (screenResolution.y - height) * 2 / 5;      framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);      Log.d(TAG, "Calculated framing rect: " + framingRect);    }    return framingRect;  }

2.通过上面5个步骤,基本上这个扫描框就跟微信的差不多了,下面看一下扫描类中的处理:

因为我们项目是要求从扫描页跳转到一个链接显示的网页,所以我新建了一个SweepResultActivity类,并且加上了从相册选择二维码进行扫描的功能。先说一下扫描类的改动:

(1)处理扫描结果的方法

    /**     * 处理扫描结果     * @param result     * @param barcode     */    public void handleDecode(Result result, Bitmap barcode) {        if (inactivityTimer != null) {            inactivityTimer.onActivity();        }        playBeepSoundAndVibrate();        String resultString = result.getText();//扫描获取到的文字        Bitmap bitmap = barcode; // 扫描获取到的二维码图片        if (resultString.equals("")) {            Toast.makeText(SweepCodeActivity.this, getString(R.string.sweep_fail), Toast.LENGTH_LONG).show();        } else {            // 获取到文字和图片不为空            Toast.makeText(SweepCodeActivity.this, "扫描成功,链接为" + resultString, Toast.LENGTH_LONG).show();            Intent it = new Intent();            it.setClass(SweepCodeActivity.this, SweepResultActivity.class);            it.putExtra(SweepResultActivity.JUMP_TO_SWEEPRESULTACTIVITY_URL, resultString);            startActivity(it);        }    }
(2)添加从相册选的方法和图片返回的获取和处理

Button btnImagePick = (Button) findViewById(R.id.btn_title_more);        btnImagePick.setVisibility(View.VISIBLE);        btnImagePick.setText(getString(R.string.select_code_from_album));        btnImagePick.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                imagePick();            }        });
</pre><pre name="code" class="java">private Handler mHandler = new Handler(){    @Override    public void handleMessage(Message msg) {        super.handleMessage(msg);        mProgress.dismiss();        switch (msg.what) {            case PARSE_BARCODE_SUCCESS:                handleDecode((Result) msg.obj, scanBitmap);                break;            case PARSE_BARCODE_FAIL:                Toast.makeText(SweepCodeActivity.this, (String) msg.obj, Toast.LENGTH_LONG).show();                break;        }    }};/** * 从相册选取 */private void imagePick() {    try {        Intent intent = new Intent(Intent.ACTION_PICK, null);        // 指定调用相机拍照后照片的储存路径        intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");        startActivityForResult(intent, REQUEST_CODE);    } catch (Exception e) {        Toast.makeText(SweepCodeActivity.this, getString(R.string.edit_user_info_image_no_browser), Toast.LENGTH_LONG).show();    }}/** * 处理扫描结果 * @param result * @param barcode */public void handleDecode(Result result, Bitmap barcode) {    if (inactivityTimer != null) {        inactivityTimer.onActivity();    }    playBeepSoundAndVibrate();    String resultString = result.getText();//扫描获取到的文字    Bitmap bitmap = barcode; // 扫描获取到的二维码图片    if (resultString.equals("")) {        Toast.makeText(SweepCodeActivity.this, getString(R.string.sweep_fail), Toast.LENGTH_LONG).show();    } else {        // 获取到文字和图片不为空        Toast.makeText(SweepCodeActivity.this, "扫描成功,链接为" + resultString, Toast.LENGTH_LONG).show();        Intent it = new Intent();        it.setClass(SweepCodeActivity.this, SweepResultActivity.class);        it.putExtra(SweepResultActivity.JUMP_TO_SWEEPRESULTACTIVITY_URL, resultString);        startActivity(it);    }}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {    super.onActivityResult(requestCode, resultCode, data);    if(resultCode == RESULT_OK){        switch(requestCode){            case REQUEST_CODE:                //获取选中图片的路径                Cursor cursor = getContentResolver().query(data.getData(), null, null, null, null);                if (cursor.moveToFirst()) {                    photo_path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));                }                cursor.close();                mProgress = new ProgressDialog(SweepCodeActivity.this);                mProgress.setMessage(getString(R.string.sweeping));                mProgress.setCancelable(false);                mProgress.show();                new Thread(new Runnable() {                    @Override                    public void run() {                        Result result = scanningImage(photo_path);                        if (result != null) {                            Message m = mHandler.obtainMessage();                            m.what = PARSE_BARCODE_SUCCESS;                            m.obj = result;                            mHandler.sendMessage(m);                        } else {                            Message m = mHandler.obtainMessage();                            m.what = PARSE_BARCODE_FAIL;                            m.obj = getString(R.string.sweep_fail);                            mHandler.sendMessage(m);                        }                    }                }).start();                break;        }    }}/** * 扫描二维码图片的方法 * * @param path * @return */public Result scanningImage(String path) {    if (TextUtils.isEmpty(path)) {        return null;    }    Hashtable<DecodeHintType, String> hints = new Hashtable<DecodeHintType, String>();    hints.put(DecodeHintType.CHARACTER_SET, "UTF-8"); //设置二维码内容的编码    BitmapFactory.Options options = new BitmapFactory.Options();    options.inJustDecodeBounds = true; // 先获取原大小    scanBitmap = BitmapFactory.decodeFile(path, options);    options.inJustDecodeBounds = false; // 获取新的大小    int sampleSize = (int) (options.outHeight / (float) 200);    if (sampleSize <= 0)        sampleSize = 1;    options.inSampleSize = sampleSize;    scanBitmap = BitmapFactory.decodeFile(path, options);    RGBLuminanceSource source = new RGBLuminanceSource(scanBitmap);    BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));    QRCodeReader reader = new QRCodeReader();    try {        return reader.decode(bitmap1, hints);    } catch (NotFoundException e) {        e.printStackTrace();    } catch (ChecksumException e) {        e.printStackTrace();    } catch (FormatException e) {        e.printStackTrace();    }    return null;}

这样SweepCodeActivity也算是完善了,不过运行的时候会发现会出现扫描的页面会变形,一个正方形的二维码会变高一些。

3.其实解决这个办法很简单,只要在CameraConfigurationManager类的initFromCameraParameters()中判断一下显示的高度和宽度大小就可以了,代码如下:

void initFromCameraParameters(Camera camera) {        Camera.Parameters parameters = camera.getParameters();        previewFormat = parameters.getPreviewFormat();        previewFormatString = parameters.get("preview-format");        WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);        Display display = manager.getDefaultDisplay();        int width = display.getWidth();        int height = display.getHeight();        screenResolution = new Point(width, height);        if (width < height) {            cameraResolution = getCameraResolution(parameters, new Point(height, width));        } else {            cameraResolution = getCameraResolution(parameters, screenResolution);        }    }


0 0
原创粉丝点击