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

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

前段时间,因为开发需求要求加上二维码扫描的功能,所以我也是通过网上一系列的资料学习实现了这个功能,很感谢网上一些大牛提供的学习博客,特别是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);
    }
}

@Override
protected 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
0
0
1
分享
相关文章
潮玩宇宙大逃杀无聊猿卷轴模式系统开发详细规则丨步骤需求丨方案项目丨技术架构丨源码功能
确定游戏类型和规则:明确无聊猿卷轴模式游戏类型和游戏规则,包括敌人类型、地图设计、任务类型、战斗机制等。
外贸网站怎么做站内优化?
答案是:外贸官网的站内优化包括:代码优化,内链优化,结构优化三大类。 内容是王道 一份优质的内容是Google优化的基础。 投入时间和精力来创造独特、有价值的内容,以吸引和保留读者。 记住,内容必须是原创的,并且应该针对您的目标受众。 不断更新新的、有价值的内容也能够帮助提高网站的Google排名。
107 0
外贸网站怎么做站内优化?
前端知识学习案例13-开发企业网站12-团队介绍部分2
前端知识学习案例13-开发企业网站12-团队介绍部分2
70 0
前端知识学习案例13-开发企业网站12-团队介绍部分2
前端知识学习案例12-开发企业网站12-团队介绍部分1
前端知识学习案例12-开发企业网站12-团队介绍部分1
58 0
前端知识学习案例12-开发企业网站12-团队介绍部分1
前端知识学习案例8-开发企业网站8-实现关于我们的部分1
前端知识学习案例8-开发企业网站8-实现关于我们的部分1
84 0
前端知识学习案例8-开发企业网站8-实现关于我们的部分1
前端知识学习案例9-开发企业网站9-实现关于我们的部分2
前端知识学习案例9-开发企业网站9-实现关于我们的部分2
61 0
前端知识学习案例9-开发企业网站9-实现关于我们的部分2
前端知识学习案例14-开发企业网站14-实现数据区域
前端知识学习案例14-开发企业网站14-实现数据区域
66 0
前端知识学习案例14-开发企业网站14-实现数据区域
前端知识学习案例15-开发企业网站15-实现数据区域
前端知识学习案例15-开发企业网站15-实现数据区域
59 0
前端知识学习案例15-开发企业网站15-实现数据区域
站在巨人WordPress的肩膀上学架构
WordPress 可能是很多学习搭建云计算网站的第一课,写了篇 Hello World 就匆匆结束了对 WordPress 的体验。在全球前 1,000 万的网站中,三分之一都使用了 WordPress,像国外的白宫官网、纽约邮报、微软新闻中心、国内的爱范儿等等。因此开源社区的 WordPress 有着大量的开发者贡献代码和思路,帮助我们构建一个强大而成熟的网站。 我们要实现一个架构其实最重要的是三个目标,快、稳和省。 前端访问要快,后端运行要稳,但是实现前两者不能无节制的上升成本,我们要节省成本让利益最大化。。
1498 0
站在巨人WordPress的肩膀上学架构
前端知识学习案例1-开发企业网站1-网站介绍
前端知识学习案例1-开发企业网站1-网站介绍
80 0
前端知识学习案例1-开发企业网站1-网站介绍
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等